So the scenario is this: the player takes a hit and his health drops 10 units (out of 100). I then call a function with multiple awaits in order to regenerate the health every 3 seconds by 1 unit.

The problem is if the player takes another hit while regenerating, the first called functtion (with the initial awaits) keeps adding the original health points, and now there's a second one doing that on top.

I would like to stop the first function with the await timers from running at all. Something as simple as having a bool flag that turns on when the function is first called, and then each time it is called it checks the flag. The problem is I don't know what to do if I find the flag already on. I don't know how to discard the already running (or queued) await calls.

A few notes:

  • i used one player as an example, but i do in fact have several thousands of objects that trigger their own await, so just using a get_time and checking in the _process loop is not an option
  • i want just a single function regenerating at a time. I don't want to stagger them for various reasons (the simplest being i may not use just a constant single health point, but may regenerate 5 then 4 then 3 points and so on)
  • I am open to other ideas on how to call a function that runs in parallel but can be cancelled once it is called again

Example code:

func _ready():
	player.health_points = 100

func _process():
	if (player.hit):
		player.health_points -= 10
		_start_regen(player)

func _start_regen():
	await get_tree().create_timer(3).timeout
	player.health_points += 1
	await get_tree().create_timer(3).timeout
	player.health_points += 1
	await get_tree().create_timer(3).timeout
	player.health_points += 1

Thanks

The easiest solution in this case would be to use coroutines, however this requires the GDScript version being at least 3.1.

Coroutines are essentially running functions that can be suspended at any point, which allows you to "pause" a function and then come back to it with the same data/state. This would allow you to "discard" a running await, as the coroutine would be paused with each health increment. Then when the player is hit again, you can simply detect that the coroutine is already running and then skip over it.

For more information on coroutines in GDScript, check out this page: http://docs.godotengine.org/en/3.1/getting_started/scripting/gdscript/gdscript_basics.html#coroutines

You can use a tween which can be canceled:

var hp = 100
var regen_tween = null

func regen(count: int = 3, delay: float = 1):
	if regen_tween:
		regen_tween.kill()
	regen_tween = get_tree().create_tween()
	for i in count:
		regen_tween.tween_callback(increment_hp).set_delay(delay)
		
func increment_hp():
	hp += 1
	print("HP=", hp)
		
# stand in for player hit
func _input(event):
	if event is InputEventKey and event.is_pressed():
		regen(5, .5) # regenerate 5 hitpoints in .5 second intervals

    xyz thank you! I haven't really worked with tween before but it seems like an elegant solution. I'll give it a try