|
|
|
|
Attention |
Topic was automatically imported from the old Question2Answer platform. |
|
Asked By |
CharlesMerriam |
This came up on this question.
Yield works differently inside a function other than _ready()
.
I have a new project with one scene which has two nodes: A node 2D with button name TheButton
. Here is the attached script:
extends Node2D
func do_yield():
yield($TheButton, "pressed")
print("4, on the second press")
func _ready():
print("1")
yield($TheButton, "pressed")
print("2, after pressing")
do_yield()
print("3, immediately after 2")
When running this scene:
- The
_ready()
function starts and prints “1” as expected.
- The
yield
pauses the thread until the button is pressed. It prints “2”.
do_yield()
is called. The yield
does not pause, but waits on a new thread and immediately returns. “3” is printed.
- When the button is pressed again, the paused thread from
do_yield()
fulfills waiting on the signal and “4” is printed.
Can anyone explain why the yield
statement works differently in different functions?
|
|
|
|
Reply From: |
Inces |
It works as intended. Yield()
is only supposed to pause one thread, isn’t it ?
You can’t expect corroutine do_yield()
to pause ready
, because yield
line is only met inside this corroutine. So print(3)
is executed 1 frame after do_yield
line, because there is no reason for it to wait until corroutine is completed. At the same time, do_yield()
is paused itself and waiting for button press.
corroutines don’t have to return, calling functions continue witohout it
Thank you for your answer. I want to clarify what you said, so this comment got long:
_ready()
and do_yield()
are each coroutines because each contains a yield
statement. When called, the current thread is suspended and execution of the function starts on a newly created thread.
- On executing any form of a
yield
statement, the current thread is suspended and execution continues with the calling thread.
- When the thread running
do_yield()
is suspended and _ready()
continues, the statement print("3, immediately after 2")
is executed, though the display may not update until the beginning of the next frame.
This makes the sequence:
- The system thread (S1) calls
ready()
. This suspends S1, creates a new thread (call it T1), and starts T1 executing ready(). It prints 1
.
- T1 executes
yield($TheButton, "pressed")
. T1 suspends and S1 wakes and continues.
$TheButton
is eventually pressed, and the pressed
signal wakes T1. It prints 2
.
- T1 executes
do_yield()
. Because do_yield()
is a coroutine, T1 suspends and a new thread (call it T2) starts executing.
- T2 executes the
yield($TheButton, "pressed")
in do_yield()
. T2 suspends, T1 wakes and prints 3
. T1 completes its function body and is destroyed.
$TheButton
is eventually pressed, and the pressed
signal wakes T2. It prints 4
, finishes its body, and is destroyed.
To make debugging more fun:
- Can Godot show which signals are firing? Outside of connection to each one?
- Can Godot use syntax highlighting to differentiate ‘normal’ calls from coroutine calls that spawn a thread?
- Can Godot list what threads are active and where in the source they are executing?
And finally, am I correct that emit
and connect
do not touch the threads directly, only yield
?
CharlesMerriam | 2022-06-02 04:53
Yeah, I actually used wrong wording I guess - I meant subroutine
This naming difference may be exactly what You asked for. As I believe do yield() would become true coroutine if this one line went :
var x = do_yield()
This way ready() would actually have to wait until yield line of do yield() until x would become yieldedfunctionstate. You can also go :
yield(self,"do_yield","completed")
to force ready() to wait until do_yield() gets to its return line
To be honest I don’t understand swapping yielded function states inbetween corroutines, these are some advanced options for me
I tried experimenting with it earlier but it beat me
Maybe You can get to something ?
PS I don’t understand what You meant about signals and connect relative to threads. I fail to see simarity between those methods.
|
|
|
|
Reply From: |
SnapCracklins |
For the record, I would not use a yield here. I would just make a variable the button holds (or global if some other scene needs it) and just run a match on button press to check the variable amount and run the event you want on each step.
Example:
func on_button_pressed():
Var _gain = myButtonVariable+1
myButtonVariable = _gain
match myButtonVariable:
1:
### this event here
2:
### this event here
3:
You get the idea. Yield can cause problems sometimes especially if you are waiting for a solid state or work a bit too fast. Usually flags or value comparisons will do what you want better with less risk of breaking something.
Thank you. Asking on the discord channel, I find that people are generally considering yield
to unreliable and that it will be leaving in Godot 4. Good to know.
CharlesMerriam | 2022-06-03 02:42