0 votes

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. :)

Godot version 3.4.2.stable
in Engine by (57 points)

1 Answer

0 votes
Best answer

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 "tweenallcompleted" signal to onplayertimeadvanced and check if time < movecost, 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 = getnodeornull("Path to a tween node")
func on
playertimeadvanced(time):
#Code to determine cost of next move.
if time >= movecost:
var next
move = path.popback()
time -= move
cost
animator.interpolateproperty(self, "position", null, nextmove, ...)
animator.start()
connect("tweenallcompleted", self, "onplayertimeadvanced", time)
else:
disconnect("tween
allcompleted", self, "onplayertimeadvanced")
gettree().callgroup("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 = getnodeornull("Path to a tween node")
signal time
advanced(time)
func DoTheMove():
if player.position != destination:
var nextmove = path.popback()
#Some code to determine the time taken in the next move.
emitsignal("timeadvanced", time)
animator.interpolateproperty(player, "position", null, nextmove, ...)
animator.start()
connect("tweenallcompleted", self, "DoTheMove")
else:
disconnect("tweenallcompleted", 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?

by (1,340 points)
selected by

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 tweenallcompleted 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!

re: global event bus
https://www.gdquest.com/docs/guidelines/best-practices/godot-gdscript/event-bus/

The player monitors it's own tween... unless that's not important? in which case you can skip that :)

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.

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read Frequently asked questions and How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to [email protected] with your username.