Finite State Machines with Ash entity system framework

Finite state machines are one of the staple constructs in game development. During the course of a game, game objects may pass through many states and managing those states effectively is important.

The difficulty with finite state machines in an entity system framework like Ash can be summed up in one sentence – the state pattern doesn’t work with an entity system framework. Entity system frameworks use a data-oriented paradigm in which game objects are not self-contained OOP objects. So you can’t use the state pattern, or any variation of it. All the data is in the components, all the logic is in the systems.

If your states are few and simple it is possible to use a good old fashioned switch statement inside a system, with the data for all the states in one or more components that are used by that system, but I wouldn’t usually recommend that.

When creating Stick Tennis I was faced with the problem of how to manage states as the two main entities in the game are the two players, and they go through a number of states as they…

  • prepare to serve
  • swing arm to toss the ball
  • release the ball
  • swing the racquet
  • hit the ball
  • follow through
  • run to a good position
  • react to the opponent hitting the ball
  • run to intercept the ball
  • swing the racquet
  • hit the ball
  • follow through
  • run to a good position
  • react to winning the point
  • …etc

Stick Tennis is a complex example, and I can’t show you the source code, so instead I’ll use something a little simpler, with source code.

An example

Lets consider a guard character in a game. This character patrols along a path, keeping watch. If they spot an enemy, they attack him/her.

In a traditional object-oriented state machine we might have a class for each state

public class PatrolState
{
    private var guard : Character;
    private var path : Vector.<Point>;

    public function PatrolState( guard : Character, path : Vector.<Point> )
    {
        this.guard = guard;
        this.path = path;
    }

    public function update( time : Number ) : void
    {
        moveAlongPath( time );
        var enemy : Character = lookForEnemies();
        if( enemy )
        {
            guard.changeState( new AttackState( guard, enemy ) );
        }
    }
}
public class AttackState
{
    private var guard : Character;
    private var enemy : Character;

    public function AttackState( guard : Character, enemy : Character )
    {
        this.guard = guard;
        this.enemy = enemy;
    }

    public function update( time : Number ) : void
    {
        guard.attack( enemy );
        if( enemy.isDead )
        {
            guard.changeState( new PatrolState( guard, PatrolPathFactory.getPath( guard.id ) );
        }
    }
}

In a entity system architecture we have to take a slightly different approach, but the core principle of the state pattern, to split the state machine across multiple classes, one for each state, can still be applied. To implement the state machine in an entity framework we will use one System per state.

public class PatrolSystem extends ListIteratingSystem
{
    public function PatrolSystem()
    {
        super( PatrolNode, updateNode );
    }

    private function updateNode( node : PatrolNode, time : Number ) : void
    {
        moveAlongPath( node );
        var enemy : Enemy = lookForEnemies( node );
        if( enemy )
        {
            node.entity.remove( Patrol );
            var attack : Attack = new Attack();
            attack.enemy = enemy;
            node.entity.add( attack );
        }
    }
}
public class AttackSystem extends ListIteratingSystem
{
    public function AttackSystem()
    {
        super( AttackNode, updateNode );
    }

    private function updateNode( node : PatrolNode, time : Number ) : void
    {
        attack( node.entity, node.attack.enemy );
        if( node.attack.enemy.get( Health ).energy == 0 )
        {
            node.entity.remove( Attack );
            var patrol : Patrol = new Patrol();
            patrol.path = PatrolPathFactory.getPath( node.entity.name );
            node.entity.add( patrol );
        }
    }
}

The guard will be processed by the PatrolSystem if he has a Patrol component, and he will be processed by the AttackSystem if he has an Attack component. By adding/removing these components from the guard we change his state.

The components and nodes look like this…

public class Patrol
{
    public var path : Vector.<Point>;
}
public class Attack
{
    public var enemy : Entity;
}
public class Position
{
    public var point : Point;
}
public class Health
{
    public var energy : Number;
}
public class PatrolNode extends Node
{
    public var patrol : Patrol;
    public var position : Position;
}
public class AttackNode extends Node
{
    public var attack : Attack;
}

So, by changing the components of the entity, we change the entities state and thus change the systems that process the entity.

Another example

Here’s another, more complex example using the Asteroids example game that I use to illustrate how Ash works. I’ve add an additional state to the spaceship for when it’s shot. Rather than simply removing the spaceship when it is shot, I show a short animation of it breaking up. While doing this, the user won’t be able to move it and the spaceship won’t react to collisions with other objects.

The two states require the following

While the ship is alive –

