How to Make a Simple State Machine in Godot 3.1

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

Great to see a little more advanced state machine so well explained!

👍︎︎ 8 👤︎︎ u/golddotasksquestions 📅︎︎ May 03 2019 đź—«︎ replies

Thank you for this very good tutorial.

I am using a similar approach for my states but I have separate classes for the actual states to avoid the if logic within the StateMachine. Each State class handles the enter, update and exit logic for the state it represents and the StateMachine handles only the transitions between the states.

You could also think about the StateMachine as a stack of states, where only the state on top of your stack is processed. Entering a new state would just add the new state to the top of stack. Exiting a state would pop the state from the stack and automatically fall back into the previous state. For instance if your default state is Idle you could always have it at the bottom of your stack. If the player presses the jump button you would add the Jump state which would exit if you touch the ground again and you would be back in your Idle state.

This stack like StateMachine is especially useful for game states. Let's say you press ESC while your are playing, a Pause state would be added to your stack which would pause the game and show an Options menu. Clicking on the continue button would remove the Pause state and you would be back in your game play.

I learned that some years ago when I was working with the Ogre3D engine and I would like to share the link, even it is C++ code. But it was very helpful for me to understand how that could work:

http://gamedevgeek.com/tutorials/managing-game-states-in-c/

http://wiki.ogre3d.org/Managing+Game+States+with+OGRE

Anyway, I like your approach, it is simple and easy to understand. Thank for sharing.

👍︎︎ 4 👤︎︎ u/Gentle22 📅︎︎ May 04 2019 đź—«︎ replies

Awesome. I am a computer scientist with some junior software engineering experience. I know how to make complex state machines if I can design everything from scratch. But with Godot, as simple it makes some things, it sometimes also opens some new questions. How to implement a complex state machine was one of them that explicitly floated my mind just recently. It feels a bit like you read my mind.

👍︎︎ 3 👤︎︎ u/JulianHabekost 📅︎︎ May 04 2019 đź—«︎ replies

Great topic, especially considering the lack of such tutorials for Godot! Subscribed to channel!! Thank You!

