6
\$\begingroup\$

In Godot 4.x, is declaring @onready variable exactly the same as declaring it within the _ready() function?

@onready var my_variable : = 10

func _ready():
   var my_variable : = 10

If not, when/what/how does it make a difference?

\$\endgroup\$

2 Answers 2

6
\$\begingroup\$

They are similar in the sense that both deferred assignment until after the node has entered the tree. However in the example you gave, they differ with respect to their scope.

@onready var my_variable : = 10

The first example you gave (shown above) has class/script level scope. In other words the variable is considered defined everywhere in that script and can be used to share data between functions.

func _ready():
   var my_variable : = 10

The second example you gave (repeated above) the variable has function level scope. It is only considered defined & usable on the lines that come after it and only within that function. For example if you try the following:

func _ready():
   var player_health : = 10

func take_damage():
   player_health -= 2

The result will give an error when the line player_health -= 2 is reached because the scope of player_health is restricted to the _ready() function.


The example given isn't a particular useful application of @onready - there's no clear reason why you might need to defer assigning an int as shown. In contrast, consider the example in the docs of assigning

var my_label

func _ready():
    my_label = get_node("MyLabel")

Note the key difference: in your example you used the keyword var inside the _ready() function whereas this here we do not.

In the above case, you cannot use a regular assignment because get_node() doesn't make sense if the Node has not yet entered the tree. So the code example above waits until the _ready() function to assign the value. Because this pattern is common in Godot, they added some syntactic sugar to save us some typing by shortening the above code block to the following:

@onready var my_label = get_node("MyLabel")

Whether you prefer the single line or the multiline version, they only make sense when you need to make an assignment that cannot be evaluated until after the Node is in the tree.

\$\endgroup\$
4
\$\begingroup\$

Pikalek answer is correct, but there is more we can say about the difference between @onready and _ready.

Also it can use a bit of nuance: both @onready and _ready will run when the Node has entered the scene tree... And after @onready and _ready has run for all children Nodes... And only the first time... Unless you call request_ready, which will make it run again the next time the Node enters the scene tree. You can check is_node_ready to know if they already ran.

And that is the main difference between using _tree_entered (which runs when the Node enters the scene tree, no matter what) and _ready. The other is that _tree_entered runs before _ready.

Now for additional differences (aside from scope):

  • What you have in @onready will run before _ready.
  • @onready only applies to initializing variables. You can do other things in _ready.
  • @onready is limited to expressions. While _ready can have statements.

Something related to scope is lifetime. In this case, local variables only take memory while the method/function (e.g. _ready) is running... But fields (class/script level variables) will be stored for the lifetime of the object (the Node in this case). In other words, your @onready variables will be around until the Node is freed (with free or queue_free).

So when it comes to splitting execution into multiple lines:

  • Using @onready, since @onready statements run in order, you might use the result of prior ones... Yet, the Node would be storing those variables for its lifetime.
  • Using _ready, you don't only can use temporary local variables, but you can also use statements.

Thus, _ready is preferible for complex initialization.


By the way, as you know, the point of @onready is to wait until the scene tree is available before initialization. Consequently any class/script variable without @onready is initialized early, but for them accessing the scene tree (e.g. with get_node) will fail.

Which begs the question, is there method/function that can be used to do early initialization? And yes, there is: _init.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .