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?
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It only takes a minute to sign up.
Sign up to join this communityThey 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.
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 Node
s... 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):
@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:
@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._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
.