How to create a delay between NPC actions?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By aptek

Hey everyone. I have a bunch of enemy and NPC sprites moving around a TileMap node. This is a turn based game so after the player moves, the other characters act based on the code below. The act function in the enemy and civilian classes just moves them closer to either the level exit or towards each other by changing their tiles and then update_visuals is called to update sprite locations. The problem I am having is to the player it looks like everyone is acting at once. I would like to make a small delay between each sprite moving. Is there some way to use a timer before each enemy/civilian.act(self)? Thanks!

if current_ap <= 0:
    for enemy in enemies:
		enemy.act(self)
		
	for civilian in civilians:
		if civilian.tile != ladder_tile:
			civilian.act(self)
		else:
			civilian.remove()
			civilians.erase(civilian)
	current_ap = max_ap
	
call_deferred("update_visuals")
:bust_in_silhouette: Reply From: Jorge

Hi
Try to use yield before the code you want to excecute.

good luck

yield(get_tree().create_timer(0.5), "timeout")

Hey thanks for the suggestion. I added the yield statement before the civilian.act(self) function like below but the result was when the player taps a tile to move there is a delay of 0.5 seconds and then all sprites appear to move at once. So getting closer but still not the delay between each action that I am looking for. I am also not really sure why this doesn’t work as expected.

if current_ap <= 0:
for enemy in enemies:
    enemy.act(self)

for civilian in civilians:
    if civilian.tile != ladder_tile:
        yield(get_tree().create_timer(0.5), "timeout")
        civilian.act(self)
    else:
        civilian.remove()
        civilians.erase(civilian)
current_ap = max_ap

call_deferred("update_visuals")

aptek | 2020-07-05 18:02

Hey, adding update_visuals() after the .act(self) function made this work! Thanks for your help!

aptek | 2020-07-05 21:40

:bust_in_silhouette: Reply From: theeaglecometh

Are your enemies their own nodes?
If they are you could add a timer child (delay_timer) to their node then change your code to:

var delay_scale : float = 0.1
var counter : int = 1
for enemy in enemies:
              enemy.$delay_timer.wait_time = counter * delay_scale
              counter = counter + 1

This would set a timer in each successive enemy that is slightly bigger than the last. Then each enemy needs to react to this timer timing out.
In your enemy script connect the timer timeout call to the root node and add the code:

func _on_delay_timer_timeout():
	act(self)

Hey enemies are instances of an Enemy.tscn. So I think what you suggest would work but the other answer I got seems a bit more straightforward.

aptek | 2020-07-05 21:42