0 votes

My project is getting bigger and has lots of states for the state machine. Unfortunately, it seems that I constantly have to use boolean switches to prevent parts of the code from being looped more than once.

Stuff like this pseudocode:

EnemyAttackState.gd:

func _process(delta):
  enemy.attack()

func attack():
  enemy.move_towards_player()

 if distance to player <= 5:
   enemy.stop()
   enemy.animation_player.play("attack")

 if executed_once == false:
   player.take_damage(weapon.damage_dealt)
   executed_once = true

 enemy_statemachine.transition_to("Enemy Idle State")

This is only a simple example, but I have a lot of complex functions in my project that alternate between parts that have to be looped by the process function and parts that should only be executed once. Is there a way to have a function like the example above, e.g. that moves the enemy to a target but deals damage only once?

I'm new to Godot and coding, but my feeling tells me that this is probably bad practice. I know about signals but it seems like such a hassle to set up hundreds of signal connections.... Is there a more elegant way? Or is it OK to do stuff like this?

Godot version 3.4
in Engine by (70 points)
edited by

1 Answer

0 votes
Best answer

Hey there, for a more robust (but more complex) statemachine, I recommand you this article. No more booleans, a clean design pattern, this is the elegant way you're looking for

by (167 points)
selected by

Hi, thanks for taking the time to answer my question. Unfortunately, I am already using this exact state machine, but it doesn't solve the problem I described in my original question.

It is true, that you don't need flags in such a state machine when dealing with simple states like MOVE and JUMP, but the states in my game often contain a longer sequence of instructions between actions that have to be looped and actions that should only be called once. The internet is full of questions about "how to run code only once" and the general answer seems to be" use a boolean flag". This method does indeed work, but I hoped or suspected that there might be a different solution that I cannot see due to my lack of experience.

what kind of behaviour do you wish to create that needs loops and everything ?

For example, what I wrote in my original question. Movement of the enemy to the current player position has to be handled inside a process or physics process loop, or it won't move the full distance. But when the enemy arrives and attacks the player, the attack and deduction of the player's health should happen only once.

Here is the actual code of the "Enemy Attack State" in the turn based Battle Statemachine:

    if enemy.position.distance_to(player) > 30:
        enemy.move_and_slide(enemy.velocity)


    if enemy.position.distance_to(player) < 30:

        if executed_once == false:
            enemy.attack_position = _enemy.position
            var _applied_dmg = enemy.stats.dmg_dealt - player.armor

            if _applied_dmg > 0:
                player.TakeDamage(_applied_dmg)
            else:
                player.TakeDamage(0)

        enemy.play_attack_animation()

        executed_once = true

        yield(enemy.animationplayer,"animation_finished")
        battlemode.transition_to("EnemyReturn")

In this example, movement has to be in the loop, TakeDamage() and the call to start the attack animation may only be performed once, and the yield afterwards only works when it's in the loop again (otherwise executed_once = true gets postponed and the whole thing breaks).

Maybe you didn't understand well the state pattern ? You should split your logic into 1 state Move and another Attack. The thing is you could move toward the player then, when another "component" of your logic detect that the player is close enough, the statemachine is requested to switch to Attack state and imadiately after the state end, the state goes back to Move

Understand that ONE state should make ONE thing

Ok, thanks for clarifying. That is the way I use a state machine for my main character's movement and simple states. The battle state machine has about 15 states already, so I guess I either have to split it up even more or continue using flags.

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 webmaster@godotengine.org with your username.