This tutorial will implement a generic Finite State Machine using C#. We will then illustrate the concept by applying the implemented Finite State Machine using Unity in a few scenarios.
Contact me:
You can find the source code for this project in GitHub https://github.com/shamim-akhtar/fsm-generic.
This tutorial requires a basic understanding of object-oriented programming and inheritance.
This tutorial aims to make our Finite State Machine generic by using a generic identifier for the state type.
There are several ways you can implement a finite state machine using C#. The easiest and fastest way probably is to use the enumerator type and the switch-case statement. However, in this tutorial, we are not going to do that. Instead, we will use a slightly sophisticated, more robust, generic class-based approach that will be reusable across multiple projects.
At the same time, we also want to extend the functionality of using delegates in the same framework.
But first, let’s recap on what is a Finite State Machine.
Definition
Finite State Machine (or FSM in short) is a computational pattern that defines and models state behaviour.
At any given time, a Finite State Machine can exist in only one State out of a set of a possible number of states. This State can change to another in response to some inputs (sometimes called events).
The process of switching from one State to another is called a transition.
The Classes
For organization purposes, we will put the generic reusable codes in the Patterns namespace. You can put them in any other namespace as well.
The State Class
The State class is the base class for a Finite State Machine state. This is a data structure (class) that encapsulates the state-related functionalities. We will implement this class in the section after FiniteStateMachine class implementation. For now, we define the basic structure.
namespace Patterns
{
public class State<T>
{
// The name for the state.
public string Name { get; set; }
// The ID of the state.
public T ID { get; private set; }
public State(T id)
{
ID = id;
}
public State(T id, string name) : this(id)
{
Name = name;
}
}
}
Code language: C# (cs)
We have added two constructors. One takes in the type T (unique ID) as a parameter, and the other takes in the type T (unique ID of the State) and a string value (name of the State) as parameters.
The Finite State Machine
As defined above, a Finite State Machine.
- consists of a set of states,
- and at any given time, a Finite State Machine can exist in only one State out of these possible states.
Thus, we will need a variable to store the collection of states. This collection will represent a set of states. And then, we will need a variable to keep the current state of the Finite State Machine.
// A Finite State Machine
// - consists of a set of states,
// - and at any given time, an FSM can exist in only one
// State out of these possible set of states.
// A dictionary to represent the set of states.
protected Dictionary<T, State<T>> mStates;
// The current state.
protected State<T> mCurrentState;
Code language: C# (cs)
To construct the FiniteStateMachine class, we probably won’t need any arguments. At least, not for now. We will proceed with a default constructor.
public FiniteStateMachine()
{
mStates = new Dictionary<T, State<T>>();
}
Code language: C# (cs)
Add State to the Finite State Machine
In the previous section, we created the variable that stores the set of states. Now we will create a method that will fill that set by adding state.
public void Add(State<T> state)
{
mStates.Add(state.ID, state);
}
public void Add(T stateID, State<T> state)
{
mStates.Add(stateID, state);
}
Code language: C# (cs)
Get State from the Finite State Machine
A method that returns a State based on the key.
public State<T> GetState(T stateID)
{
if (mStates.ContainsKey(stateID))
return mStates[stateID];
return null;
}
Code language: C# (cs)
Note that the method will return null if a State of the same key has not been added previously to the FSM. This method is a convenient function.
Set the current State to the Finite State Machine
Now perhaps the most critical function of the Finite State Machine, SetCurrentState. This method will set the current state of the Finite State Machine.
What happens when we set a state to the current State? There are two possible code paths to it. The first code path is when the previous-current State is valid, and the second code path is when the previous-current state is invalid (or null).
public void SetCurrentState(State<T> state)
{
if (mCurrentState != null)
{
}
mCurrentState = state;
}
Code language: C# (cs)
The above code implements the SetCurrentState method. If the previous-current State of Finite State Mchine is invalid, then the implementation directly sets the State to the mCurrentState. However, if the previous-current State was not null, then what happens?
Can we still overwrite the previous-current state with the new current state?
The answer is probably not. We might want to implement specific functions whenever a state exits and a new state enters. How do we then implement this into our current code?
Enter and Exit
The answer is simple. Create two virtual methods in the State class called Enter and Exit. The base State class implements nothing for both the Enter and Exit methods and instead relies on the application to create concrete implementations of the base State class. Then call these two methods whenever there is a change in the State.
public void SetCurrentState(State<T> state)
{
if (mCurrentState == state)
{
return;
}
if (mCurrentState != null)
{
mCurrentState.Exit();
}
mCurrentState = state;
if (mCurrentState != null)
{
mCurrentState.Enter();
}
}
Code language: C# (cs)
We will finally put the Unity context to the FSM and the State class by adding two methods called Update and FixedUpdate. These two methods we will call from Unity Monobehavior for every Update and FixedUpdate.
public void Update()
{
if (mCurrentState != null)
{
mCurrentState.Update();
}
}
public void FixedUpdate()
{
if (mCurrentState != null)
{
mCurrentState.FixedUpdate();
}
}
Code language: C# (cs)
This completes our implementation of a Finite State Machine in C#. We will now continue by completing the State class.
Completing the State Class
We have four key function calls. These are Enter, Exit, Update and FixedUpdate. We will keep these methods virtual.
virtual public void Enter()
{
}
virtual public void Exit()
{
}
virtual public void Update()
{
}
virtual public void FixedUpdate()
{
}
Code language: C# (cs)
For convenience, we will add delegates to handle the key function calls such as Enter, Exit, Update and FixedUpdate.
public delegate void DelegateNoArg();
public DelegateNoArg OnEnter;
public DelegateNoArg OnExit;
public DelegateNoArg OnUpdate;
public DelegateNoArg OnFixedUpdate;
Code language: C# (cs)
Now we amend the four key functions as below by calling the respective delegate is valid.
virtual public void Enter()
{
OnEnter?.Invoke();
}
virtual public void Exit()
{
OnExit?.Invoke();
}
virtual public void Update()
{
OnUpdate?.Invoke();
}
virtual public void FixedUpdate()
{
OnFixedUpdate?.Invoke();
}
Code language: C# (cs)
Finally, we add two more constructors to construct a State class with the given delegates as parameters.
public State(T id,
DelegateNoArg onEnter,
DelegateNoArg onExit = null,
DelegateNoArg onUpdate = null,
DelegateNoArg onFixedUpdate = null) : this(id)
{
OnEnter = onEnter;
OnExit = onExit;
OnUpdate = onUpdate;
OnFixedUpdate = onFixedUpdate;
}
public State(T id,
string name,
DelegateNoArg onEnter,
DelegateNoArg onExit = null,
DelegateNoArg onUpdate = null,
DelegateNoArg onFixedUpdate = null) : this(id, name)
{
OnEnter = onEnter;
OnExit = onExit;
OnUpdate = onUpdate;
OnFixedUpdate = onFixedUpdate;
}
Code language: C# (cs)
We have implemented a generic reusable Finite State Machine that we can reuse/override and apply based on what is required by our application domain down the stream. We can also use delegates to provide the necessary behaviour of a state without deriving a new State class.
You can find the source code for this project in GitHub https://github.com/shamim-akhtar/fsm-generic.
I have also added several examples for the different use cases of the Finite State Machine. Fork the repository and try out the different examples.
This tutorial is an extension of my past tutorial on Implementing a Finite State Machine Using C# in Unity.
To read more about Finite State Machine, please refer to my other series of tutorials on Finite State Machine.
- Part 1: Implementing a Finite State Machine Using C# in Unity
- Part 2: Implement a Splash Screen Using a Finite State Machine in Unity
- Part 3: Player Controls With Finite State Machine Using C# in Unity
- Part 4: Finite State Machine Using C# Delegates in Unity
- Part 5: Enemy Behaviour With Finite State Machine Using C# Delegates in Unity
A committed and optimistic professional who brings passion and enthusiasm to help motivate, guide and mentor young students into their transition to the Industry and reshape their careers for a fulfilling future. The past is something that you cannot undo. The future is something that you can build.
I enjoy coding, developing games and writing tutorials. Visit my GitHub to see the projects I am working on right now.
Educator | Developer | Mentor
I have followed your blogs on the FSM and I thank you very much for the excellent explanations and examples made available for free. You are a true Angel. Thanks
Thank you for your compliment. I am happy that you are following my tutorials.