Question about _ready() order in "Control the game’s UI with code" tutorial

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By nimh

I’m confused about how the _ready() function in GUI.gd from the Control the game’s UI with code tutorial works. It looks like this

func _ready():
    var player_max_health = $"../Characters/Player".max_health
    bar.max_value = player_max_health

As far as I can see in the documentation, _ready() is guaranteed to happen after _enter_tree() is called for all of a node’s parents and children, and after _ready() is called for all of a node’s children. However, in this case, the GUI node’s _ready() function is trying to access the children of a sibling, Characters. Now, max_health is set at the top level of Player.gd, so I assume that it’s available as soon as the Player node enters the tree. Does this mean that _ready() is also guaranteed to not be called until all sibling nodes and their children have called _enter_tree()? Does this have something to do with the fact that GUI is higher up in the Scene Tree Editor than Player? Is this a race that just happens to work out, even though there’s no guarantee that Player has called _enter_tree() before GUI calls _ready()?

Um… clearly underscores have a special meaning (italics) in this commenting markup language. Also preview lies. It shows _enter_tree() having both underscores, and not having “enter” in italics.

Nevermind. I figured it out. _enter_tree().

nimh | 2019-01-17 05:08

Unfortunately, I can’t really answer your questions since I hav eno clue in what order the _ready() functions are called for siblings.
However, this is the reasony why I’d think coupling siblings like that isn’t good design. So in that case i’d prefer to move that logic to the parent node.

Tornadowarnung | 2019-01-17 12:07

I’d like to think that the tutorial code wasn’t racey. In fact I’d like to think that the tutorials were an example of best practices. If nobody has a definitive answer offhand, perhaps I’ll try reading the engine code to see if I can figure out what guarantees do exist. Otherwise, it does make sense to just assume that there is no synchronization between siblings, and you need to rely on the parent node for that. But, at any rate, either the explanation of _ready() needs to be fixed in the documentation to state all of the guarantees, or the tutorials need to be changed to not teach people to write racey code.

nimh | 2019-01-17 18:47

:bust_in_silhouette: Reply From: nimh

Looking at Node::_set_tree(SceneTree *p_tree) in scene/main/node.cpp, it appears that you go through _enter_tree() for the whole tree from the starting node first, and then you call _ready() on the whole tree back to the starting node. By adding print() commands to _enter_tree() and _ready() in my code, I can see that this is exactly what happens. So, the tutorial code is safe, and whenever a node is added to the scene tree, _enter_tree() will be called on all of its children before _ready() is called on any of them. Also _enter_tree() and _ready() will be called in a depth first method, sorted by index.