How to pause a piece of code until several others, and their tweened animations, are complete.

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

I have my game’s “clock” tied to the movement of a character being controlled by the player. It works by the player queuing a series of moves, which the code then iterates through. The basic code looks like this:

var path = [] #This contains the series of moves.
var destination #The final point in the path.
var animator = get_node_or_null("Path to a tween node")
signal time_advanced(time)
while player.position != destination:
    var next_move = path.pop_back()
    #Some code to determine the time taken in the next move.
    emit_signal("time_advanced", time)
    animator.interpolate_property(player, "position", null, next_move, ...)
    animator.start()
    #This is where I need to pause the code.

The time_advanced signal is connected to a piece of code in all of the NPCs’ code that handles movement for them. They each have their own path, and their own speed. As a result, they need to be able to move multiple times, each time the player moves. The code I have for this is roughly as follows:

var path = []
var time #This stores how much time this character has to act.
var destination
var animator = get_node_or_null("Path to a tween node")
func on_player_time_advanced(time):
    #Code to determine cost of next move.
    while time >= move_cost:
        var next_move = path.pop_back()
        time -= move_cost
        animator.interpolate_property(self, "position", null, next_move, ...)
        animator.start()
        yield(animator, "tween_all_completed")

As you can see, the NPCs are able to move several spaces for one player move. This means that I cannot use the yield I do for them for the player, because the player has to wait for more than one round of tweens to complete. I am looking for a way to pause the code for the player. Any suggestions are appreciated. :slight_smile:

:bust_in_silhouette: Reply From: rossunger

You might want consider not using Yield. I find yield is a bit dangerous, because it can make the game hang or feel unresponsive.

Instead of yield, you could connect the “tween_all_completed” signal to on_player_time_advanced and check if time < move_cost, then notify the player script.

e.g. NPC:

var path =
var time #This stores how much time this character has to act.
var destination
var animator = get_node_or_null(“Path to a tween node”)
func on_player_time_advanced(time):
#Code to determine cost of next move.
if time >= move_cost:
var next_move = path.pop_back()
time -= move_cost
animator.interpolate_property(self, “position”, null, next_move, …)
animator.start()
connect(“tween_all_completed”, self, “on_player_time_advanced”, time)
else:
disconnect(“tween_all_completed”, self, “on_player_time_advanced”)
get_tree().call_group(“Player”, “DoNextMove - or whatever”)

and then in player:

var path = #This contains the series of moves.
var destination #The final point in the path.
var animator = get_node_or_null(“Path to a tween node”)
signal time_advanced(time)
func DoTheMove():
if player.position != destination:
var next_move = path.pop_back()
#Some code to determine the time taken in the next move.
emit_signal(“time_advanced”, time)
animator.interpolate_property(player, “position”, null, next_move, …)
animator.start()
connect(“tween_all_completed”, self, “DoTheMove”)
else:
disconnect(“tween_all_completed”, self, “DoTheMove”)

In this case you’d add the player to a group called “Player”
You might want to consider using groups instead of signals

This is a different design pattern… I’m not sure while loops and yields are the best choice here (although I could be totally wrong! I don’t know your situation).

Another option to consider…Are you familiar with Global Event Bus design patterns?

First of all, no I’m not familiar with that design pattern.

Second, a couple of questions:
Since the NPC code calls the player code when it is done, why does the player code also moniter the tween_all_completed signal?
Is there a way to keep the first NPC that runs out of time from restarting the player, before the others are done?

Thanks for the help!

fatcat__25 | 2022-01-27 02:37

re: global event bus
GDQuest

The player monitors it’s own tween… unless that’s not important? in which case you can skip that :slight_smile:

If you’re trying to wait until all the npcs are done, then you could have an NPC controller, that keeps an array of which NPCs are in progress, and then have them pop themselves from that array when they’re done, at which point the NPC controller would notify the player… NPC controller could be an autoload singleton or something…

also, this is just one possible approach… I’m not sure what would be best practice in this case… an ideal version might involve some refactoring and architecture changes to your project. I’m still fairly new to godot, and I often find myself having structured things in a way that isn’t ideal… using the wrong design patterns… not sure if it’s my lack of experience, or maybe just part of the art of programming.

rossunger | 2022-01-27 02:56