Godot Version : Godot 4.3
Question: Is there a “Best” way to cancel an await.
Thanks for looking at my topic 
I’m working on a horror game with a hiding mechanic. I have a method the player uses to handle the hiding interactions. Currently its working by using a few awaits.
SUDO CODE
SUDO CODE
in_hiding_transition = true
await reset()
await hidee.mount_camera(where the player camera mounts, transition time)
await hiding_spot.hiding_interactor.enter()
in_hiding_transition = false
However, there is a scenario I’d like the game to have where the player can be interrupted during this hiding transition where a similar process using awaits will happen when the AI attacks the player.
This approach looks fine on paper but once this thing gets started it’s hard to interrupt. I did try adding a cancel method that would just set a cancelled bool. Then in the method with lots of awaits after each await, I’d query if cancelled was true and then return out of the method.
If CANCELLED SUDO CODE
If CANCELLED SUDO CODE
in_hiding_transition = true
await reset()
if cancelled:
do_the_cancelling()
await hidee.mount_camera(where the player camera mounts, transition time)
if cancelled:
do_the_cancelling()
await hiding_spot.hiding_interactor.enter()
if cancelled:
do_the_cancelling()
in_hiding_transition = false
A very ugly approach and not maintainable long term
I’ve searched the docs and even consulted the bots. However, I think my approach was flawed from the start.
Has anyone got any better ideas/approaches for this system.
Full Current code here
Please note I’ve kept this as bare bones as possible if you need more info please let me know.
func enter() -> Signal:
hidee.item_controller.hide_equipment()
in_hiding_tranisition = true
await hiding_spot.hiding_interactor.reset()
await hidee.mount_camera(hiding_spot.hiding_interactor.camera_anchor, 0.1)
await hiding_spot.hiding_interactor.enter()
hidee.detectable_target.detectable = false
hidee.teleport_to_node(hiding_spot.hide_hint, true)
in_hiding_tranisition = false
is_hidden = true
return finished
Further;
- This question is coming from a ex-unity game developer who has some experience using coroutines in C#
- It’s also my first time really interacting with the forum. If I’m missing something let me know how I can improve these interactions.
Thanks for reading
Could you use a tween for this?
Then you could save a reference to the tween and cancel it later as you wish.
I’d think you’d want to use a timer instead of await
.
Those have callbacks they can fire, and they’re nodes in the tree so if you need to cancel them you can queue_free()
them.
@thisisnotdalton
Thanks for the response.
I did want this approach. Maybe you can correct me. When you start a tween unless cancelled and restarted. It won’t recalculate the tween to the new intended target value.
In the example of moving the players camera to the hiding animations camera mount. if I want to tween to a position. and that position moves during the tween. Then the current tween will only move to the target point it was initially told to move to.
Please say I’m wrong because I haven’t had much luck with tweens yet.
@hexgrid
Thanks for the comment.
With the timer approach. initially I wasn’t too sure how best this could be handled. Other than daisy chaining mutliple timers that would call the next behaviour. Eitherway I went ahead and implemented the system with timers and it does work.
I broke the hiding into 3 stages/methods.
enter → enter_animation → entered
Implementation
class_name HidingHandler extends Node
#This class is doing way too much!
var hidee:Player = null
var hiding_spot:HidingSpot = null
var in_hiding_tranisition:bool = false
var is_hidden:bool = false
var timer:Timer = Timer.new()
signal finished
func _init(_hidee:Player, _hiding_spot:HidingSpot) -> void:
hidee = _hidee
hiding_spot = _hiding_spot
add_child(timer)
timer.one_shot = true
timer.autostart = false
timer.timeout.connect(flush_timeout_connections) # Automatic tear down between stages
#Set all intitial state!
func enter():
hidee.item_controller.hide_equipment()
hidee.freeze()
in_hiding_tranisition = true
var mount_duration:float = 1
hidee.mount_camera(hiding_spot.hiding_interactor.camera_anchor, mount_duration)
timer.start(mount_duration)
timer.timeout.connect(enter_animation)
print("enter")
#Call animation and wait for it to finish
func enter_animation() -> void:
timer.start(hiding_spot.hiding_interactor.get_anim_length("enter"))
timer.timeout.connect(entered)
hiding_spot.hiding_interactor.play("enter")
print("animation")
#Set player values when the animation has finished
func entered() -> void:
hidee.detectable_target.detectable = false
hidee.teleport_to_node(hiding_spot.hide_hint, true)
in_hiding_tranisition = false
is_hidden = true
print("entered")
#This method is on the player just putting it here for the example
func cancel() -> void:
queue_free()
func flush_timeout_connections() -> void:
for connection in timer.timeout.get_connections():
if connection["callable"] != flush_timeout_connections:
print("removed " + str(connection["callable"]))
timer.timeout.disconnect(connection["callable"])
Video of it working → https://youtu.be/gbRA2mjNbxI
Massive thank you for this idea 
When I have had to make tweens that interpolate things between moving targets, I would usually opt to store the destination node instead of its position, and then use tween_method to call a function to update the position based on the node’s most up-to-date position.
1 Like