+2 votes

I am running into a problem where my parent will try to set_text() of some labels that are his children, but apparently they are not ready yet which returns "null instance" error and crashes the game.

How do we tell the parent to wait for the children ready signal?

I know that this is the problem, because if I write a script on the child, and call the same function on it's parent using _ready(), it works.

in Engine by (375 points)

A not ready instance and null instances are different things. You can set variables even before adding an instance to the scene so being ready or not isn't a problem.
Use the remote scene view when the error happens to see if the child really exists.

2 Answers

+3 votes
Best answer

I use this:

func _ready():
    yield(get_tree().root, "ready")

If you have autoload scripts and you reload the scene, use this:

yield(get_tree().root.get_child(get_tree().root.get_child_count()-1), "ready")
by (313 points)
selected by

I'm using 3.5.1 and found this works:

yield(self, "ready")

(a shorter version of the code in the actual answer).

I'm using this in a custom initialise() func on a custom node that is dynamically added to the scene tree. My onready variables aren't created until _ready() is called, and I need them in order to initialise them.

To avoid creating a dependent sequence where I add the custom node to the tree before initialising it, in my initialiser I wait until _ready() has fired before initialising the onready variables. This keeps my code clean and neat when I dynamically add the custom node, and works no matter which order I call my initialise() and add_child().

After all, it makes more sense (to me) to initialise the node before adding it to the tree from the context of the creator:

var customNodeInstance = customNodeScene.instance()
customNodeInstance.initialise(x, y)
0 votes

I don't know exactly how your code is structured, but I would advise you to call the function that creates/prepares the labels first and then write your text in those boxes. That way you make sure that the boxes will be ready before you attempt to reference them.

Another way is to use the yield keyword, which waits for a signal before it executes what you want it to execute. More on that here, here and in the docs here.

Your problem could well easily be solved by using the onready keyword before declaring your labels like this:

onready var lbl_foo = $Label_Foo

That way Godot loads the label first before it does anything to it.

Last but not least, you could create a Timer node and use its timeout() signal to execute your functions after some time has elapsed.
I hope these tips help you. But then again I do not know how your code and nodes are laid out.

by (1,887 points)

Well, I used direct access to these labels, on the parent ready. Like:

func _ready():

These labels don't change, they are only static texts, but I set them like this because they need to retrieve a data stored in a library. In this case a autoloadd which has all items data.

I am going to experiment more with the yield, as I think it's a better solution. And as well try to reference the labels before-hand with the onready var.

Thank you for your response and the links!

Strangely enough, this type of thing worked:

    for node in $Labels.get_children():
            match node.name:
                "TEST_LABEL":nodes.set_text("Cheese with Peanut butter.")

I tried every method mentioned in the answer and some other of my own, but my project didn't liked any. I don't know the reason, it remained a mystery to me.

Also I didn't fully prompt the error, going to put it here,

 0:00:00.840   get_node: Node not found: TEST_LABEL.
  <C++ Error>   Condition ' !node ' is true. returned: __null
  <C++ Source>  scene/main/node.cpp:1381 @ get_node()
  <Stack Trace> Status_UI.gd:18 @ STATUS_UPDATE() 
                Status_UI.gd:14 @ _ready()

And here is the line of code on ready.


IoI found the real culprit, It was my fault, a old node was hidden in the scene lower on the list that was using the same script, and I didn't noticed, but upon simply doing: print(self.name)printed 2 times. So, doing the match:prevented it from crashing, because it works almost like a if.

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read Frequently asked questions and How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to [email protected] with your username.