On my free time I love to create game prototypes using C# in Unity. One of these prototypes is a turn-based game where I need to have multiple game states and a fine control over them.
I started working on a typical finite state machine (FSM) for controlling the game scenario and player input in different game modes, such as battle
and exploration
.
First the state machine looked something like this:
StateMachine |
---|
StateExplorationUnitSelected |
StateExplorationUnitMoving |
StateBattleUnitSelected |
StateBattleUnitMoving |
StateBattleSelectingSkill |
StateBattleCastingSkill |
Immediately I started to stress about how bloated the single state machine would become as the codebase grows in size. This is when I divided it in two state machines:
ExplorationStateMachine | BattleStateMachine |
---|---|
StateExplorationUnitSelected | StateBattleUnitSelected |
StateExplorationUnitMoving | StateBattleUnitMoving |
StateBattleSelectingSkill | |
StateBattleCastingSkill |
After dividing the state machine in two, I wanted to have the ability to bundle related state machines together and allow their states to change the active state machine based on what happens in the game world. For example if player encounters an enemy in StateExplorationMoving
- that state can call ChangeStateMachine(BattleStateMachine);
I created a parent state machine class that can hold a collection of child state machines:
PlayerStateMachine |
---|
ExplorationStateMachine |
BattleStateMachine |
In the above table PlayerStateMachine
is a parent state machine for controlling child state machines. ExplorationStateMachine
and BattleStateMachine
can be considered as the states of the PlayerStateMachine
.
After having this structure in place, I created a StateMachineController class that holds a list of parent state machines and runs their update methods.
I extracted the code from my prototype and started editing it to be more generic. You are free to use it for your own Unity projects if you want - and feel free to create a pull request if you have ideas to make it better. The project can be found from GitHub.
Top comments (2)
Nice, I do a similar thing. Usually I put my states on a stack though, guess it depends on your use case. Any particular reason behind making those methods internal?
No special reason really. I made them internal instead of public out of force of habit of following coding conventions at my day job.