How to properly await for @onready vars?

Godot Version

4.2.1

Question

Hi there,

i’ve created a UI scene with an @onready var which is a reference to a grandchild node. The UI root node is receiving a signal before the @onready var has been initialized. Side note: is this behavior intended or a bug?

Anyway, i’ve worked around the problem this way (update_shield is the signal callback):

func update_shield(max_value, value):
	while not shield_bar:
		print('waiting for shield bar')
		await get_tree().create_timer(.1).timeout

	print('found shield bar')
	shield_bar.max_value = max_value
	shield_bar.value = value

Like expected, the console gives me this at the start:

waiting for shield bar
found shield bar

Obviously, this doesn’t seem to be a clean approch, so my question is: is there something like this pseudocode

await self.is_ready

i could use to solve the problem in a cleaner way?

Thanks in advance for your time.

Regards,
Normen

PS: thanks for maintaining and supporting this great game engine!

Is update_shield() function on the root or on the grandchild?

How is it called?

You could try using Node.is_node_ready() and await for its Node.ready signal. Something like:

func update_shield(max_value, value):
	if not shield_bar.is_node_ready():
		await shield_bar.ready

	print('found shield bar')
	shield_bar.max_value = max_value
	shield_bar.value = value

update_shield() is on the scene root node. It’s called by a signal.

shield_bar.is_node_ready() doesn’t work because shield_bar is still nil. I’ve tried

await shield_bar.ready

but it gives me this error:

Invalid get index 'ready' (on base: 'Nil').

probably for the same reason as above.

then this whoever send the signal should really wait a frame before the all node is ready

await get_tree().process_frame

then put the emit_signal to the UI Root node code

Thanks for your replies, after some fiddling the problem is solved.

@mrcdk, you were almost right. The working code is this:

func update_shield(max_value, value):
	if not shield_bar:
		await self.ready

One question remains: should a node receive a signal before it has been initialized (== ready)?

Thanks for your input, @zdrmlpzdrmlp. But this would mean i had to prefix every emit() like this if there’s a chance that the receiving node is not ready, doesn’t it? Shouldn’t there be a cleaner solution to this?

i dont know how you made it so that the signal has to be emit in quick manner before the whole nodes is parented in the tree. if it’s to tell update shield from main core game to the UI node, usually it needs to read from save file, get the variable, then emit the signal to update the UIs, it’s definitely not when the main core game _ready, emit_signal to update UI
curious where do you emit the update UI signal

My main scene looks like this:

Main
-- ...
-- Player
-- ...
-- CanvasLayer
---- UI

The signal that’s being received by UI is emitted in Player._ready() so it seems pretty clear that there’s no guarantee that the UI node is ready. My fault was to expect that signals will only be dispatched to nodes that are ready, i guess.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.