This happens after every child node1 has been initialized, added to the scene, tree and has already called _ready().
_init()usually is called after the constructor finishes
It seems that we should assume that _init() will be called after the constructor, but unfortunately Godot doesn’t seem to guarantee the order? Read this from the docs
“_init() is Called when the object’s script is instantiated, oftentimes after the object is initialized in memory (through Object.new() in GDScript, or new GodotObject in C#). It can be also defined to take in parameters. This method is similar to a constructor in most programming languages.”
Instantiating a scene
As for as I can tell from the docs, this is the order of events when instantiating a scene and adding it2:
Initial value assignment: the property is assigned its initialization value, or its default value if one is not specified. If a setter exists, it is not used.3
_init() assignment: the property’s value is replaced by any assignments made in _init(), triggering the setter.
Exported value assignment: an exported property’s value is again replaced by any value set in the Inspector, triggering the setter.
GDScript Example
# test is initialized to "one", without triggering the setter.@export var test: String = "one": set(value): test = value + "!"func _init(): # Triggers the setter, changing test's value from "one" to "two!". test = "two"# If someone sets test to "three" from the Inspector, it would trigger# the setter, changing test's value from "two!" to "three!".
Gotcha: Setting a Resource type in the inspector and leaving all its properties at default values
class_name MyResource extends Resource@export var my_int: int = -1func _init() -> void: if my_int < 0: my_int = 2class_name MyNode extends Node@export var my_resource := MyResource.new()
If my_resource is set in the inspector, MyResource.my_int will be -1. If my_resource isn’t set via inspector, it will be 2.
Work-around: Implement a private initialization() function that you [[call_deferred() in Godot|call_deferred()]] at the end of the _init() function. The deferred function will execute after the @export properties are initialized so are overwritten.
This appears to only be true when using GDScript. When the setter is defined another language such as Swift or C# then the setter appears to follow whatever the language determines. ↩