Order

Inserting a node into the scene tree

As far as I can tell from the docs this is the order of events when creating a new node and adding it:

  1. the constructor (MyObject.new())
  2. _init()
  3. _enter_tree()
    • (This happens after the node is added to the Scene Tree using add_Child())
  4. ready in Godot
    • 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:

  1. 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
  2. _init() assignment: the property’s value is replaced by any assignments made in _init(), triggering the setter.
  3. 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

Quote

From: Godot notifications — Godot Engine (stable) documentation in English @ User-contributed notes:

There’s a gotcha with object initialization. When setting Resource types in the inspector, and leaving all its @exported Variant data members (for example) at default values, the default values will overwrite values set in its _init() function as if they were set via the inspector. Example below:
(https://docs.godotengine.org/en/stable/tutorials/best_practices/godot_notifications.html#init-vs-initialization-vs-export)

class_name MyResource extends Resource
@export var my_int: int = -1
 
func _init() -> void:
  if my_int < 0: my_int = 2
 
 
class_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.

Docs

See Also

Footnotes

  1. recursively

  2. See docs here

  3. 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.