Stack-based Hierarchical FSM Plugin for UE4.22+
A Finite State Machine allows you to create clean, maintainable 'non-spaghettified' code. With a stack-based hierarchical implementation, you are able to stack multiple states and remember what state you were in while having greater control in each state.
Looking for the documentation? Click here
Available on the Unreal Marketplace
How to get started
Once you've installed the Stack-based FSM plugin into the engine, and you've enabled it, we can begin using this in your project.
Let's start by creating a new character blueprint or you can use your existing one.
Right-click on an empty space in the content browser, select Blueprint Class, and select 'Character'. I'm going to name it BP_Player
Next, open the character blueprint and add an FSM Component. Like this:
If this is your first time setting up a character blueprint, here is some movement input code:
And make sure that the Player 0 will possess this character when we press play.
Finally, drag your new blueprint character into the world. When you press play you should be able to move and look around!
Now we can go on to create our first state blueprint asset!
Right-click on an empty space in the content browser, select Blueprint Class, expand the 'All Classes' list and search for StateBase.
Click on StateBase, click select and give your blueprint a name. I'm going to name it BP_PlayerState_Idle
Open BP_PlayerState_Idle and let's begin implementing this state!
But first, you should give this state a meaningful name. For me, it's just Idle
Next, let's override the Init event, which of the 5 events that are available to us.
Let's retrieve our player character in Init (It's the same as an actor's 'BeginPlay' event) and store it for later use.
Next, we'll add some input handling. Let's make our character jump when the space bar is pressed. The great thing about this is that the character will only jump when it's in the Idle state!
Click compile and save. And open your character blueprint, because we need to add the new state that we created.
Click on the FSM Component, and in the details panel, under FSM. Add a new state to the States list and set the Initial State Name to whatever you've named your state. In my case, it's Idle. If you don't give it the correct state name (capitalization matters), the FSM will throw an error.
After that, we'll need to call Start to start our state machine
While you're there, enable debugging so we can see what's going on. Like this:
You can also choose where you want the log messages to output to. For now, I'm going to leave it as it is.
Click compile and save. Go back to the main editor and press play. You should see that your new state was added and that the FSM has successfully started. And you should also try to jump since that's implemented now.
But there is a slight problem here, "What if I want to walk and jump or run and jump, or slide and jump?". Now we have to create a slew of states, which adds a lot of unnecessary fluff. That is a valid concern. That's where Hierarchical states come into play. Shall we begin?
We will create 2 new state blueprint objects. One called Locomotion and another called Walk. Locomotion will be the base state for all states that are 'locomotion-based'. And the Idle and Walk states will inherit from the Locomotion base state. Note: Don't forget to give these 2 new states a meaningful name, like what we did when we created our first state. This will be important later down the road.
Open the new locomotion state blueprint, select 'Class Settings' and drop down the advanced section under 'Class Options' and enable Generate Abstract Class. That way we don't accidentally add it to the state's list. And it won't be useful as well since we'll be extending this base class through our two states Idle and Walk.
Now, we need to reparent the Idle and Walk states to inherit from our Locomotion blueprint class.
To reparent a blueprint, go to File>Reparent Blueprint. And search for 'locomotion'.
And do the same for the Walk state
Now that they've been reparented, we can move the blueprint code that was in the Idle state to the new locomotion state blueprint. Like this:
Idle should be empty now.
Let's implement the Idle state, this blueprint code will detect if the player has started moving and if so, push the Walk state (then this will now be the active state)
Note: You must always call the parent update function if you're inheriting from another state blueprint and if it has some logic in the base event, or else anything in the Locomotion's Update event will not get called! To get the parent event node, right-click on the 'Event Update' node and select Add call to parent function. This will give you the orange node.
We'll do the same for the Walk state but now use the '<=' node to determine if the player has stopped moving. If so, pop the active state, which is the Walk state obviously.
That is how you set the active states in the FSM, it's called the pushdown-automaton. Basically a push and pop system. And it's really that simple.
Compile and save all your changes.
Finally, add your 2 states to the state's list. Like this:
Compile and save. Go back to the main editor and press play. You should now be able to jump while idling and jump while walking. No need to copy n' paste the jump code for each state you make. Do it once and forget about it! This is the result:
It's all up to you now from here on out. Finite state machines offers us a great way to create gameplay systems and simple AI. They help clean up code, organize it and make it way more maintainable for large projects.
A quick shortcut to create states that inherit from our Locomotion state blueprint would be to right-click on the BP_PlayerState_Locomotion asset and click Create Child Blueprint Class, then name your new locomotion-based state and add it to the list of states in the FSM component.
You can stop here since we have finished the blueprint tutorial. However, if you'd like to translate this over to C++ and extend it through blueprints then continue on!
How to use this plugin in C++
Before we can use this plugin in C++, we first need to modify your [PROJECTNAME].Build.cs file to include the StackBasedFSM module. To do that, open up your Visual Studio solution, go to Source>[PROJECTNAME] and open [PROJECTNAME].Build.cs. You will see PrivateDependencyModuleNames, it's empty. If you don't have this, just add it in under PublicDependencyModuleNames. Add "StackBasedFSM" inside the new string array, just like the image below.
Once that is done, save the file, close Visual Studio and close your Unreal project. Go to your Unreal project directory, delete .vs, Binaries, Intermediate folder. Right-click on [PROJECTNAME].uproject, click Generate Visual Studio project files. Once that's done, double click on your .uproject file to open up your project, it will ask you to rebuild the binaries, click Yes. It will take about a minute or less, depending on your computer and how many source files you have. Now we can use this anywhere in your game module!
Lets's test this out by doing the same thing we did in blueprints, but in C++ code, just so you are familiar with how this system works.
Right-click anywhere on the content browser and select New C++ Class, then select Character and give it a name. I'm going to call it GenericCharacter. Click Create Class and wait for unreal to compile the new class.
Once that's is done compiling, your code editor will open with the 2 new files, in my case Visual Studio.
Now let's create the FSM Component and make it a default component for our GenericCharacter.
.h File
Over in the constructor, that's where you'll create the component. Like this:
.cpp File
And in BeginPlay, that's where you'd add your states, but we don't have any states that we can choose from, so let's create one.
Go back to the main editor, right-click anywhere on the content browser and select New C++ Class, then select Show All Classes and search for 'StateBase'. Select that and click Next. Give your class a name, I'm going to call it CombatState. Click Create Class and wait for unreal to compile the new class.
Next, you'd want to override the 5 events and give your state a meaningful name, like this:
.h File
.cpp File
Now we can go back to our GenericCharacter class and add our states in BeginPlay. Also, this is where you'd call the Start function to start the FSM
Note: If you are going to derive this GenericCharacter in blueprints, then you would need to call Super::BeginPlay at the end of the BeginPlay function so that this BeginPlay runs first, then the blueprint implementation!
Save the file, go back to unreal and hit Compile (next to the play button). If the compile has failed for some reason, close the editor and open it back up again. Once that's done, open your character blueprint and delete your FSM component and the begin play code (if you were following from the blueprint tutorial above)
Next, reparent the character blueprint by going to File>Reparent Blueprint and search for GenericCharacter, select that and it should reparent successfully.
And you would see that it has added the FSM component we created from C++
You would need to repopulate the States list and turn on debugging, like so:
Then go back to the main editor, and press play, you should see that our CombatState was added!
One last thing, let's push this combat state when we click the left mouse button and when we are in the Walk state.
To do that, open the walk state blueprint and add this code on the Handle Input event:
Compile and save, go back to the main editor and press play. Now, whenever you press the left mouse button while walking you can enter the combat state, notice how you can't enter the combat state when you're in the Idle state, pretty neat huh? That's why FSMs have the benefit of organizing code and keeping your code clean, because all the input handling and update events are in their own bubble, not disturbing other states.
For example, whenever we enter the combat state we can play an animation or a sound or whatever you want, you just have to override the 'Enter' event and run your code in there!
And that is all there is to it! I hope you will find this plugin useful to your workflow and your project. Enjoy!
I need help!
If you are still having issues getting this plugin to work, feel free to join the Discord Support Server and report what the issue is in the #stack-based-fsm channel.