  • It looks like a spaceship
  • The user can move it
  • The user can fire its gun
  • It collides with asteroids

When the ship is dead –

  • It looks like bits of a spaceship floating in space
  • The user cannot move it
  • The user cannot fire its gun
  • It doesn’t collide with asteroids
  • After a fixed time it is removed from the game

The relevant piece of code, where the spaceship dies, is in the CollisionSystem. Without the second state it would look like this

for ( spaceship = spaceships.head; spaceship; spaceship = spaceship.next )
{
    for ( asteroid = asteroids.head; asteroid; asteroid = asteroid.next )
    {
        if ( Point.distance( asteroid.position.position, spaceship.position.position )
            <= asteroid.position.collisionRadius + spaceship.position.collisionRadius )
        {
            creator.destroyEntity( spaceship.entity );
            break;
        }
    }
}

The code tests whether the ship is colliding with an asteroid, and if it is it removes the ship. Elsewhere, the GameManager system handles the situation where there is no spaceship and creates another one, if any are left, or ends the game. Instead of destroying the spaceship, we need to change its state. So, lets try this...

We can prevent the user controlling the spaceship by simply removing the MotionControls and GunControls components. We might as well remove the Motion and Gun components while we’re at it since they're of no use without the controls. So we replace the code above with

for ( spaceship = spaceships.head; spaceship; spaceship = spaceship.next )
{
    for ( asteroid = asteroids.head; asteroid; asteroid = asteroid.next )
    {
        if ( Point.distance( asteroid.position.position, spaceship.position.position )
            <= asteroid.position.collisionRadius + spaceship.position.collisionRadius )
        {
            spaceship.entity.remove( MotionControls );
            spaceship.entity.remove( Motion );
            spaceship.entity.remove( GunControls );
            spaceship.entity.remove( Gun );
            break;
        }
    }
}

Next, we need to change how the ship looks and remove the collision behaviour

for ( spaceship = spaceships.head; spaceship; spaceship = spaceship.next )
{
    for ( asteroid = asteroids.head; asteroid; asteroid = asteroid.next )
    {
        if ( Point.distance( asteroid.position.position, spaceship.position.position )
            <= asteroid.position.collisionRadius + spaceship.position.collisionRadius )
        {
            spaceship.entity.remove( MotionControls );
            spaceship.entity.remove( Motion );
            spaceship.entity.remove( GunControls );
            spaceship.entity.remove( Gun );
            spaceship.entity.remove( Collision );
            spaceship.entity.remove( Display );
            spaceship.entity.add( new Display( new SpaceshipDeathView() ) );
            break;
        }
    }
}

And finally, we need to ensure that the spaceship is removed after a short period of time. To do this, we’ll need a new system and component like this

public class DeathThroes
{
    public var countdown : Number;
        
    public function DeathThroes( duration : Number )
    {
        countdown = duration;
    }
}
public class DeathThroesNode extends Node
{
    public var death : DeathThroes;
}
public class DeathThroesSystem extends ListIteratingSystem
{
    private var creator : EntityCreator;
    
    public function DeathThroesSystem( creator : EntityCreator )
    {
        super( DeathThroesNode, updateNode );
        this.creator = creator;
    }

