I understand. The process of creating a minimum project has helped me solve the issue. Thanks! I’ll document my findings here.
TL;DR:
Do use await get_tree().process_frame
in _draw()
for correct, consistent results for control.size
:
func _draw():
await get_tree().process_frame
print(control.size) # consistent 0, 90
Do not use call_deferred()
Do not use await get_tree().process_frame
in _ready()
even if it prints the correct values for you, since the results can be inconsistent or wrong depending on your UI complexity
Reference code
For reference, this is the (simplified) code in the control:
func _ready():
check_control_size_timings()
func _draw():
check_control_size_timings()
func check_control_size_timings():
print_container_size()
call_deferred("print_container_size")
await get_tree().process_frame
print_container_size()
await_to_be_sized()
func print_container_size():
# Expected output: (0, 90)
print("Control size: %s" % [control_to_get_size_of.size])
func await_to_be_sized():
while control_to_get_size_of.size == Vector2.ZERO:
await get_tree().process_frame
print_container_size()
Hierarchy
And this was my hierarchy:
-> Main_Scene
-> UI <- (Has script mentioned above)
-> A bunch of control nodes here...
-> A bunch of child control nodes here...
-> A bunch of nested control nodes here...
-> A bunch of nested control nodes here...
-> ControlToGetSizeOf <- control.size
-> A bunch of control nodes here...
-> A bunch of child control nodes here...
Tests
When running the scene with the control.size
script directly:
Expected value is (0, 90)
- Directly calling print(control.size) in:
1.1. _ready(): (0, 0)
1.2. _draw(): (0,0) - Call_deferred print in:
2.1. _ready(): (0, 0)
2.2. _draw(): (0, 0) - await get_tree().process_frame in:
3.1. _ready(): (0, 90) Consistently
3.2. _draw(): (0, 90) Consistently - await_to_be_sized():
4.1. _ready(): (0, 90) Consistently
4.2. _draw(): (0, 90) Consistently
However, I’m not running that scene directly. It’s a child scene, and it should not be visible until a button is pressed. So I have two options:
Initiate the node by drag and dropping in the editor, and turning it invisible. It is turned visible by the press of a button. Here are the results:
- All _ready() methods return (0,0)
- _draw():
2.1. Direct print (control.size): (0, 0)
2.2. Call_deferred print: (0, 0)
2.3: await get_tree().process_frame and await_to_be_resized() both consistently return (0, 90), the expected value
Instantiating the scene manually is a no-go for my use-case.
Each scene represents one “card reward” in my game, and I want it the ‘rewards’ to vary between 1 to (many) cards at a time.
For completeness sake, I will spawn them in, in 2 ways:
- When the “Make visible” button is pressed, instead of making the invisible scene → visible, the
ui
node will instantiate a new scene instead when the button is pressed; - the
ui
node will instantiate the required scenes before the button is pressed. The button press will turn the required scenes visible.
Here are the results.
Instantiate on button press:
- Directly calling print(control.size) in:
1.1. _ready(): (0, 0)
1.2. _draw(): (0,0) - Call_deferred print in:
2.1. _ready(): (0, 0)
2.2. _draw(): (0, 0) - await get_tree().process_frame in:
3.1. _ready(): (0, 90) INCONSISTENTLY, can also print (0, 0)
3.2. _draw(): (0, 90) Consistently - await_to_be_sized():
4.1. _ready(): (0, 90) Consistently
4.2. _draw(): (0, 90) Consistently
Instantiate the scene(s) at the _ready() of the parent scene:
- Directly calling print(control.size) in:
1.1. _ready(): (0, 0)
1.2. _draw(): (0,0) - Call_deferred print in:
2.1. _ready(): (0, 0)
2.2. _draw(): (0, 0) - await get_tree().process_frame in:
3.1. _ready(): (0, 0)
3.2. _draw(): (0, 90) Consistently - await_to_be_sized():
4.1. _ready(): (0, 90) Consistently
4.2. _draw(): (0, 90) Consistently
In previous (forum) threads, call_deferred
was recommended as a solution for this issue. However, in my tests, it returns the wrong value, so I cannot recommend using call_deferred
for getting the control.size
Seeing as getting the size in _ready() can be wrong or inconsistent
func _ready():
await get_tree().process_frame
print(control.size) # 0,0 or inconsistent 0, 90
the proper solution is to get the size in _draw()
func _draw():
await get_tree().process_frame
print(control.size) # consistent 0, 90
TL;DR:
Do use await get_tree().process_frame
in _draw()
for correct, consistent results for control.size
:
func _draw():
await get_tree().process_frame
print(control.size) # consistent 0, 90
Do not use call_deferred()
Do not use await get_tree().process_frame
in _ready()
even if it prints the correct values for you, since the results can be inconsistent or wrong depending on your UI complexity