Finite state machines

338 views
Skip to first unread message

Richard

unread,
Nov 15, 2012, 1:48:01 PM11/15/12
to ash-fr...@googlegroups.com
I've merged the FSM branch into master. The way finite state machines work is...

A finite state machine will alter the components that are an entity has based on the current state. By altering the components we alter the systems that process the entity and thus its behaviour.

You create a state machine with

var fsm : EntityStateMachine = new EntityStateMachine( entity );

You define the states with a fluid interface like this -

fsm.createState( "guard" )
   .add( Patrol ).withInstance( new Patrol( guardPath ) );
fsm.createState( "investigate" )
   .add( Investigate );
fsm.createState( "defend" )
   .add( Defend );

You can add multiple components to a state

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() ) );

var deathView : SpaceshipDeathView = new SpaceshipDeathView();
fsm.createState( "destroyed" )
   .add( DeathThroes ).withInstance( new DeathThroes( 5 ) )
   .add( Display ).withInstance( new Display( deathView ) )
   .add( Animation ).withInstance( new Animation( deathView ) );

(that code is taken from the asteroids example project).

You change state with 

fsm.changeState( "playing" );

Finally, you will usually want to add the state machine to a component so it can be used by the systems that need to change the state.

There is a read-me in the repository - https://github.com/richardlord/Ash/tree/master/src/net/richardlord/ash/fsm and the asteroids example projects have been updated to use a state machine on the spaceship and change state to show an animation when the spaceship is destroyed.

The state machine relies on component providers, which hide behind the fluent interface. There are three providers included - 

The type provider provides a new instance of the given type whenever the state is entered
state.add( componentType : Class ).withType( instanceType : Class );
or
state.add( componentType : Class );
if componentType and instanceType are the same

The instance provider provides the given instance when the state is entered
state.add( componentType : Class ).withInstance( component : * );

The singleton provider provides the same instance whenever the state is entered, creating it when it is first required
state.add( componentType : Class ).withSingleton( instanceType : Class );

Finally, custom component providers may be used
state.add( componentType : Class ).withProvider( provider : ComponentProvider );

It is likely that more standard providers will emerge from use (e.g. a standard way to set the properties on a component), at which point they can be added to the library.

I hope all that makes sense. Have fun with it and let me know if it works for you. I have half-written a blog post about it which I may get finished next week.

Richard

Michał Wróblewski

unread,
Nov 15, 2012, 6:51:14 PM11/15/12
to ash-fr...@googlegroups.com
Very cool feature that I was missing. Thanks for the clear explanation. Today I presented Ash to our Adobe User Group, and noticed that the state machine is on master branch. Great work!

Wondering if something like "remove" component on given state would make sense. Will validate it in my next game.

Michał

Richard

unread,
Nov 16, 2012, 3:14:12 AM11/16/12
to ash-fr...@googlegroups.com
Hi Michal

I should have mentioned, any components that were associated with the previous state and are not part of the new state are removed automatically.

Richard

Neil Manuell

unread,
Nov 17, 2012, 10:50:01 AM11/17/12
to ash-fr...@googlegroups.com
Thanks Richard this fixes my problem far more elegantly than my attempt.

Neil Manuell

unread,
Nov 17, 2012, 3:53:02 PM11/17/12
to ash-fr...@googlegroups.com
is there a good reason that the fsm can't be interrogated externally for it's current state?

Richard

unread,
Nov 18, 2012, 6:15:16 AM11/18/12
to ash-fr...@googlegroups.com
Only because I didn't think it would be necessary. The state is encapsulated in the components of the entity. You should never need to do 
  if( fsm.currentState == ... )
because all activity should be based on the components and their internal state, not the state of the state machine.

There may be situations that I haven't thought of where it is useful to be able to interrogate the state of the state machine, in which case I'm happy to add that feature.

Richard

Neil Manuell

unread,
Nov 18, 2012, 6:29:02 AM11/18/12
to ash-fr...@googlegroups.com


Yes, that's what I figured. I was really wanting to test the default state, but I've done that by testing the default components.
Again i'm just having to think differently.

Cheers