    private function updateNode( node : DeathThroesNode, time : Number ) : void
    {
        node.death.countdown -= time;
        if ( node.death.countdown <= 0 )
        {
            creator.destroyEntity( node.entity );
        }
    }
}

We add the DeathThroesSystem to the game at the start, so it will handle the drawn-out death of any entity. Then we add the DeathThroes component to the spaceship when it dies.

for ( spaceship = spaceships.head; spaceship; spaceship = spaceship.next )
{
    for ( asteroid = asteroids.head; asteroid; asteroid = asteroid.next )
    {
        if ( Point.distance( asteroid.position.position, spaceship.position.position )
            <= asteroid.position.collisionRadius + spaceship.position.collisionRadius )
        {
            spaceship.entity.remove( MotionControls );
            spaceship.entity.remove( Motion );
            spaceship.entity.remove( GunControls );
            spaceship.entity.remove( Gun );
            spaceship.entity.remove( Collision );
            spaceship.entity.remove( Display );
            spaceship.entity.add( new Display( new SpaceshiopDeathView() ) );
            spaceship.entity.add( new DeathThroes( 5 ) );
            break;
        }
    }
}

And that is our state transition. The transition is achieved by altering which components the entity has.

The state is encapsulated in its components

This is the general rule of the entity system architecture - the state of an entity is encapsulated in its components. If you want to change how an entity is processed, you should change its components. That will alter which systems operate on it and that changes how the entity is processed.

Standardised state machine code

To help with state machines I’ve added some standard state machine classes to Ash. These classes help you manage states by defining states based on the components they contain, and then changing state simply by specifying the new state you want.

A finite state machine is an instance of the EntityStateMachine class. You pass it a reference to the entity it will manage when constructing it. You will usually store the state machine in a component on the entity so it can be recovered from within any system that is operating on the entity.

var stateMachine : EntityStateMachine = new EntityStateMachine( guard );

A state machine is configured with states, and the state can be changed by calling the state machine's changeState() method. States are identified by a string, which is assigned when the state is created and used to identify the state when calling the changeState() method.

States are instances of the EntityState class. They may be added to the EntityStateMachine using the EntityStateMachine.addState() method, or they may be created and added in one call using the EntityStateMachine.createState() method.

var patrolState : EntityState = stateMachine.createState( "patrol" );
var attackState : EntityState = stateMachine.createState( "attack" );

A state is a set of components that should be added to the entity when that state is entered, and removed when that state exits (unless they are also required for the next state). The add method of the EntityState specifies the type of component required for the state and is followed by a rule specifying how to create that component.

var patrol : Patrol = new Patrol();
patrol.path = PatrolPathFactory.getPath( node.entity.name );
patrolState.add( Patrol ).withInstance( patrol );
attackState.add( Attack );

The four standard rules for components are

entityState.add( type : Class );

Without a rule, the state machine will create a new instance of the given type to provide the component every time the state is entered.

entityState.add( type : Class ).withType( otherType : Class );

This rule will create a new instance of the otherType every time the state is entered. otherType should be the same as or extend the specified component type. You only need this rule if you create component classes that extend other component classes and should be treated as the base class by the engine, which is rare.

entityState.add( type : Class ).withInstance( instance : * );

This method will use the provided instance for the component every time the state is entered.

Finally

entityState.add( type : Class ).withSingleton();

or

entityState.add( type : Class ).withSingleton( otherType : Class );

will create a single instance and use that one instance every time the state is entered. This is similar to using the withInstance method, but the withSingleton method will not create the instance until it is needed. If otherType is omitted, then the singleton with be an instance of type, if included it will be of otherType and otherType must be the same as or extend type.

Finally, you can use custom code to provide the component by implementing the IComponentProvider interface and then using your custom provider with

entityState.add( type : Class ).withProvider( provider : IComponentProvider );

The IComponentProvider interface is defined as

public interface IComponentProvider
{
    function getComponent() : *;
    function get identifier() : *;
}

The getComponent method returns a component instance. The identifier in the IComponentProvider is used to compare two component providers to see if they will effectively return the same component. This is used to avoid replacing a component unnecessarily if two successive states use the same component.

The methods are designed to be chained together, to create a fluid interface, as you’ll see in the next example.

Back to the examples

If we apply these new tools to the spaceship example, the states are set-up when the spaceship entity is created, as follows

var fsm : EntityStateMachine = new EntityStateMachine( spaceshipEntity );

fsm.createState( "playing" )
   .add( Motion ).withInstance( new Motion( 0, 0, 0, 15 ) )
   .add( MotionControls )
       .withInstance( new MotionControls( Keyboard.LEFT, Keyboard.RIGHT, Keyboard.UP, 100, 3 ) )
   .add( Gun ).withInstance( new Gun( 8, 0, 0.3, 2 ) )
   .add( GunControls ).withInstance( new GunControls( Keyboard.SPACE ) )
   .add( Collision ).withInstance( new Collision( 9 ) )
   .add( Display ).withInstance( new Display( new SpaceshipView() ) );

fsm.createState( "destroyed" )
   .add( DeathThroes ).withInstance( new DeathThroes( 5 ) )
   .add( Display ).withInstance( new Display( new SpaceshipDeathView() ) );

var spaceshipComponent : Spaceship = new Spaceship();
spaceshipComponent.fsm = fsm;
spaceshipEntity.add( spaceshipComponent );
fsm.changeState( "playing" );

and the state change is simplified to

for ( spaceship = spaceships.head; spaceship; spaceship = spaceship.next )
{
    for ( asteroid = asteroids.head; asteroid; asteroid = asteroid.next )
    {
        if ( Point.distance( asteroid.position.position, spaceship.position.position )
            <= asteroid.position.collisionRadius + spaceship.position.collisionRadius )
        {
            spaceship.spaceship.fsm.changeState( "destroyed" );
            break;
        }
    }
}

To do

There will be further refinement and additions to the state machine tools based on feedback so please do let me know how you get on with them. Use the mailing list for Ash to get in touch.

31 thoughts on “Finite State Machines with Ash entity system framework

