Design patterns are reusable solutions to common problems found in software projects. Using good design patterns in Unity can help keep your project clean, maintainable, and easy to extend. It also makes them easier to build.. Today, I’ll cover the State pattern and the basics of building a State Machine in Unity3D.
The state pattern is a behavioral software design pattern that implements a state machine in an object-oriented way. … This pattern is used in computer programming to encapsulate varying behavior for the same object based on its internal state.
An implementation I don’t like..
There are a few ways you can implement a state pattern in Unity. One of the common methods I see is done by using the switch statement. It generally looks something like this.
While this technically works, there are some serious issues that will arise over time.
First, all of our logic is now inside this single class. Attacking, defending, returning home, and any other state we want to create all need to be added here. This will lead to a big bloated master class for our AI. It will also make our code more likely to break since we’re editing the same class for every type of logic. Before long, this class could easily turn into a 1000+ line disaster.
There also isn’t a great way to deal with events that happen on a state enter and exit. Quite often, we want a character or object to do something when it enters a state, repeat an action while it’s in that state, and do something totally different when it leaves the state.
With this setup, we’d have to add more switches for entering and exiting state, further bloating the class and making everything harder to understand.
A better Implementation
A better approach is to have each state be it’s own class, generally with a base ‘state’ class that they inherit from.
This way, the character or object only needs to have a reference to it’s current state and a call to make that state update. When we want to add a new state, we just create a new class.
Using this method, our character class looks a bit different:
You may notice that the classes are about the same size.. but that’s only because the first version doesn’t actually do anything (mainly to keep it digestible). If that first class were doing everything the system I’m going to show you did, it’d be huge.
How does it work?
If you look at the Update method, you’ll see that it simply calls the Tick() method on the current state. With that call, the state controls what the character will do each frame.
We also have a SetState method that allows us to pass in a state we want the character to enter. By calling SetState and passing in different states, we can completely change the behavior of this character, without changing anything in the character class itself.
Depending on your setup, it may be advisable to keep states around and switch between them to avoid garbage collection. To keep this easy to understand, I’ve skipped that, but it’s worth keeping in mind if your state changes are fast and you’re on a platform where it matters.
What’s the State class?
The state class is an abstract base class we’ll use for all states we create. It defines a very basic contract for what a state must implement and other things it can optionally implement.
The character reference is also added to this state base class, because all of our states in this example will interface with a character in some way. We take the character in the constructor of our state class and keep it in a protected variable so it’s available to our state classes.
We also have the abstract Tick() method. Making it abstract means that our state classes are forced to implement it.
In contrast, we have two virtual methods for OnStateEnter() & OnStateExit() that are optional. Because they’re virtual, they have a default implementation that does nothing, and aren’t required to be implemented.
That’s the entirety of the state class.. pretty simple right?
Creating a State – Return Home
Now that we have the basics, let’s create an implementation of a state that we can hook up to our character.
The return home state simply makes the character return to it’s home location. Since we’re not actually defining the destination for home, it’s the default Vector3.zero.
The Tick method tells the character to MoveToward the destination (0, 0, 0).
The MoveToward method is on a version of the character at the end of this post. In a real project, this movement would probably be controlled by another component like a ‘CharacterMovement’ class or something else.
Then it checks if the character made it home, and if so it tells the character to switch to a ‘wander’ state. Notice that we pass in the character when creating the new state. This is because our state is designed to require a character.
We also change the color of the character when the return home state is entered via OnStateEnter. So as a character is going home, it’s blue.
Creating another State – Wander
For a state machine to make sense, we need more than one state. In-fact, our return home state actually wants to switch the character to a wander state once it’s done, so we need to create that wander state.
The wander state will pick a random location and move the character toward that position. When it reaches that position, it’ll pick another spot to wander to.
It will continue to do this until it’s wandered for 5 seconds (based on our wanderTime variable). At that point, it will tell the character to go back to a ReturnHome state (line 43).
The wander state also changes the color of our character to green via the OnStateEnter method.
Let’s see what it looks like
Here, I’ve created a scene with a dozen of the ‘characters’ represented as cubes.
You’ll see them wandering around while they’re green, then going to home again when they’re blue, and repeating the process.
The Full Character
I mentioned above that the full character class is here with the MoveToward method. Again, in a real project, I’d recommend keeping your basic movement seperated into another component, but to keep this simple, here it is in the Character class.
The state pattern is amazingly powerful. You can build AI systems, menus, and more with ease once you get comfortable with this pattern.
This state machine is also very basic and can be extended to do things like caching states (to avoid new memory allocations) or customized more to your needs.
Another great way to build state machines is via scriptableobjects. A state machine setup using scriptableobjects can be easily configured in the editor/inspector by designers without ever touching code, but that’s a bit more complicated subject that I may dive into later.
Next time you need to build a little game logic, try setting up a state machine of your own (or copy this one as a starting point), I’m sure it’ll help and you’ll enjoy the new pattern.