¿How to avoid this race condition?

Godot Version

4.2.2.stable - Windows 11 - GDScript


First, some context.

I come from Unity, where in every component you have an Awake() and a Start() function. You can not normally know in which order each Awake or each Start function is executed. What you DO know is that first all Awake functions are run, then all Start functions.

This is what allows me to basically have each gameobject (would be node in this case) initialize itself and its references on Awake(), and then call methods on other gameobjects on Start().

With that said, after some investigation I have concluded that this workflow is basically not the same in Godot. Also, I learned that a root node _ready is only called when al its children have already been initialized. So, in other words, the _ready function is called from children to parent.

So, this is my problem. I want to see if the solution Im trying to do makes sense, or if it would be best to refactor and solve it in another way. Keep in mind this is a miniscule test example im doing and not some big project.

Node Structure of relevance:

  • Game (Root Scene)
    • Player (Scene)
      • Gun (Scene)
    • GunUICanvas
      • BulletsLabel

BulletsLabel is a Label that shows “Ammo: currentAmmo/totalAmmo”.

I want to do the following:

  1. When gun.gd initializes, it calls bullets_changed(bulletsAmmount) signal.
  2. When bullets_label.gd initializes, it listens to Gun.bullets_changed signal on _gun_bullets_changed(amoun) method.
  3. When _gun_bullets_changed is called, its text value gets updated.

When starting the game:
Expected behaviour:

  1. First, gun calls bullets_changed as a “setup call” (so that the label can get its start value)
  2. Second, label updates its value. This script does not store totalAmmo nor currentAmmo, this is stored on gun.gd script.

Actual behaviour:
As nodes are initialized from children to parent, Gun is initialized before BulletsLabel. So when BulletsLabelstarts listening to the signal, the first call has already been made before by Gun, so it doesnt get that initial call, only calls after that.

In summary, my questions are:

  • How can I control de order of execution of _start if possible?
  • If the previous answer is I cant, then how can I make sure (with a manual approach or something) that I have to instances of “startup” of each script (similar to unity). First, an instance where each script starts up itself and gets any references it needs, then a second step where each reference or signal can be called, making sure every other node has also been initialized and is correctly listening.

I hope I could make myself understood. Here is a screenshot of my hierarchy, and snippets of the relevant code that was mentioned:


extends Area2D

signal bullets_changed(bulletsAmount)

var maxBullets = 10
var bullets = maxBullets

func _ready():

... Some more code ...

func resetBullets():
	bullets = maxBullets


extends Label

@onready var gun : Node2D = get_tree().get_first_node_in_group("gun")
var totalBullets = 0

func _ready():

func _gun_bullets_changed(newBullets):
	if totalBullets == 0:
		totalBullets = newBullets
	text = "Ammo: " + str(newBullets) + "/10"

In this case couldn’t you just put GunUICanvas above Player in the scene tree? That would make BulletsLabel be ready before Gun and it would recieve the signal. I might be wrong…

But one general solution would be to use call_deferred for the things you want to happen when every node is ready:

func _ready():

func stuff_to_do_when_all_nodes_are_ready():
    # this will be called during idle time after the whole scene tree is created

But I would be interested too in a proper / better way if there’s any.

I didn’t know the order of nodes on the scene was the order in which godot initializes them. Thats very useful.


1 Like

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