Godot: onready variable is not initialized inside _ready() of base class

Created on 14 Nov 2019  路  4Comments  路  Source: godotengine/godot

Godot version:
3.2 beta1

Issue description:
This one is complicated a bit, but boils down to this:

extends Node2D
class_name Base

func _ready() -> void:
    test_node()

func test_node():
    pass
extends Base

onready var control = $Control

func test_node():
    print(control.name)

The control will be null in this case, which is a bit unexpected. Looks like the base script is initialized and calls _ready before the inheriting script is properly initialized.

Minimal reproduction project:
ReproductionProject.zip

bug confirmed gdscript

Most helpful comment

I guess this is expected. The onready keywork is equivalent to setting the variable at the top of the _ready function. So when your script runs it initializes things in this order:

  • run the parent Base's _ready() function
  • initialize the child's onready variable
  • run the child _ready() function

Thus the variable cannot be initialized inside the parent's call to the _ready function

All 4 comments

I guess this is expected. The onready keywork is equivalent to setting the variable at the top of the _ready function. So when your script runs it initializes things in this order:

  • run the parent Base's _ready() function
  • initialize the child's onready variable
  • run the child _ready() function

Thus the variable cannot be initialized inside the parent's call to the _ready function

I also would say that this is expected behaviour, similar to C++ constructor call ordering with inherited objects.

You can solve this by calling test_node() from the child's _ready() function. Another way I did not test might be using Object's call_deferred() see here.

@KoBeWi I've managed to workaround the issue like that:

extends Base

#onready var control = $Control
var control

func _notification(what):
    match what:
        NOTIFICATION_INSTANCED:
            control = $Control
#       NOTIFICATION_READY: # same as onready
#           control = $Control

func test_node():
    print(control.name)

So basically the whole initialization happens on early NOTIFICATION_INSTANCED instead of syntax sugar coated NOTIFICATION_READY.

I've been thinking on migrating to this kind of technique over using onready var in fact which seems to resolve onready dependent node referencing with get_node() later on (while the scene is not even added to the tree). Talking about dependency: #30460.


I had to modify the scene tree because of wrong hierarchy of nodes, updated repro project:
ReproductionProject.zip

Still valid in 3.2.3
There's a chance it's fixed in master, but can't test due to #42440 and #42506

Was this page helpful?
0 / 5 - 0 ratings