A Minimal FSM for Unity
Why should you care about FSMs?
Finite-state machines (FSMs) are an effective way to structure your code. They’re popular in the world of game development. We often use them when developing character controllers, among other things. Whether they’re player-controller or AI-controlled, game characters can usually be in one of a handful of states where each state determines what actions they can take. For example, a player may need to be standing on the ground to jump. Hence, if a player who’s crawling or in the air receives the input to jump, they won’t react to it.
Typically you could handle this with nothing but a simple if-statement, but as the number of possible states increases, you’ll end up with a mess on your hands. That’s where FSMs become valuable. FSMs add some structure to your code that makes it simple to manage and maintain by breaking it down into clearly defined states.
What is an FSM?
Hopefully I convinced you that FSMs are valuable, but you may be wondering what an FSM is at this point. If you want to skip to the solution, jump to the bottom of this article or my Github repository. I’ll avoid the academic explanation; you could look that up on your own. For our purposes, an FSM is an object that behaves differently depending on its current state. Besides that, it also has a way of transitioning into other states. Their strength lies in their ability to control how they behave, what they respond to, and what other states they can transition to from a given state. With such a broad definition, there are countless ways you could code up an FSM.
Here’s my single-file FSM. I’ve written dozens of FSMs, and with every new project comes a new iteration. That’s why I’m sharing this flexible, minimal code-based FSM. For me, it has remarkable adaptability with the least code.
All it takes is these 31 lines of code. Some FSMs rely on a class for the machine, a class for each state and a class for transitions. In my case, I use a single class for the FSM and delegates (basically C# function pointers) to represent each state. It’s helpful to know whether you’re entering or exiting a state so you can perform any initialization or cleanup. Class-based states usually separate these into different functions. However, in my implementation, I pass the step (Enter, Update, Exit) into the delegate to get away with a single function per state. When transitioning, I also pass the previous or next state as an argument to define specific transitions between two states.
If you use this system as-is, it’s an excellent code-based FSM to embed into any MonoBehaviour. For me, this is the most common way to use it. It can access all the data in its MonoBehaviour; it can be paused or time-sliced by selectively choosing to call OnUpdate(), plus, you can even embed FSMs inside of other FSMs for complex behaviours.
Unfortunately, there’s rarely a one-size-fits-all solution to any problem. Since this type of FSM is entirely code-based, non-programmers aren’t able to play with it. If interested in seeing a ScriptableObject-based approach, I recommend checking out this pluggable AI tutorial from Unity. This approach is a little more designer-friendly in that states are created (by a programmer); a designer could mix and match them however they see fit. And finally, Unity Visual Scripting allows you to create State Machines as well in a node-based way.