How to Create a Simple Finite State Machine for 2D Games using Unity Scriptable Objects
What is Unity3D?
Unity3d is a game engine with several years in the market. During these years, Unity has been improving its tools for programmers, like Unity Scriptable Objects, which gives them more flexibility to build videogames using good practices and cleaner architectures.
Unity Scriptable Objects
Let's talk about one of the tools which make our life as developers easier. The tool is called “Scriptable Objects” (SO). Additionally, we will discuss how it can be applied to create a finite state machine (FSM) using the example of a game that I’m currently developing called “Building Outbreak”.
What are Scriptable Objects?
A SO is a new type of asset (like a scene or an image) which represents a data container. This container is a C# script that can be instantiated by a MonoBehavior to store the data.
What are the Advantages of using Scriptable Objects?
The Monobehaviours can be used as data containers. However, it has a big memory impact because when it is instantiated to access the data, it also gets a full copy of a game object to which the Monobehaviour was attached to. In other words, we get no needed data (like position, scale, and rotation of the object). On the other hand, when a Monobehaviour instantiates a SO, it only stores its reference not a full copy.
It also helps the game architecture by separating the data and the game logic. It allows adding modularity to the game code, avoiding dependencies. This helps avoid global managers (singletons) and makes the unit testing easier.
How do the Scriptable Objects work?
Let’s explain it but using the example of how the FSM was built for “BuildingOutbreak”, and a step-by-step guide on doing so.
The best way to understand how the SO works is through an example. In this case, I’m going to show how the SO was implemented in “Building Outbreak”. The game is a 2D adventure game where the player has to escape from a building infested by zombies.
The zombies are “Non-Player Characters” (NPC), so they are controlled by artificial intelligence. For this game, the AI technique used was Finite State Machine (FSM), where the character changes its state according to the input from the virtual world.
For instance, if the zombie is currently in an “Idle” state and it detects the player (the player enters on its vision range), then its state changes to “chase”.
Once the FSM was designed, then the SO can be created. To add a new SO, a class that extends from ScriptableObject class must be created. This new class represents a generic state, so it should be created as an abstract class as follows.
As you can see, the SO can be used as a simple data container and also as a normal class with methods to manipulate the data internally. Now, let’s create concrete classes which represent the states by extending the class “State”.
Instantiating a SO could be made through the Unity Menu (as any other asset is created) by adding the attribute “CreateAssetMenu” with the path in the Menu where the new asset would be located. When Unity compiles the script, the asset should be located in the menu as follows.
With those steps, all the states required can be found in the menu to be used in the game. Besides, the SO can be edited in the unity inspector as well.
In this stage, it is possible to inject the states to which the current one can travel (Figure 3). This helps low coupling between classes, and provides a tool for designers who can make changes from the editor avoiding source code changes.
Now we can create a mechanism to instantiate the states in a Monobehaviour. This should be a controller class that manages the state changes based on the events (rules) that the zombie gets (see the player for example).
The controller should be a Monobehaviour which will be injected in the “Enemy” class. This allows it to be used by all enemies that implement this class. The idea is that each enemy can execute its FSM according to its rules. For example, if a new zombie with more skills like run or attack by using objects is created, it can have its FSM set to run when it sees the player or uses a brick to attack instead of biting the player.
Finally, the controller has a method to execute the transition between states based on the rules. For example, if the zombie will attack, it should first check the rule which is being close enough to the player to bite him.
The transition method checks the nodes where the current one can travel (figure 3) and verifies the rules to validate if it can move to that state.
The SO is a tool that Unity provides as a data container to reduce the memory usage with unnecessary information. Also, it promotes modularity in the code, avoiding dependencies and global managers (singletons), which allows code reusability in other projects.
Scriptable Objects can be used in many ways. It can be used for inventory systems for role-playing games, Pathfinding, game loading systems, player statistics systems, etc. Therefore, you can think of building these systems to be reused in different projects.
Finally, the SO is simple to use, therefore, it does not require much learning time, in addition to the good documentation and tutorials that Unity and other contributors publish for free.
Juan Manuel Orjuela is a software engineer and a game developer enthusiast. Juan had worked on different projects as a full-stack developer mainly using .Net technology. He also is a Google Flutter mobile developer. His passion for software development and video games led him to start creating his own games as a hobby with Unity 3D. His goal is to create his own game development studio.