Indeed the docs say that you should have access to children and their properties in _ready. However this is not always the case, especially if you are creating new nodes dynamically by instancing scenes and adding them to your existing nodes, instead of having everything statically defined.
As a result, a lot of times you will try to access a property like "position" of a child node from within a parent node's script (e.g. in the parent node's _process function), and you will get errors like 'bad index "position" on null thing'. This can happen in the _ready function. How do we wait on stuff to load asynchronously that isn't available when _ready is called?
Here is one approach you can use.
You can connect the "tree_entered" signal on the child node to a method on the ancestor node.
If you are interested in children of the child node, you might want to use the "childenteredtree" signal instead. Or you could connect directly to the "tree_entered" signal on the lower child.
In your ancestor node, which presumably needs to make a function call like getnode("MyChildNode") or getnode("MyChildNode/MyLowerChildNode"), and which is presumably causing issues and throwing errors like "bad index on null thing", you can define a "ready" boolean variable initialized to "false". All your "treeentered" or "childentered_tree" signal handlers (defined in the ancestor Node's script) is set "ready" equal to "true".
Then in your function that actually needs to call getnode and use the child's data and methods, before you use the child you first check if "treeready" is true. If "tree_ready" is still false, you just skip the logic that messes with the node. You try to design your application in a such a way that it doesn't care when things get loaded.
# MyAncestorNode.gd
extends Node2D
var child_node_ready = false
var lower_child_node_ready = false
func _process(delta):
if ! child_node_ready:
return
print($MyChildNode.position)
if ! lower_child_node_ready:
return
print($MyChildNode/MyLowerChildNode.position)
func _on_MyChildNode_child_entered_tree():
lower_child_node_ready = true
func _on_MyChildNode_tree_entered():
child_node_ready = true