--
You received this message because you are subscribed to the Google Groups "Ash Framework" group.
Visit this group at http://groups.google.com/group/ash-framework?hl=en.
 
 

Sercan Altun

unread,
Nov 28, 2012, 4:25:58 PM11/28/12
to ash-fr...@googlegroups.com
I haven't tested FSM yet skimmed the code a bit and couldn't see a cleanup code for destroyed entities. Do you have to hold your FSM instance inside entity via a component to make sure it gets garbage collected when entity is destroyed?

Richard

unread,
Nov 29, 2012, 3:14:33 AM11/29/12
to ash-fr...@googlegroups.com
You have to retain a reference to it somewhere, else you won't be able to use it later (and it will be garbage collected immediately). The recommendation is you place it in a component and systems access it from there.

Richard

unread,
Dec 5, 2012, 4:33:13 PM12/5/12
to ash-fr...@googlegroups.com
I've written a blog post about the finite state machine stuff - http://www.richardlord.net/blog/finite-state-machines-with-ash

johnjohn

unread,
May 28, 2013, 10:55:40 AM5/28/13
to ash-fr...@googlegroups.com
So this answers a question I had about caching states for later use.  It might worth it to add a param & the return value on the changeState API such that it would allow you to cache the old state.  I see this useful in cases like so:

I have a mining ship that has the following logic flow:

if (isMining)
{
    if (isCargoAtCapacity || isAsteroidDepleted)
        findNearestFactory();
   
    else
         keepMining();
}

else if (isDroppingCargo)
{
   if (isCargoDepleted)
       findNearestAsteroid();

    else
       keepDroppingCargo();
}

else
   navigateToTarget(); //this would be handled elsewhere most likely


Richard

unread,
May 28, 2013, 1:53:30 PM5/28/13
to ash-fr...@googlegroups.com
I'm not clear, from your example, what you want to cache. This thread is about retaining the reference to the EntityStateMachine, not to an individual state, and your code example doesn't look like it uses an EntityStateMachine.

Richard

johnjohn

unread,
May 28, 2013, 3:03:00 PM5/28/13
to ash-fr...@googlegroups.com
Right I havne't implemented the FSM stuff yet.  I think I solved my issues though,

Aymeric

unread,
Aug 8, 2015, 9:08:30 AM8/8/15
to Ash Framework
Hi Richard,

Thanks for your awesome stuff on Ash, I enjoy it a lot :)

Well, I'm a but surprised with the Finite (State Machine). I'm not sure to get it why I'm not able to access states or change them later (I mean adding new components to them). My main goal using an ECS is the way to add components to an entity and being able to remove them at runtime. Let's take a simple example:

I've a player (2D side scroller game), he is able to move, jump and get hurt. So my FSM have two states : "playing" and "hurt" the last one doesn't include moving etc.
Now my player pick up a weapon, I would like to just be able to add a component to my State Machine rather than creating a new state machine. Then it discovers a power and he can use a Ladder, same here I would like to just add a component rather than managing an other FSM. And also maybe he could use a ladder but couldn't use a weapon...

I know, I could put those components directly on the Entity, but I would have to manage/remove them when changing state : if my player is hurts I don't want it to be able to use a ladder or shoot.

Thanks for your explanations.

Richard

unread,
Aug 8, 2015, 1:20:22 PM8/8/15
to Ash Framework
Hi

I don't think editing states while the game is running is a good idea - it makes the game unpredictable since the nature of each state varies depending on where you are in the game. However, if you want to do it you can keep a reference to the EntityState object (returned by the AddState method) and add components to it later, although removing them is not possible. Still, the code for removing them would be easy to add, as would the code to get a state object from the engine if you want to access it that way.

For myself, I would change the rule from "player can only shoot when in the playing state" to "player can only shoot when they can move". The result is exactly the same but if you phrase it this way a new solution becomes apparent. Add the move component to the nodes for systems that manage shooting. Now those systems will only process entities that can move.

Richard

Aymeric

unread,
Aug 9, 2015, 1:03:20 PM8/9/15
to Ash Framework
Hi Richard,

Indeed this is an elegant solution and going to the "node way" is feeling so natural.

Thanks again for your quick answer!
Reply all
Reply to author
Forward
0 new messages