Alexander

Alexander

hit me up
Home

Non MonoBehaviour State Machine

Alexander - 08/06/2016

This is my state machine. There are many like it, but this one is mine.

I originally wrote this when working in XNA a couple of years ago and more recently I wanted a state machine in Unity. There are a number of solutions out there but I remembered I had my own so I adapted it for use in Unity which is what I’m sharing today.

In short this state machine requires one initial set-up where you create an instance of the state machine and a number states. From there you define the transitions between states so the state machine knows when to change state and which state to change to. I’m essentially describing state machines in general so let’s get on with the code.

public abstract class State<T>
{
    public abstract void Enter(T owner);    //Do something the instance we enter this state
    public abstract void Execute(T owner);  //Do something each update tick while in this state
    public abstract void Exit(T owner);     //Do one last thing before we exit this state

    public string Name { get; set; }

    private List<Transition<T>> transitions = new List<Transition<T>>();
    public List<Transition<T>> Transitions
    {
        get { return transitions; }
    }

    public void AddTransition(Transition<T> _transition)
    {
        transitions.Add(_transition);
    }
}

The three methods Enter, Execute and Exit need to be overridden in derived State classes which is where the functionality of the state is defined. Each state also has a list of transitions which are defined by: A) the condition which needs to be met in order to trigger the transition and B) the state which needs to be transitioned to when the condition is met (see Transition class below). Lastly the state can be given a name so it doesn’t feel unloved.

public delegate bool Condition();

public class Transition<T>
{
    public readonly State<T> NextState;
    public readonly Condition Condition;

    public Transition(State<T> _nextState, Condition _condition)
    {
        NextState = _nextState;
        Condition += _condition;
    }
}

Now we just need the actual state machine which puts it all together.

public class FSM<T>
{
    T owner;

    State<T> currentState;
    public State<T> CurrentState
    {
        get { return currentState; }
    }

    public FSM(T _owner)
    {
        owner = _owner;
        currentState = null;
    }

    public void Initialise(State<T> _state)
    {
        currentState = _state;
        currentState.Enter(owner);
    }

    public void Update()
    {
        if (currentState == null)
            return;

        foreach (Transition<T> transition in currentState.Transitions)
        {
            if (transition.Condition())
            {
                currentState.Exit(owner);
                currentState = transition.NextState;
                currentState.Enter(owner);
            }
        }
        currentState.Execute(owner);
    }
}

You will probably have noticed that each class is generic. This is done so that the states can access the instance of the class passed to the constructor (see public FSM(T _owner) on line 11). This will most likely be the class in which the state machine is created (unless you find some other funky use for it). You’ll then create an instance of a number of states, one of which can be used to initialise the state machine.

Each update tick the state machine loops through all the transitions in the current state and checks them to see if the conditions for those transitions are met, and if they are it transitions to the new state.

Below is a quick example of how to implement the state machine in a mono based class.

public class Example : MonoBehaviour
{
    FSM<Example> stateMachine;

    bool toggleme;

    void Start()
    {
        toggleme = false;

        //Create an instance of the state machine
        stateMachine = new FSM<Example>(this); 

        //Create instances of each state class
        StateOne stateOne = new StateOne();
        StateTwo stateTwo = new StateTwo();

        //Add a transition to State One which changes to State Two when toggleme is true
        stateOne.AddTransition(new Transition<Example>(stateTwo, () => (toggleme == true)));
        //and vice versa
        stateTwo.AddTransition(new Transition<Example>(stateOne, () => (toggleme == false)));

        //Set the initial state of the state machine
        stateMachine.Initialise(stateOne);
    }
    
    void Update()
    {
        stateMachine.Update();

        //Press the Space key to toggle the bool toggleme which will
        //trigger the transitions between State One and State Two
        if (Input.GetKeyDown(KeyCode.Space))
            toggleme = !toggleme;
    }
}

In this example whenever then space key is pressed the state machine will transition between StateOne and StateTwo which will log a message to the console saying which state we have just transitioned to (these states are identical except for their name, see the code below).

Once you have created an instance of a state you can start adding transitions to it. On line 19 (highlighted) you can see a transition is added to the state StateOne by creating a new transition (passing the same type as when the state machine is initialised) and passing the state we want to transition to and the condition that needs to be met to trigger the transition. The condition is most easily created using a lambda expression and passing your condition such as toggleme == true in the example above.

class StateOne : State<Example>
{
    public StateOne()
    {
        Name = "State One";
    }

    public override void Enter(Example _owner)
    {
        //Do something the instance we enter this state
        Debug.Log("Entered " + Name);
    }

    public override void Execute(Example _owner)
    {
        //Do something each update tick while in this state
    }

    public override void Exit(Example _owner)
    {
        //Do one last thing before we exit this state
    }
}

As mentioned earlier, the methods of each state provide access to the mono based class which was passed when creating the state machine. This allows us to call methods on that class and with it you can do mono based stuff 😉

This is one of the simplest versions of this state machine and it can easily be expanded with additional functionality that is commonly seen in some other state machines such as the ability to see the previous state.

Drop a comment below if you have any questions or perhaps you just want to say something.

Leave a Comment


Leave a Reply

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


Alexander Dudok de Wit

http://onf.re/