The most common answer I found to this question is ‘organization.’ I know that a state machine is a tool designed to make things more organized and flexible. However, because I don’t fully understand it yet, state machines just make things more complicated for me. I’m not a beginner—I’ve been using the engine for a few years—but since discovering FSMs, I feel like one. I have a lot of questions on this topic.
Let’s say I’m working on a platformer game. I add the player, give them a sprite, and when it comes to programming, I add a state machine with child nodes. Now, is this machine just supposed to tell me which state the player is currently in, or is it supposed to handle the transitions between states and apply the current state’s logic to the player? If it’s just a way to organize code, what is the purpose of these components? I understand the programming side from tutorials, but what I don’t get is why I should even do this. And if I do add a state machine, do I still need composition?
Let’s return to the platformer example. The player needs to run and jump. The usual way I handle this is through composition: I simply add a movement component, a jump component, and an input component, and call them in the main player script. Thus, when the player wants to move, they move; when they want to jump, they jump—and they can do both at the same time. If I don’t want the player to jump mid-air, I simply add an ‘if’ statement to check if the player is on the floor. In this scenario, why should I add a state machine, and if I do, how do I keep it synced with my components?
The answer is you don’t need it whatsoever. It’s completely optional. If the system you’re currently having is working fine, there’s no need to add more abstractions just because someone somewhere said that’s better “organization”.
Besides, there are many ways to implement a state machine other than keeping code for each state in a child node. That’s often falsely presented in Godot context as the way it has to be done. You may even have one implemented right now without even realizing it.
The defining characteristic of a state machine is mutual exclusivity of states, i.e. only a single discrete state can be active at a time. It’s nothing more than a multi-switch, like a knob on your owen. If a pattern like that appears anywhere in your system - then you’re running a state machine.
It’d be highly depend on the game system you’re trying to implement. That’s why it’s very important to, first and foremost, analyze and understand the problem domain instead of blindly following recipes.
The first question to answer when thinking about using a state machine is: is my system actually a state machine, i.e. a collection of fully mutually exclusive states?
As @normalized said, you don’t need one. Everyone who uses Godot is technically using one, as that is what a game loop is. It’s a state machine that just rolls through states 60 times a second.
We talk about state machines on this forum a lot. In this particular post I go into great detail about some of my own uses of a node-based pull state machine. I think it will answer many of your questions. The code is in my State Machine Plugin if you want to study the code, or try out my state machine.
But also, here’s some direct answers to your questions:
That depends on the kind of state machine you make. A push state machine is one where the machine decides what state should be active and pushes the states to enter. A pull state machine has the logic of when a state is entered inside each state, and pulls the state machine to enter its state. Either way, the state machine is supposed to handle transitioning between states. The states are responsible for the logic of what happens in that state.
Encapsulation. To reduce the amount of code in a single file, and put related code in smaller pieces. As in you example, you take all the Jump code inside the Player and move it to a Jump State. If you decided to add double-jump functionality, you can then either add it to the Jump State or make a new Double Jump State. Either way, as that functionality gets more complex, the code in your Player doesn’t have to contain it all.
This question is unclear. An Enum-based state machine doesn’t usually use composition, and usually sits inside the Player script. A Resource-based or Node-based state machine typically has a Resource or Node for each State and so uses composition.
This is an example of a complex state machine for a player.
Note I have two state machines. One for movement, and one for actions. But for your example, I just have movement logic in the Jump and Fall states and call it air control. One of the other benefits of a node-based state machine is it is easy to see all the states in the editor. Another is you can tweak each state with @exported variables.
Personally, I like node-based pull state machines. I find them easy to use once they are developed, and I make a lot of games, so it pays off for me to do the work once, re-use it, and improve it incrementally over time. For example, I use it for what state the game itself is in as well in my Game Template Plugin. But, I don’t always use them.
Thank you so much for giving detailed information about my questions. Also thanks again for sharing your workflow and code. I’m continuing to improve at Godot and it’s really good to get help about it.
It’s easy to get wrapped up in this technical jargon and hype, just to realise that it isn’t much more than a way to structure your code.
Don’t get me wrong, state machines are great, and I wrote one myself to use for npc/ai behaviours and it helps me a lot to easily develop new behaviours for my npcs.
But long story short, all a state machine does is select a specific piece of code to run based on if some conditions are met.
In a sense, the most basic state machine you could implement is a simple if-statement. And you could keep adding “states” to it by adding more else-if clauses.
Then as you make more advanced state machines a simple if-statement won’t suffice. Maybe you want states to be able to switch inbetween themselves more dynamically, and then an if-statement won’t be able to handle that, and you will likely need some abstracted code for transitions between states.
You should likely be using state machines when whatever you are doing can be defined into clear states and should be independent from each other. In my case, my npc behaviours fit these criteria. My npcs can for example: Chase the player, perform an attack, jump, dodge etc. And since they are all independent states and code snippets, it allows me to easily change the jump behaviour without affecting any other state implementation.
If whatever you are doing doesn’t require this sort of flexibility with adding/modifying independent states, or you cannot neatly define it into separate states, then you should probably not use state machine solutions.