  1. Thanks for posting about FSMS. This shows how naturally these frameworks fit together, and reduces significantly the code (and code documentation) needed to keep track of what is onscreen.

  2. Just wanted to thank you again for your efforts,
    an interesting approach to solve such a problem 😛

    Much better than traditional means of changing and encapsulating state 😀

  3. How does the state machine handle cases when a new state is being entered into, in order to reset the components to it’s starting values? For example, if i go to the “death” state, the entity’s animation component might need to reset the frame counter back to zero..

    I assume this is done by the systems which listen to Families ‘nodeAdded’ event? This means i’m entirely dependant on state-specific Systems and Families to perform state initialization prior to processing? I assume this often means 1 state system to handle 1 state family, right?

    Often, different states for an entity might share the same component instances. Your code examples should reflect that, storing key var references which are used for different states as well.

  4. @Glidias

    There are many ways to set or reset the data on a component after the state is entered.

    1. You could simply follow the state change with code to set the component’s property.
    2. You could use a standard component provider, which will replace the component with a new component of the same type, which will thus have the default values for the component’s properties.
    3. You could provide a custom component provider that doesn’t replace the component if it’s already present but does set the properties of the component as you want them.

    Also, I’ve been considering adding a withProperties() modifier after the component provider, that sets properties on the component, but have yet to implement it.

  5. In your Patrol/Attack example tho, fsm isn’t really needed beyond simply adding/removing the state component. After all, the state class will contain constructor or static method parameters necessary for initializing it.

  6. Technically, there’s no need to include an enemy var in the Attack component because the AttackSystem would already contain a reference to 1 enemy or multiple potential enemies. In a real mp game, the bot will have to to decide the best candidate to shoot at, and the AttackSystem could determine that. Same goes for the PatrolSystem which probably contains path graph data to determine the best path.

  7. The vars can still be kept I guess but would still need some form of resetting

  8. @Glidias

    Re: “the state class will contain constructor or static method parameters”
    What state class?

    Re: “the AttackSystem would already contain a reference to 1 enemy or multiple potential enemies”
    Only if I choose to construct the game that way, which I haven’t.

    Re: “The vars can still be kept I guess but would still need some form of resetting”
    Every time the attack state is entered, a new Attack component is created and the code sets the enemy property to the enemy to be attacked. There is no need to “reset” anything.

