_int() Use Case Conflicts

Godot Version

4.4

Question

` Is there a reason why _init() is called both during the creation and loading of objects?

I understand why “initialization” could be used to define both cases, however I feel like the lack of real constructors that are only used during the creation of an object isn’t particularly great.

This is especially awkward in the case of Resources, since when you want to make one in the inspector, you can’t utilize a constructor or define a resource variable without the tool annotation, and _init is still called when resources are loaded again when the project is reset.

Anybody have any insight on this design choice and how to work around its limitations? `

Init is only ever called once. But if your using it as a tool you need to be aware that things are being initialized in the editor.

My issue is that it is not called during inspector-based resource creation without the tool annotation, meaning it would be convenient if there were a proper way to initialize member variables within a Resource with Resource.new(), or an actual constructor system to utilize to that effect.

Some manner of work-around would be appreciated, if anyone knows of one.

What if you put default arguments into the constructor so that you could both create the Resource in the editor and through code?

1 Like

To expand on @paintsimmon

You can setup parameters to init.

class_name Foo
var apple_type
func _init(a): 
  apple_type = a
  

Calling it like

var new_foo := Foo.new("Red")

Just be aware that using new with a script in a scene that relies on child nodes to exist can run into trouble. I like to make a static builder function for a scene-script class.

class_name MyFooScene

static func create(a) -> MyFooScene:
  var scene : = preload("res://path/to/my_foo_scene.tscn").instantiate()
  scene.apple_type = a
  return scene

var apple_type

Calling it like this

var new_scene := MyFooScene.create("Red")

I think there is some movement to allow instantiate() to define parameters like _init but im not sure when it will come if ever.

I think you just need to add defualt values to the parameter list.

func _init(a:String = ""):
  ...

Then when you instantiate the object it will have a default value.

But inspector resources you should just use @export variables.

Sorry if it wasn’t clear, but by resources, I meant capital-R Resouce-class-derived resources. When they are created in the inspector, by clicking the exposed property and creating a new instance, they aren’t given any manner of initialization except in the case of basic value types.

I’d want internal resource reference type initialization, such as:

class_name MainResource
extends Resource

@export var resource := InternalResource.new()

to occur, and for a means of further set-up to occur as well, in the way a constructor normally would. For example, I would want a newly created AtlasTexture resoource to be fed a Texture2D loaded from the filesystem. I had mistakenly figured that _init() would be called during asset creation in this manner, but it does not, unless the tool annotation is used, then it is called in instances that are not fit for one-time creation procedures.

I understand that the inspector will show empty values for internal resources. The defualt value constructor new() happens when the scene is loaded. When the node is initialized it will get the defualt export value resource. If you override the value in the inspector you will get the override value. Just because its not visible doesnt mean its not there.

It is a little confusing, but if the default resource was present in the inspector and it was customized, it is no longer the defualt value that the code defined. I.e. you are not using the default anymore. I assume they are not there in the inspector, as an optimization, for this reason. As well as the optimization of caching the overridden resource properties within the scene file (.tscn) its been saved to.

Its up to you to setup the default values of the exported values within the custom class in the scripts. You can add new( <parameters>... ) (by defining _init(<parameters> ...)) if you want a code-based constructor.

If you already have a defualt and need a customized defualt for a scene i would save a custom version of the resource in the filesystem and use that as the scene’s defualt. Or make a new class that does the customization for you and define an export with the new class.

Another thing is if the scene with the internal resource, has a custom internal resource, that custom resource will be preserved into the other scenes that import it. And can be customized within that importing scene.

I ended up just utilizing the file-system for individual resources rather than creating and storing them within my inventory resource’s arrays.

While the array has the benefit of viewing each resource in sequence, making it handy for comparison, the filesystem allows you to make a default object, then duplicate it to perform an instant, painless, fully unique reproduction of said pre-prepared resource (and its internal resources).

Following that, I coded the initializer of my inventory to load all assets from their files into their corresponding inventories when the game is loaded.

In terms of reviewing them in sequence, I suppose I can just code something for that after the fact.

1 Like