In my game, I have a custom resource which holds a field of type Node. I want to set this field through the editor. Since the engine doesn’t allow Resources to export fields of type Node, I want to pass the value as a parameter of the _init() function, like so:
I create the Resource at the top of my main scene script like this:
var my_resource: MyResource = preload("res://resources/my_resource.tres").new(%MyNode)
The error I get is Invalid call. Nonexistent function 'new' in base 'Resource'. A pretty clear error message. But since Resource inherits from Object, I don’t understand how it can’t have a new() function. What am I doing wrong here?
I also tried to call _init() directly, in which case something funny happens:
The error log tells me 'Resource(my_resource.gd)::_init': Method expected 1 arguments, but called with 0.
The Stack Trace on the other hand tells me Too many arguments for "_init()" call. Expected at most 0 but received 1.
I suppose you’re not supposed to call _init() directly anyway, but I still find it funny that the error messages can’t agree on what the actual error is.
I’m not sure if it’s the expected way, but what I did when I needed that kind of thing was to make a separate function (maybe call it create() or something) which takes the arguments you want to supply:
func create_and_init(thing: String, arg: Node) -> Node:
var res = preload(thing)
res.some_var = arg
return res
When you preload() a Resource it will automatically call _init() with no arguments. new() it’s not working because preload() preloads the Resource itself not the class. new() only works on scripts or classes.
You can add default values to the _init() parameters to avoid it failing to preload()
class_name MyResource extends Resource
var field:Node
func _init(init_field:Node = null) -> void:
field = init_field
If you want to create a new MyResource you can do it like:
So I just came back to this code and unfortunately this doesn’t solve my problem after all (after 4 hours of trying and failing I wasn’t able to spot this last time).
The problem is: I’m not instantiating the “base” resource, but a version of it with some preset fields I saved to file.
So my Resource code looks more like this:
class_name MyResource extends Resource
@export var preset_field: int
var dynamic_field:Node
func _init(init_dynamic_field:Node = null) -> void:
dynamic_field = init_dynamic_field
Now, I have my_resource_1.tres with preset_field set to 1, and my_resource_2.tres with preset_field set to 2. What I want to do is get an instance of both of those resources while passing a Node to them. Is that still possible?
No. load() or preload() already instance the resource itself.
You can do what @hexgrid said. Maybe make it static so you can do it in one line MyResource.load_and_init("res://my_resource.tres", other_node) or create a method that sets the node and returns the resource like:
class_name MyResource extends Resource
@export var value:int
var other_node:Node
func with_node(other:Node) -> MyResource:
other_node = node
return self
And then you can do: var my_resource:MyResource = preload("res://my_resource.tres").with_node(my_node)
if you still want to do it in one line for whatever reason.