  9. Thanks for this Richard, really helpful! I like the chaining style for state configuration :)

    I’m currently solving this in a different way (I’m using Python, so no Ash for me!) using a StateSystem instead, which just checks State components for current state/new state and adds/removes components from the owning entity as necessary.

    Do you think that’s a reasonable approach (aside from not having an interface to program to, of course)?

    Thanks again for your articles, really helpful!

  10. Isn’t there a more ES-way to do it? I don’t think it’s appropriate to make FSMs a special case when there is a lucid ES solution to the problem.

    That is, states are new entities referenced by FSMComponents and managed by FSMSystems.

    This doesn’t make complete sense until you realize that multiple entities can share the same components.

    Though there are multiple ways to do it, you can store the conditions (as flags preferably) for changing states and a reference to the new entity in each FSMComponent, from which the FSMSystem would simply update the entity ID to reference the new entity. All other systems would continue using the original ID, which now references the new state.

    This allows your FSM to be liberated from other systems and provides an added bonus of consistent and predictable state changes.

  11. Hi Marco

    I don’t consider my solution to be a non-entity-system way of doing finite state machines. On the contrary, it recognises that the components are the state of an entity and the entity itself is a state-machine (albeit more fuzzy than finite), so all that’s needed is tooling to help manage that state.

  12. You have an FSM sitting on top of an entity system, rather than integrating the FSM into the component-entity system architecture.

    You are correctly representing a change of state as a different set of components, but the states themselves (in your implementation) are NOT components- they are governing constructs sitting on top of specific types of entities (or even just specific entities). You should be able to define and declare each state within components.

    This works against the ES model of being able to freely and dynamically associate components with entities. In a more complicated game, dynamically adding states and/or state managers to a variety of differing entities at run-time is a powerful tool for the designer. The only way to reasonably achieve this is to make the FSM work as a series of components acted upon by an FSM system. This has the added bonus of data security, as you’ll never change states until each system has updated.

    Your system doesn’t quite achieve that state of flexibility without wrapping it into a component system- at which point there’s no reason why it should be anything but.

  13. Hi Marco

    Sorry, I misunderstood your complaint.

    Wanting the state-management itself to be handled by components and systems is worthy but prone to issues. When I first tried implementing state machines in Ash I used a component to represent an FSM and a system to process that component and change the state (see https://github.com/richardlord/Ash/tree/6cc7572be2a1918cc876b7ec2c8d3e69eb9ff112/src/net/richardlord/ash/fsm). The FSM component was configured with the states and added to the entity that it belonged to. The FSM system would then process the component and change the entity’s state as necessary.

    There were two problems with this…

    1. Because an entity can only have one component of any type entities were restricted to only one FSM each. This is manageable by adding a multi-FSM component containing multiple state machines. This adds complexity but would have been fine if it was the only issue.

    2. Because the state change is delayed, any further action required after the state change has to be queued up until after the system has changed the state, probably via callbacks or promisses. e.g. two objects collide and you want to change the state of one objecting including setting some property based on a property of the other object. You can’t do this until after the state change is processed because it relies on the component being in the new state. Ideally, all this would be handled inside the state machine, but that makes the state machine much more complex, so I let the developer handle this themselves in their code where they call changeState on the state machine.

    So, in the interests of wanting something that works and does not restrict developers I settled on the solution I have. The important point for me was to recognise that states for an FSM are just combinations of components – something that seems not to be recognised by other entity-system frameworks that I could find. When it comes to actual working code, I tend to favour pragmatism over purity, and so accepted the impurity of my solution as better than everything else I tried or had seen elsewhere.

    Now, you are suggesting that each FSM be its own entity, which hadn’t occurred to me. My immediate instinct is that that will be complicated and ultimately impractical, but instincts are sometimes wrong. Have you tried implementing this idea?

  14. Firstly, why limit the number of components of a given type? Also, nesting groups of similar components into a single component-containing component tends to work against the flexibility we want from an ES. It’s okay for components to point to each other, but I would avoid nesting that information.

    Secondly,
    In continuous collision detection, you should be yielding to other systems for evaluation in the order in which hits occur. This will always ensure that a coherent resolution of hits occurs.

    The data structure that describes an entity isn’t what determines an entity’s identity- it’s the configuration of components. Two entities that share components also share a functional identity. Swapping pointers is just a really simple way to accomplish this. There are nonetheless many other creative ways to put state machines into the ES component-architecture. If we were dealing with class hierarchies, your instinct would be correct- but we aren’t.

  15. Hi!
    Thanks for your great framework!
    I have a question regarding EntityStateMachies: why is the field currentState private, or being more general, why there is no method of finding out the current state of the Entity (and also the name of the state)? Are there some consistency\security reasons? Thanks!

  16. Hi Vladimir

    That is because I don’t think it should be necessary to access it. You should never care what the current state is, i.e. you should never write

    if( stateMachine.currentState == "mystate" )...

    The current state is represented by the components. Your systems should only care about the components, not some magic string that represents a state. If you’re doing conditional code based on the state than you may be using one component where you should use two.

  17. Hey, thanks for this, it’s great.
    A little big problem though: there doesn’t seem to be an easy way of giving the same component to many states. This is crucial. I actually implemented this in C# with Artemis, plus with the possibility of using the same component for more than one state. It’s kind of like inheritance, except it’s actually *gasp* composition!
    Like so (pseudocode):
    // Each component has a name.
    FSM.Components = {
    “onAir”: OnAirComponent(gravity),
    “onGround”: OnGroundComponent(aceleration),
    “walk”: WalkComponent(walk_speed),
    “run”: RunComponent(run_speed),
    “idle”: IdleComponent(friction),
    “jump”: JumpComponent(jump_force)
    }
    // Each state has a name, plus a list of component names.
    FSM.States = {
    “idle”: [“onGround”, “idle”],
    “walk”: [“onGround”, “walk”],
    “run”: [“onGround”, “run”],
    “jump”: [“onAir”, “jump”],
    “fall”: [“onAir”]
    }

  18. Hi Ideka

    You can use withInstance to use the same component in multiple states

    var onGround : OnGroundComponent = new OnGroundComponent( acceleration );
    
    fsm.createState( "idle" )
        .add( OnGroundComponent ).withInstance( onGround )
        .add( IdleComponent )...
        
    fsm.createState( "walk" )
        .add( OnGroundComponent ).withInstance( onGround )
        .add( WalkComponent )...

    or you can create any provider separately and add that to multiple states to share the components

    var provider : ComponentSingletonProvider
        = new ComponentSingletonProvider( OnGroundComponent );
    
    fsm.createState( "idle" )
        .add( OnGroundComponent ).withProvider( provider )
        .add( IdleComponent )...
        
    fsm.createState( "walk" )
        .add( OnGroundComponent ).withProvider( provider )
        .add( WalkComponent )...
  19. Oh, I guess I missed that before.

    By the way, about this:
    > You should never care what the current state is, i.e. you should never write
    > if( stateMachine.currentState == “mystate” )…

    What if I have a character with a bunch of components and states, that can either have a PlayerInputComponent or a CPUInputComponent? The player is a human being that can deduce the state the character is in by looking at it, and then choose how to act accordingly. The CPU, however, must query the state machine, isn’t that right?

  20. Hi Ideka

    I don’t think so, no. The state of an entity is represented by the components the entity has and their contents. A finite state machine is just a formalised way of managing the process of altering those components in response to other activity. What the entity is doing is represented by the components, not the state machine. So for the non-player character to know what another character is doing it should examine its components and their contents, not the state machine.

  21. OK, sounds reasonable enough.

    I have another question. Is there a way to pass data to a state after setting it?

  22. Pingback: Implementing An Es - Entity Systems Wiki

  23. Yowza! As I mentioned in a comment on a previous post of yours, I’m using an FSM to coordinate which systems run when. I don’t know the “state pattern”; my only exposure to state machines has been through machine-level programming courses at university. My understanding is that an FSM is (basically) two functions that run in a loop. The first function checks the state transition conditions for the current state, and the second function runs the code for the current state. The transition function depends on boolean flags which are set and unset as the states run their code. This is what I implemented as a way of coordinating the CES.

    I had just begun to think “hmm, rather than having a number of states to govern my map generation… why not give the map generator an FSM internally?” That is to say, if a “system” can have states, I could put those states in the system. The “MapGeneration” system goes through a few phases, where it is 1. making a random map, 2. analyzing the map, 3. refining the map, etc… Rather than including these states in the main coordinating FSM, I thought I might just include an FSM in that system. In a week or so, I predict I would have thought “hmm, this entity has states… wouldn’t it be nice if my entities could have FSM themselves?”

    I’m glad I came across this article sooner, as I can now prepare myself for the coming times of trouble. My intuition was that an entity’s “state” should be the collection of components it has; your article seems to argue the same. I think I would rather declare the state transitions as part of the FSM though, rather than have other systems change its state directly. Where would those transitions functions live though… hmm! Much to think on.

    Thanks!

    z.

  24. Thanks for the article. I’m new to ECS and hit a wall trying to come up with a way to handle state. Your article has given me great insight on how I can achieve it. I’m doing this in a LibGDX / Ashley environment but your concept has helped me see the light! Thanks again.

  25. A bit late to the party here, great summary, but quick question:

    – With this approach, how would one handle multiple states within a single behavior (component/system)? A practical example I’m actually stumped with now is Jump, where you have three stages of animation after the initial impulse is applied, i.e. jump, fall (once apex of the jump reached) and land (once you hit the floor). When I’m in the air I go into an “airborne” state and jump is disabled (since I don’t allow more than one jump). I could create a component for each, and perhaps two systems one handling fall/land and another handling jump, that way I can re-use the fall/land (falling off a ledge or into a hole to my death). Of course that’s a simple example, there may be better examples for which that solution is not feasible. Perhaps a better example (with the fall/land issue put aside) were if I was to apply a force over time rather than an impulse. In this case I’d need to continue applying that using the Jump system, but then I’d be unable to remove the jump component to prevent more than one jump, I’d instead have to leave the component in place and handle this with logic inside the Jump system.

    Cheers,
    Adrian

    * disclaimer: hobbyist, not a pro.

  26. @Adrian

    If you want to have multiple states inside a single component/system you will need to store a property in the component that reflects the current state and use an if/else or switch in the system to decide what to do based on that state.

    This is fine if the differences are simple and the states are few. In my experience it is always possible to set up the appropriate components and systems so that each state has different components, but sometimes it’s not worth the complexity to do so and using a single system across multiple states is the best choice.

    In your jump example you need to distinguish between the momentary act of choosing to jump and the continuous act of being in the air because you are jumping. When you are not jumping you can choose to jump. When you are already jumping you can not choose to jump.

  27. Thanks Richard, I wasn’t expecting such a quick response!

    Where possible, I’d take the multiple component approach every time, so long as I don’t need to go to an unmanageable granular level. Then perhaps I just need a Falling state / component / system that’s smart enough to know when an entity is falling by checking on the desired vs. actual velocity, that tied with a one time step impulse would allow me to disable the jump instantly and the jump system wouldn’t have to care what happens next. The Falling system should then work for non-jump use cases. I hope.

    Anyway, thanks again Richard, apologies for blabering.

    Cheers,
    Adrian

  28. Hi Richard! So I assume you only add components in state, while removal of a component is either handled by it’s own system, or by other component’s systems?

  29. Hi Mikey When entering a state the FSM removes the components that form the previous state and adds the components that form the current state, with the exception that components that exist in both states are left alone rather than removing and re-adding them.

  30. I’m struggling with implementing a FSM in my game to handle entity states.

    What I’m struggling with is how to make generic states that can be used in all different types of entities, while also allowing for flexibility when it comes to what causes changes in the states. For example, the player might have a running state that is triggered with the input for move left or right is being held down, whilst an enemy might have a running state that is triggered by its AI.

    Another example would be the falling state. For a flying enemy, the falling state is triggered when the enemy is shot down. For a ground based enemy, the falling state is triggered when the linear y velocity is negative. In both cases, I want the falling state to be the same. How can I establish a good system for transitions without a lot of if/else statements in my systems that have to check for specific entities? (e.g. if flying enemy, check for if shot down. Else if ground based enemy, check linear y velocity, etc…)

  31. A very interesting approach. But I have a doubt about state transitions.

    It’s clear that the state of an entity is determined by its components. But what about transition logic from one state to another? In your example, you hardcode transitions between systems (states), going always to Attack when you find an enemy, and back to Patrol when it’s killed. That won’t work if we have, for example, an attacker that must sleep again after the killing. I think hardcoding it’s fine for very generic transitions, like a HealthSystem that always adds a DeathThroes component when energy = 0.

    What would be a correct approach for handling non-generic transitions? For example, supose we have a VisionSystem that can tells us which entities can be seen by another one at any moment. And we want:

    * Every PatrolEnemy return to a patrol path when he can’t see any targets.
    * Every LazyEnemy return to a sleep when he can’t see any targets.

    Transitions can’t be hardcoded inside the VisionSystem. I think a posible approach would be to have a PatrolEnemySystem and LazyEnemySystem, each one handling specific state transitions the way a state machine works, like (pseudocode):

    PatrolEnemySystem {
    State1;
    State2;

    update (entity, delta) {
    //get current state based on components
    if (getCurrentState(entity) == State1) {
    changeState(State2); //add-remove components
    } else if (getCurrentState(entity) == State2) {
    changeState(State1); //add-remove components
    }
    }
    }

    I thinks it’s a valid approach, but I don’t know if it breaks the ECS paradigm. Or do you have other ideas?

Leave a Reply

Your email address will not be published. Required fields are marked *