![]() |
Attention | Topic was automatically imported from the old Question2Answer platform. |
![]() |
Asked By | CowThing |
![]() |
Old Version | Published before Godot 3 was released. |
I have a function that does something over time, yielding every frame.
func example():
timer.start()
while timer.get_time_left() >= 0.1:
# Do things
yield(get_tree(), "fixed_frame")
The problem is, if I call this function and then queue_free()
the object before this function finishes, it will give me an error, Resumed after yield, but class instance is gone
. Now this error doesn’t seem to have any ill effects, the function is still working just as intended, but it only works in the debug version of the game. With an exported game this error causes a crash.
So, how do I properly stop a function like this?
More discussion on this issue can be found here: Destroying an object running a coroutine (yield) should interrupt this coroutine properly · Issue #24311 · godotengine/godot · GitHub
jpate | 2020-10-27 15:03
I had the exact same issue. Took me some hours to get a workaround which doesn’t require you to change all your yield’s. Create a new singleton and paste this code:
func yield_wait(var timeout : float, var parent = get_tree().get_root()):
var timeoutCap = max(0, timeout / 1000.0)
if timeoutCap <= 0:
return
var timer = Timer.new()
timer.set_one_shot(true)
# ensure that the timer object is indeed within the tree
yield(yield_call_deferred(parent, "add_child", timer), "completed")
timer.start(timeoutCap)
var timerRoutine = _yield_wait(timer)
if timerRoutine.is_valid():
yield(timerRoutine, "completed")
yield(yield_call_deferred(parent, "remove_child", timer), "completed")
func _yield_wait(var timer : Timer):
yield(timer, "timeout")
func yield_call_deferred(var node, var action, var parameter):
node.call_deferred(action, parameter)
yield(get_tree(), "idle_frame")
Now you can call, like before:
yield(yourgd.yield_wait(5000, self), "completed")
to wait for 5 seconds. (I used MS, since I dont like seconds)
The magic is simple: The code attaches a timer node to the caller. When the nodes gets queue_freed, then the timer node also will be removed. Thus, its promise (timer.create(…)) won’t wait till its end, but abort and return to your code.
Billy the Boy | 2020-12-28 02:58
This solution mostly works, although I get “ObjectDB instances leaked at exit” warnings if I quit my game during a yield_wait()
call
WARNING: cleanup: ObjectDB instances leaked at exit (run with --verbose for details).
At: core/object.cpp:2132
Leaked instance: Timer:11570 - Node name: @@3
Leaked instance: Timer:11602 - Node name: @@7
Leaked instance: Timer:11586 - Node name: @@5
Leaked instance: Timer:11618 - Node name: @@9
Poobslag | 2021-11-03 17:37