Unity3D – Easy Finite State Machine (C#)

You are here:
Estimated reading time: 2 min

State machines are a very effective way to manage game state, either on your main game play object (Game Over, Restart, Continue etc) or on individual actors and NPCs (AI behaviours, Animations, etc). The following is a simple state machine that should work well within any Unity context.

Designed with simplicity in mind

Most state machines come from the world of C# enterprise, and are wonderfully complicated or require a lot of boilerplate code. State Machines however are an incredibly useful pattern in game development, administrative overhead should never be a burden that discourages you from writing good code.

  • Simple use of Enums as state definition.
  • Minimal initialization – one line of code.
  • Incredibly easy to add/remove states
  • Uses reflection to avoid boiler plate code – only write the methods you actually need.
  • Compatible with Coroutines.
  • Tested on iOS and Android

Usage

An example project is included (Unity 5.0) to show the State Machine in action.

To use the state machine you need a few simple steps

Include the StateMachine package
using AxlPlay; //Remember the using statement before the class declaration

public class MyManagedComponent : MonoBehaviour
{

}
Define your states using an Enum
public enum States
{
    Init, 
    Play, 
    Win, 
    Lose
}
Create a variable to store a reference to the State Machine
StateMachine<States> fsm;
Get a valid state machine for your MonoBehaviour
fsm = StateMachine<States>.Initialize(this);

This is where all of the magic in the StateMachine happens: in the background it inspects your MonoBehaviour (this) and looks for any methods described by the convention shown below.

You can call this at any time, but generally Awake() is a safe choice.

You are now ready to manage state by simply calling ChangeState()
fsm.ChangeState(States.Init);
State callbacks are defined by underscore convention ( StateName_Method )
void Init_Enter()
{
    Debug.Log("We are now ready");
}

//Coroutines are supported, simply return IEnumerator
IEnumerator Play_Enter()
{
    Debug.Log("Game Starting in 3");
    yield return new WaitForSeconds(1);

    Debug.Log("Game Starting in 2");
    yield return new WaitForSeconds(1);

    Debug.Log("Game Starting in 1");
    yield return new WaitForSeconds(1);

    Debug.Log("Start"); 
}

void Play_Update()
{
    Debug.Log("Game Playing");
}

void Play_Exit()
{
    Debug.Log("Game Over");
}

Currently supported methods are:

  • Enter
  • Exit
  • FixedUpdate
  • Update
  • LateUpdate
  • Finally
  • OnCollisionEnter
  • OnTriggerEnter

These methods can be private or public. The methods themselves are all optional, so you only need to provide the ones you actually intend on using.

Couroutines are supported on Enter and Exit, simply return IEnumerator. This can be great way to accommodate animations. Note: FixedUpdate, Update and LateUpdate calls won’t execute while an Enter or Exit routine is running.

Finally is a special method guaranteed to be called after a state has exited. This is a good place to perform any hygiene operations such as removing event listeners. Note: Finally does not support coroutines.

Transitions

There is simple support for managing asynchronous state changes with long enter or exit coroutines.

fsm.ChangeState(States.MyNextState, StateTransition.Safe);

The default is StateTransition.Safe. This will always allows the current state to finish both it’s enter and exit functions before transitioning to any new states.

fsm.ChangeState(States.MyNextState, StateTransition.Overwrite);

StateMahcine.Overwrite will cancel any current transitions, and call the next state immediately. This means any code which has yet to run in enter and exit routines will be skipped. If you need to ensure you end with a particular configuration, the finally function will always be called:

void MyCurrentState_Finally()
{
    //Reset object to desired configuration
}
 EXAMPLE:
using AxlPlay; //Remember the using statement before the class declaration

 // init FSM 
public enum States { 
       Idle, 
       Pursue, 
       ArrivedEvent 
} 
public StateMachine<States> fsm; 
void Start()
{
        //Initialize State Machine Engine		
        fsm = StateMachine<States>.Initialize(this, States.Idle);
 }
void Idle_Enter(){
    fsm.ChangeState(States.Pursue)
}
void Pursue_Enter(){

}
void Pursue_Update(){

}
Was this article helpful?
Dislike 0
Views: 85