👍︎︎ 2 👤︎︎ u/yahma 📅︎︎ May 04 2019 đź—«︎ replies
Captions
In this video, you will learn how to create a simple state machine that will give you better control over your code, make it easier to implement ai into your games, and allow you to automate certain behaviours. Howdy! And welcome to Game Endeavor. State machines are a vital part of coding complex behaviors in your games such as wall-jumping, climbing ladders, and enemy ai. It allows us to organize our code and limit its scope based on its current status. I use them daily in my developer job, and in this tutorial I will be showing you how I quickly implement a reliable state machine. Let’s go ahead and get started. We’re going to be creating an abstract state machine. Meaning you will never use this script directly, instead you will always inherit from it and call the method hooks that we’ll provide. The state machine will be a script that will attach to any node, meaning we won’t need a dedicated scene for it, just the script. We need to keep track of the state that we’re currently in, so I’ll create a variable named `state`. Sometimes it is useful to know what the previous state was as well, so we’ll create another variable named `previous_state`. The state machine is going to act as a sort of interface. It’s not going to perform any actions on its own, instead it is going to check some conditions and then tell its parent what it should be doing. To do this, we’re going to need a reference to what we’ll be controlling. So I’ll create an onready variable named parent, which we’ll set to the return value of `get_parent()` Every tick, this state machine is going to have two phases. For the first phase, it is going to call the logic required for the state. For example, in a jump state, we would allow the player to move left and right, we would apply gravity, and then we want `move_and_slide` to be called. But if we were in a wall-jumping state, then we would not want the player to move horizontally else they would fall off the wall. So we’re going to create a method that will handle the logic, which we’ll name `_state_logic.` We’re going to be calling this method in `_physics_process()`, so we’ll want to access delta. Go ahead and add that as a parameter. This is going to be a virtual method, meaning it’s expected that we’re going to override this method in the scripts that inherit from this. So we’re not going to write any code for it here, we’ll simply close this method with a pass statement. Next, we want to handle the transitions that change our state. This is where we’ll determine if we can go from standing to jumping, or jumping to falling. However you may not want to go from wall jumping to moon walking, and this method will be how we manage those transitions. So we’ll make another method named `_get_transition()`. Although not as useful to us here, we’re also going to pass in the delta parameter, since we’re also going to be calling this in the `_physics_process()` method. This method will be virtual as well, but we’re expecting it to return a value from it, so for now we’ll have it return null. Not only do we want to perform the state logic every tick, but often we will also want to perform some logic once when we enter a new state or as we’re exiting a state. So to prepare for this we’ll create a couple of methods. The first being `_enter_state()`. This will take two parameters, the first being `new_state` which will be the state that we’re transitioning into. This is the most common value that you’ll be checking for. The 2nd parameter will be `old_state` which we’re transitioning out of. In this method, it’s very common to do things such as setting your animation for the state, or starting tweens and timers. This method is also virtual, so we’ll close it with a pass statement. The next method will be `_exit_state()`, and this will need the parameters `old_state` and then `new_state`. And finally we’ll close this off with a pass statement as well. Now that we have these methods. We need some sort of mechanism for changing our states, so we can ensure that these methods are properly called. So we’ll create a method named `set_state`. This will need a parameter of `new_state` so that it knows what state to change in to. Since we’re keeping track of our previous state, we can update our variables first and then call the enter and exit methods. So we’ll set `previous_state` equal to `state`, and then we will set `state` equal to `new_state`. First, we’re going to call our `_exit_state` method, so we’ll make a call to that and give it our `previous_state` as the first parameter, and then `new_state` as the 2nd parameter. However if our `previous_state` is null, then there is no reason for us to call this method, because there is no state for us to exit from. So we’ll add an if condition before this, checking to see if `previous_state` does not equal null. Now that we’ve exited the state, we want to enter the next state by calling our `_enter_state` method, and then pass to it our `new_state`, and then `previous_state` parameters. But again, there’s no need to call this method if we’re not entering a state. So we’ll preface this with an if condition checking to see if `new_state` does not equal null. And that’s the mechanism for changing our states. If you want to call this automatically anytime the state variable is set outside of our script, then we can set our state variable to use `set_state` with setget. Keep in mind though that in this script and scripts that inherit from it. We will need to call the `set_state` method directly. Now that we have these methods, we will go ahead and create our `_physics_process()` method. We’re going to check to see if our current state is not equal to null. There’s no reason for us to perform state logic or transitions if we have no state. And then, inside of this if condition we will call our method `_state_logic()`, and pass to it the delta parameter. We’re going to call `_get_transition()` as well, passing to it the delta parameter, but we’re going to store its return value in a variable named transition. Which we’ll immediately check to see if it is not null. If it’s null then we’re going to assume that we shouldn’t change our state, otherwise we need to change to our state, so we’ll call our `set_state` method, passing to it the transition variable. The values that you use for the states are entirely up to you. The only requirement is that they need to be unique in some form or another. You can use enumerators, string constants, and even integers if you really wanted to, but don’t be that guy. The thing to keep in mind however, is that you may need to have nested inheritance of your state machines. So if you use an enumerator, then you will be incapable of adding more states to the enum. What I’m going to do is set up a dictionary of states that we can append to and use. So I’m going to create a variable named states, and initialize it as an empty dictionary. Then I’ll create a method named `_add_state` and it will be taking a string value as a parameter. And inside of this method, all I’m going to do it set `states[state_name]` equal to the size of the states dictionary. So the first entry will start at 0, the 2nd entry will be 1, and so on. You shouldn’t remove states from this, but you will be able to add to them. There’s no real advantage to doing this. I just think it looks cleaner, and it allows you to print out a list of your possible states. Now all you have to do to create a state machine is inherit from this script, copy over the abstract functions to your new script. Add your states in the ready method, and then set your initial state. You will most likely want to set your first state using the `call_deferred` method. This will set the state during the idle time which will allow the parent to populate their properties and be ready for manipulation. Because this state machine is a child of the parent, it will load before the parent. To help demonstrate how you would use this state machine in your projects, I have created a simple enemy ai that will stop moving when you’re looking at it. But when you turn away from it, then it will chase you. And once it is within attacking range, it will jump at you as an attack. However it will not attack you if you are looking at it. It will also turn to face you, but only if it is not attacking. Without a state machine, your code would be full of complicated if statements for each of these conditions, and this is a simple ai with only three states. But with a state machine it is far more streamlined, easier to read, and a lot less prone to bugs. I won’t go over all of the code right now because we will cover enemy ai in a future video, but as you can see, we have our `_state_logic` method that handles what the parent does. For every state we’re applying gravity. Then we check to see if we should turn, but only if we’re not in the attack state. We chase the player if we’re in the chase state, otherwise we stop moving. And then we use `_apply_velocity` to actually apply the movement. In the transition method we use a match statement to check which state we’re in, and if our state should change. For example if we’re in the sleep state but we should chase, then we change our state to chase. Or if we’re in the attack state, which is a jumping attack, then once we’re on the floor we will set our state back to the sleeping state. And then we have our `_enter_state` method which in this case is primarily used for setting animations. But as you can see, when we enter the attack state we also call the parent’s attack method, which is what causes the sleeping angel to jump towards the player. In future videos I will be showing you how we can use this state machine to perform wall-jumping, enemy ai, and more. So if you’re new then join the sub club to get notified for future videos. And until next time, y’all take it easy.
Info
Channel: Game Endeavor
Views: 56,242
Rating: undefined out of 5
Keywords: Game Endeavor, GameEndeavor, rk, rk3Omega, Godot, Godot 3.0, Godot 3, Godot Engine, Godot Game Engine, Tutorial, Godot Tutorial, Godot Engine Tutorial, gdscript, 2D Game, Game Development, Game Creation, gamedev, Indie, Indie Development, Programming, Code, Coding, Scripting, State Machine, Finite State Machines, States, FSM, Godot State Machine, godot state machine example, state machine tutorial, Godot 3.1 state machine, Godot 3 state machine, state machine node
Id: BNU8xNRk_oU
Channel Id: undefined
Length: 8min 10sec (490 seconds)
Published: Fri May 03 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.