Let’s improve the basic FSM we made in a previous tutorial and add a jump state!
This article is also available on Medium.
This tutorial is available either in video format or in text format – see below 🙂
Why use a hierarchical state machine?
This tutorial directly builds upon the previous one on how to make a basic finite state machine in Unity/C#. I will start from the code we had at the end of this previous article, so make sure to have a look at it or directly get the scripts from Part I! 🙂
Hierarchical FSMs are a particular kind of finite state machine that let us group together several states which are similar in terms of input or update logic so that you can use one input in several states without repeating your code. They use some “general” states that multiple substates inherit from to share behaviour.
In our case, we are going to define a general “grounded” state that will define some behaviour common to both the “idle” and the “moving” states. So “idle” and “moving” will be substates of this “grounded” state. This will allows us to transition from either one to the “jumping” state while maintaining a reasonable amount of code.
It’s important to note that at no point are we going to instantiate the “grounded” general state in our FSM: it is an abstract group, a yet-to-complete behaviour description. So we are still going to use the “idle” and “moving” states in the end.
The big picture
To transform our basic FSM into a hierarchical one, we can reuse inheritance and virtual methods. We’ll create an intermediate class in our chain of inheritance to represent the “grounded” state and have our “idle” and “moving” state inherit from it.
We’ll also see how to use Unity’s 2D physics and rigidbody velocities, to add a vertical motion to our player (for the jump); and we’ll talk about Unity’s layers and how they can help us optimise the physics computations.
So – are you ready? Then, let’s dive in! 🙂
Step 1: Improving our scene with object layers
Layers are defined project-wide in the project settings. Then, game objects can be assigned to a given layer using the little dropdown in the top right corner of the Inspector.
Those layers are used to define the interactions between the objects in your scene. For example, they can help you hide or show specific objects in your camera, or they can tell the lights which part of the scene they’ll impact. They can also be used as a way of optimising Unity’s physics computations – that’s what we’ll see here.
The idea is that, thanks to layers, whenever you do raycasts or check for collisions, you can focus on just a sub-hierarchy of your scene rather than checking against all the objects. This significantly reduces the number of calculations to make and makes it much quicker!
We will use this on our “Ground” object. For now, it’s just a 2D sprite with a BoxCollider2D component. We’ll assign it to its own layer, “Ground”, so that the collision checks between the player and the ground take advantage of this computation optimisation.
To create a new layer, you need to go to your project settings and navigate to the “Tags & Layers” subpanel. There, you’ll see some of the layers that Unity predefines for us. You can add our new layer, “Ground” in this list, in one of the user layers slots:
Once that’s done, don’t forget to actually assign the layer to the game object! For now, you’ve just created the layer but it’s not yet in use. To fix this, select the “Ground” object and make sure to change its layer in the Inspector:
Our scene is now ready! Let’s get back to coding 🙂
Step 2: Adding the “Grounded” state and updating our inheritance hierarchy
To begin with, we’ll need to create our new “Grounded” state. Remember that we won’t actually be instantiated this state – it will just be a “custom blueprint”, a more refined state base for our “idle” and “moving” states, so that both of them can transition to the “jumping” state using the same input (pressing the space bar).
In truth, the “Grounded” state is pretty basic compared to the other ones. It just has to check for the space bar input to switch to the “jumping” state. The only subtle thing is that, since it’s not an actual state, its constructor won’t use a hardcoded value for the state name – instead, it will pass on the one that it receives from its child constructor:
Just like in the last tutorial, I’ve added a reference to our state machine casted to its specific
Movement-SM-type. I’ve made it protected so it can be accessed in our child states (we’ll see soon that we’ll be able to remove the
_sm private variables in our
Moving state and to use it in the
jumpingState variable doesn’t exist in our
MovementSM custom state machine at the moment – we’ll add it in just a sec.
Now, we can have our
Moving classes inherit from this new
Grounded class and we don’t have to change anything else: all the logic stays the same! Here are the full updated scripts:
At that point, it’s important to note that the
base keyword in our
Moving scripts has changed meaning: it doesn’t reference the
BaseState class anymore but the
Grounded one. This is why, in the
UpdateLogic() function, the
base.UpdateLogic() line now also contains the check for the transition to the “jumping” state.
Great! Except that we haven’t coded up this “jumping” state yet… so these scripts won’t compile for now! Let’s take care of that 😉
Step 3: Implementing the “Jumping” state (with some 2D physics!)
Alright, first things first, let’s create a plain
Jumping class that inherits from the
BaseState. It won’t do anything for now, but it will allow us to set up the logic properly in our 2D player movement state machine script:
Now, in our
MovementSM class, we can do:
That’s all very well, but the “jumping” state is currently pretty useless. What we want the player to do in this state is to have the following behaviour:
- when it enters, it switches to a green colour and it gets tossed into the air
- then, we’ll need a private variable, a
_groundedboolean flag to check whether we’re still falling or we’ve touched the ground again
_groundedvariable will be updated in the
- and we will check whether it’s true in the
UpdateLogic(): as soon as it is, we’ll transition back to the “idle” state (we can’t switch back to the “grounded” state – we don’t have one! – so we’ll arbitrarily go back to our initial state)
All of this translates to the following code:
There are three important things to point out here:
- in the
Enter()entry point, we add a force to the rigidbody’s vertical velocity so it springs up
- in the
UpdatePhysics()method, we use Unity’s
IsTouchingLayers()method: this function is specifically for 2D physics and it allows us to easily check whether the given rigidbody touches any object on the given layer(s).
Also, we need to make sure that we are indeed falling down (otherwise, at the beginning of our jumps, while we’re still close to the ground, the script might think that we should be grounded immediately). This is why we check for a negative velocity on the vertical axis. Note that we’re taking into account the possible float approximation errors with the
Mathf.Epsilon trick we discussed in the previous tutorial.
- to define our “Ground” layer in C# code, we need to use bit-shifting (for more info, check out Unity’s docs)
All that’s left is to actually define this
jumpForce in the
(Like in the previous tutorial – you should just play around with different values for this variable until you find one that you like; just have it great enough to counteract the impact of gravity!)
And we’re done! If you run the game now, you’ll see we successfully integrated our new “jumping” state to the player movement state machine: whenever we press the space bar, the player turns green and it jumps up; then it slowly falls down (thanks to Unity’s built-in gravity computation) and as soon as it touches the ground, it reverts back to the “idle” state.
Notice how we can transition to the “jumping” state from either the “idle” or the “moving” state: this is thanks to our hierarchical FSM that shares the “grounded” behaviour between the two states! 🙂
Hierarchical state machines are a powerful of combining states together and merging behaviours without having to repeat your code. However, they should be used carefully to avoid entangling behaviours together too much.
Once again, remember that state machines can lead to longer code but that they are a nice way of decoupling behaviours since they separate each state in a specific file. They can be used both for user- or AI-entities and can interact with the other systems.
However, they’re usually not adapted for big complex systems: in those cases, it’s often better to switch to behaviour trees that provide more flexibility and are easier to update.
I hope you enjoyed this quick Unity tutorial and the dual video/text versions. Feel free to react in the comments and tell me if you like this new format – and of course, go ahead and share your ideas for future topics you’d like me to make Unity tutorials on! 🙂