Until digging into more detailed projects, I was happy sticking with basic enum definitions within the class for managing the Finite State operations. However, once the project gets larger or/and the number of possible states increases significantly, so does the complexity of the code. Well, here comes the State Pattern for help. State Pattern is a design pattern, which aims to reduce the complexity of the code by isolating the state related behaviours.
Here’s the definition of it from the book Game Programming Patterns:
Allow an object to alter its behaviour when its internal state changes. The object will appear to change its class.
The idea is that we simply create separate classes for each of the possible states by using an abstract class. Once the object needs to change its state, we simply tell the computer to use the class that is associated with that state.
In order to implement it, I have created a sample project with zombies. Briefly, player has to hit the zombies with a gun or a bat, and the animator states will be controlled by using State Pattern.
If you are struggling with understanding and implementing the pattern, I would suggest you to take a look at Mina Pecheux’s Medium/Youtube profiles.
To act as a basis for each of the states, a BaseState class is created (this can be also defined as an abstract class — or you can decide to move forward with an interface). In my example, I have moved on with defining a typical class as an abstract rather than an interface. For an easy implementation, I have only controlled the animation state of the player.
Despite the fact that I only needed one state machine in my project, I have included the state machine as a parameter: By creating multiple state machines, we can easily gather different behaviours for different objects using the same state classes.
A total of 4 methods are defined as Enter, Physics Update, Normal Update and Exit. The enter and exit methods will be called only one time on entry and exit of the states.
As the BaseState class is not inheriting from MonoBehaviour, we need another class that will be responsible for managing the state related operations. So, I have declared a PlayerSM class and added as a component to my Player.
Briefly, we tell the state machine to start with the idle state once the game starts and then change the state according to the conditions within the running states.
Now, all we need is to define the states. In the sample project, I have 6 states, as can be seen below:
For each of these states, a new class is derived, which are inheriting from the BaseState class. Player starts with Idle, and changes its state according to the input. Here’s the Idle State as a sample:
Since I have more than one options to switch to from the idle state, the exit is left empty. The necessary operations are handled within the entry states of the respective states.
Lastly, in order to control these states, we need to create instances of them within the PlayerSM (State manager) class:
And thats actually all. The State Pattern is just a great example of the benefits of using abstract classes or interfaces. Rather than having numerous if-else or switch statements within a class, the behaviour is successfully isolated within the State Manager: easy to understand — easy to manage.