@cybereality Yeah, it behaves precisely as I'd expect it to.

Code.ready() calls succeeding sibling's method Scene.change_color(). This method then tries to use its class property that hasn't been initialized yet. It isn't initialized because initialization happens in Scene.ready() which is yet to be called after Code._ready() finishes.

If you reverse the order of Code and Scene nodes - it'll work just fine, because now Scene.ready() did initialize the property before Code.ready() calls a method that uses it.

Calling Scene.change_color() from the parent's ready() will, of course, work, regardless of sibling order because both sibling's ready() will already finish their jobs at that point.

...

I think the main point of confusion for many people is that onready thingy. When property is declared onready it's the same as adding the assignment expression at the start of _ready() callback:

onready var foo = "godot"

is same as:

var foo
func _ready():
	foo = "godot"

In other words, doing: var foo = "godot" will initialize the class property very early in the scene setup process, immediately upon node object creation. It happens before the nodes is even added to the tree. Referring to other nodes here is guaranteed to fail.

Conversely, onready var foo = "godot" initializes at the very end of the setup process - right before _ready() is called. Referring to other nodes here will always be successful.

Okay, I get it know. The children are initialized sequentially going down from the top (and once they are all done, the parent becomes ready). So you can call siblings, as long as they are above you in the tree.

You can call them wherever they are, but if you're calling from ready() you should be aware that ready() of siblings below has not been executed yet. If initialization you count on happens in that _ready() - it'll not be there.

In your example the call from Code.ready() to Scene.change_background() is by itself perfectly ok, because node is there. The problematic part of it is that Scene.change_background() is called before Scene.ready(), but it expects to find variable bg properly initialized, which is a job of Scene._ready() that hasn't been done yet.

Yes, I understand, but calling functions on incomplete objects is sure to end in pain. Though I guess you could save a reference (like with get_node) to use at some later point after the whole game is initialized.

I shouldn't have to point this out, but I missed it so why not. If you manually instantiate a new node B inside node A's init() function and child B to A, don't expect B to be ready on A's ready() call.

In other words.. if you have made a custom level editor and you're loading up scenes dynamically, don't expect all these functions to be of help as if Godot were loading up a static scene.

@Erich_L said: I shouldn't have to point this out, but I missed it so why not. If you manually instantiate a new node B inside node A's init() function and child B to A, don't expect B to be ready on A's ready() call.

Why? I'm pretty certain that B's ready() will be executed before A's ready(). No reason for this not to happen. Try to test it out on a simple use case.

EDIT: Here's an example Script attached to node A in editor:

class_name A
extends Node

func _init():
	print("A._init()")
	add_child(B.new())

func _ready():
	print("A._ready()")

Dynamically created node B class:

class_name B
extends Node

func _init():
	print("B._init()")

func _ready():
	print("B._ready()")

Output: A.init() B.init() B.ready() A.ready()

@xyz Wow! I tried it in my project, I thought it would be different because there's so many meshes and materials to load and whatnot, but I got the same results! Very thankful you took the time to prove it, this definitely furthers my understanding of the engine.

I sure hope this stuff gets written into the doc's as it is very enlightening.

a year later