No. However there is no ârightâ way to do this and you will find everybody does it differently.
My state machine has one purpose, get told what state to be in and change to that state. The available states are the children it has been given which are separate scenes. It tells the current state to exit, the new state to enter, and that is about it. No rules, no special cases, just that simple task.
The rules for states are all kept in my behaviour_,manager. It monitors and tracks relevant information and tells the state machine what state to change to.
# Civillians run away from player
func handle_meandered_too_close_to_player() -> void:
Actor.MovementManager.max_speed = avoiding_top_speed
Actor.StateManager.change_state("Avoiding")
The state âAvoidingâ in this example will set a required direction away from the player and set an impulse on the actor, which the movement manager deals with (slowing, turning, accelerating away etc).
The signal that it has meandered (a wandering around state) too close to the player is sent by the sensors_manager. The same signal could originate from anywhere, it does not matter from where, wherever is most convenient. Like the single to deal with taking damage is sent from the life_manager. It is the behaviour_manager that is the hub of everything, handling all the different scenarios including taking into account what the current state is. Like this example below:
func handle_on_patrol_player_detected() -> void:
if self.Actor.combat_enabled:
self.Actor.StateManager.change_state("Attacking")
else:
self.Actor.StateManager.change_state(self.Actor.initial_state)
So an enemy scene ends up looking something like this:
Notice most of these are scenes themselves and are common across all the enemy types. For this civilian only the behaviour, movement and visuals are custom scripts for this actor. Even these though extend base classes that share all the fundamental behaviours in them, they just override them when needed, so are fairly short.
Anyway, hope this helps. The benefit here is that when something peculiar happens I can easily identify if it is a problem with the state itself, the behaviour decision tree, the visuals handling or the lifemanager etc. Debugging has become very simple and my enemies can exhibit very different behaviour with very little changes to the code.
PS In short, the state manager is not a decision maker, it sends no signals, and does not deal with other states or state changes. It simply deals with one state and the initialisations to enter that state and to exit it. It does not need to know I have bumped into something, the sensors do that and tell the behaviour manager we have encountered something. It does not need to say that we should die now, because the lifemanager detects that and tells the behaviour manager to handle dying etc etc.
Here is what my statemanager would typically look like.