This demo covers the State pattern, hierarchical states, and features a pushdown automaton. It's intermediate-level material: you'll need to be quite comfortable with Godot, GDscript, and have decent Object-Oriented Programming foundations to use it. But it's also a very useful pattern in games, so that's why I wanted to open source it. If you struggle with it now, at least it'll be there in the future to learn from. The video gives a broad overview of the project. Now the code contains comments to help you understand the most important parts of the scripts. And if you have questions please leave a comment below :) There's also a chapter that covers everything step by step in the Make Pro 2d Games Godot 3 course: gumroad.com/l/godot-tutorial-make-professional-2d-games
This demo is great, but as you warned us before, it really is too complicated for beginners ^^U I think I understand the concept behind it and I love it (the code is much cleaner this way, the coding of new states is less error-prone, and everything makes more sense), but I think that it could be much more understandable (I mean by beginners, like myself) if it was presented in a *much* less complicated Godot project. Probably one using only a scene, containing about 6 nodes or so, only 2 states (for example "idle" and "move"), 3 or 4 scripts, and that's it. Maybe you (or some other friendly Godot medium/advanced user reading here) could make something like that sometime in the future? :) Anyway, thank you very much for the video and the code! :D
The only functions I use with the state pattern are the main ones (change_state and update). I think the code gets even more readable without adding code that wouldn't really be necessary ("enter" and "initialize" functions, arrays, etc.). I'm doing my fighting game that way. Also, GDScript is a friendly enough language and it doesn't take a lot of effort to understand its code, so any pattern applied (like solid) makes everything very readable.
Interesting timing, since I will likely benefit from implementing one of those in a near future for a stealth thing I'm doing, where the character should be able to walk slowly, walk normally, run, sprint, crawl, sneak (crouch) and stand. I had never thought of doing it with a FSM.
Thank you for this video. I wrote FSM toolkit with my brother recently and we are quite happy about how it works but you example showed me some nice ways to structure our code better. It's also clear example why states stack is nice thing to have. We'll be implementing some of those ideas soon :)
It's one of the rare examples where pushdown is useful though :). It's a KS backer who suggested Stagger because I was stuck trying to find something different from the Game Programming Patterns book.
u say that each state is self contained, but is it 100% so if in your move script update function, it checks if ur moving and if not it returns "idle"? Awesome tutorial btw, just a bit confused about how to encapsulate for maximum efficiency
Thank you so much for this, now I gotta spend few hours trying to understand how it all ties together and decipher the code, but doing the whole thing and giving it away for free and opensource is amazing, keep up the good work!!
This is really helpful. I'm not really a game developer but I am covering Finite State Machines at university and this is an excellent way to visualize it. Great video.
I see the note you picked up from Game Programming Patterns (By Bob Nystrom) I'm barely into the book right now and I'm stoked to see that you take note from the same place I do.
Thank you for this. I'm glad you went over this as this is a nice follow up from the course. Ludum Dare 41 and work have kind taken me away, but going to get back to it this weekend.
Hi, thanks for the video. In the state machine's _change_state function, you specify that if the state_name is "previous", you don't wanna "reinitialize the state" by calling the ".enter()" function. But why ? For example, if I have an idle animation, and a stagger animation, when the stagger animation is finished, it won't go back to the idle animation. This is verifiable in your project easily, by ending the stagger animation on a red frame, you'll see that it stays red after the stagger has ended. I understand that you don't want to start the animation from the beginning in some cases, but how do you go back to the previous animation ?
I understand that the code & project is more readable but isn't this unnecessary overhead - decrease in performance for impractical reason? (in this case - nodes for each state having it's methods and game-update functions instead of single/few if(s) + might be harder to see/manage physics' forces integration) For readability and performance you could still use enums assigning name constants (like STATE_JUMP) and evaluate input with simple calculations
Optimization is secondary (at least) to comprehensibility, maintainability, and brevity. How many sets of state nodes (different entities with state) are you running where you think doubling, tripling, or octupling every entity to give it node based state would be a problem?
this state machine worked great until it didn't and cost me three fucking days to figure out what was going on. if anyone else tries to use it in a 3D game where you need jump on moving platforms, do NOT call move_and_slide inside your state scripts, only call it in the script on your kinematic body. trust me this will save you so much time and headache...
It's gaining some - fastest growing game engine on GitHub this year. But without the money of a large tech company for marketing, it spreads through word of mouth and through the community, which takes time
I had a little problem with this. The problem was with the animations and the pushdown automata pattern. Just to test it, I printed out the current_animation from the animation player. When you jump, then land and keep moving without stopping, the animation stays idle instead of walking. Maybe there's a very easy solution, but of course, this happens because the walk state doesn't get to execute the 'enter' method, signaling it never plays it's animation. Any help?
Hi Nathan Do you think you could make a tutorial for this but make it as simple as possible for beginners among us. Instead of all the code for movement and jump and so on, just make a label and and have the state machine update it's text as a different button is pressed. I am trying to do just that by myself, but my code doesn't work. I am trying to understand the very basics of state machine by making the simplest working version of it. But stuck, like I said. 😓
I know I'm kinda late to the party but I'm trying to implement FSM into my game and I can't figure out, what does the for cycle in state_machine inside ready func do. It's this part: for child in get_children(): var err = child.connect("finished", self, "_change_state") if err: printerr(err) It does not work without it but when I try to use it as is, it says signal finished does not exist
Yes, you don't have interfaces in gdscript per say. Abstract classes and interfaces are close, and in this case, State only has methods, no state of its own, like an interface would.
I like your tutorials alot, but I see no need for a final state machine, seems like it just adds more complexity to the development. I mean, if i need to add a new feature i would have to write another state for that funktion too?
I've spent a few evenings trying to figure out a problem I have with this and I wondered if you might be able to help. If I am making a platformer, where do I put the code that applies the gravity? I am having all sorts of bad ideas (i.e create a `rising` and `falling` state, which seems super finicky). Then again, if I would put it in something more "global" (like the PlayerController), my question becomes: Who should call `move_and_slide`? I am under the impression that I should call it only once per frame. Can you point me in the right direction? :)
solved the error thanks to link below. i broke the sword attack state because i didn't think about which enum set goes where but at least it proved that STATES.IDLE != ATTACK_INPUT_STATES.IDLE github.com/godotengine/godot/issues/23757
Why is the demo bugged so the player cant move back and forth? The demo keep freezing and stopping the change of state at random change of keys pressed. - EDIT Nevermind, i was playing with the arrow keys and forgot than my xbox controller was already plugged in. Had to unplug it so the keyboard entries dont get interrupted by sensitive axis changes.
Great tutorial :) But it looks so complicated to me, probably because I never used FSM and just made my own stateful object behavior. I have 6 states in my AI but it will take more time to convert to FSM than writing few more states and debugging them if needed
If you use the pattern from the start it doesn't take more time. Often less as you tend to generate fewer bugs with it and they're easy to spot and fix. Then you can't be sure sticking to stateful objects will save you time or not. It may take less time to add 1 or 2 states to your AI script but the cost grows with every function, variable and feature you add. The biggest issue is when you have a bug you have a hard time tracking down because you have too many member variables. You can end up wasting an hour trying to figure out where you didn't reset a value on this or that state. One thing with the state pattern is it's scalable: even if you need 20 states it's still manageable (common even for simple AI) while with a stateful object you waste a lot of time. And refactoring when you have 10+ states will be a real pain compared to doing it early on.
Ye, I understand that ;) I ended up rewriting code to a simple FSM, one which has all the code in the same script (the FSM tutorials I have checked have it all in the same file) as I run into inheritance problems. It is very simple: I have a small state machine which checks "state" variable and runs corresponding state. There can only be one state at a time. Once a state has completed, it calls the next state. E.g. wander --> (if player detected) --> chase --> (if player is reached) --> attack --> wait --> wander etc. This is FSM right? In this case player detection, distance to player etc are inputs for the enemy AI
You have the state pattern when the states encapsulate all the code that corresponds to a given action and they don't know anything outside themselves and the host/body they control. If you have issues with inheritance and you can't break them in separate scripts you may still have a stateful object. By the way one point of breaking up states is to reuse them: if they're individual objects you can reuse individual states on different monsters in the same project without having to make changes.
Cool! Ye, I can't break them as these states depend on some common code: navigation, movement etc. So I will have to separate them first. I like your way as it is so neat
You'll also see the first time you do it it takes some time as it's a new concept and programming patterns abstract your code a little bit. But once you become comfortable with it, it had a few advantages
Cool. I prefer this over the old solution. However i want to make a system where npc and player can use the same state scripts. With the states inheriting from the motion script it will be hard. Will try to build on this though:)
Working with the inherited motion state isn't a problem: it's input events fed to a state's handle_input method that make the character work, so for the AI you can plug in a brain-like script and have it send virtual inputs (InputEvent objects you create by yourself) down this method.
Great video, thank you! I am curious, why is the script for the State node only visible in the folders on the left, but there is no script icon in Scene tree view on the top right or the in the inspector @ 01:39(bottom right)?
The States node is just here for visual grouping, to make it easier to find our state nodes from code, and to document our scene for our teammates: $States/... #
This is incredibly frustrating, just came here for Finite State Machine, the the video narrows down on a lot of things, character control, animation, movement velocity. Please just narrow down on Finite State Machine without explaining how you made the prototype as we can always just read that from the sample code. I am already 6 minutes in and I am butt clenched on the climactic explanation on Finite State Machine (e.g. explaining why we are pushing and popping states, what happens when an event is emitted, etc.) and ended up peer reviewing the code. I don't want to finish entire video anymore. It doesn't even pore over pattern itself it kept on explaining the project. I'll guess I'll go straight to the resource material.
This is needlessly complex with its reliance on TWELVE SEPERATE SCRIPTS. You spend most of the time talking about how to pass operations between nodes and the functions of the states as opposed to explaining the operation of the state machine. I opened the demo, and most of its functions are isolated in their own scripts, making it a nightmare to track down what is going on and where.
When I try to build a state machine this way I can't, because the player node is a KinematicBody2D and it throws an error because the State Machine nodes are of a different type. You seem to have them inheriting from your main player node somehow, could you explain what you did there?
I don't remember how we did in this video, but the general idea is that the state is an object that takes control of another, in this case the KinematicBody2D node. So the state can use a reference to the player node and call functions on it, like player.move_and_slide(). That's the general idea. If you have your states as nodes in a saved scene, the root of that scene is accessible through a member variable called owner.
mh, tried to recreate the state machine from the playerv2. why won't the concept click with me ? XD nothing works and i dont know why.... its time to quit. im to dumb for it
Hey there @GDQuest! Thanks for your awesome content as always, you are of immeasurable help as I try to learn Godot. Quick question... I'm running into an issue when attempting to run the FSM demo PlayerV2 scene on Godot 3.1 from a Mac. Line 9 in your sword.gd script is throwing an error... "Parser Error: Identifier 'IDLE' is not declared in the current scope" Any idea why this is happening?
To add to this, I completed removed the sword and attack states to see if I get work around the issue, and ran into other issues (the animation player node was not being found from the idle.gd script). Not sure what the hangup is since I'm pretty new to 3.1, and am a fairly novice programmer. But thank you for the concepts, I'll try to figure out my own FSM! Thank you again for all your killer tutorials! :D
Yes, for a simple script you can go with a stateful object, what I show in chapters 1-4. This pattern is covered in details in chapter 10 in the course (pro version). If you do games as a hobby or have limited experience you likely won't use it at first. For small characters, monsters, simple AI this pattern is a lot of extra work for little gain. But when you have more than... say, maybe 6 states? You may start to consider it. There's always many solutions to one problem in programming, and as a developer you will have to learn many techniques and gain experience to know when to use each of them. There are yet other and possibly more advanced ways to code a character or an AI, depending on the size of your project!
I think I've mentioned it in the course but in case I didn't: when you create prototypes it's often better to go without patterns to save time. You're not going to keep the code most of the time. In this case what you saw in the course is super useful because it has some of the advantages of the State pattern but it's a lot faster to code. The only issue is it doesn't scale well. But if you code a big character the state pattern doesn't scale too well either ^^
I had to dislike this. The overview is the part that's easy. Encapsulate code, single purpose functions. This is literally everything we don't need to know, and none of what we actually need to understand.
The main reason is that it is visual and declarative code. When you open the scene, it is clear for you and your teammates what's the machine can or cannot do. As you add sequences and hierarchical States, or design behavior trees, you can also see the parent child relationships between the nodes. As a bonus you may also get some minor performance gains having your nodes serialized as binary data, loaded on the backend, vs instancing them from gdscript. Now that is if you want to use nodes and benefit from Godot's architecture. If you need a lower footprint on your objects you could use scripts that don't extend anything instead (but better rely on the engine's features imo)
+GDquest I have one more question, regarding Pushdown Automata, is there a reason not to reinitialize the previous state if we're going back to it? @6:18 If you're going from jump to moving, the idle animation plays. When state changes from stagger to idle, stagger animation still plays. However, the game still works fine, even if you enter the previous state. Thanks so much for answering my previous question by the way.
Why did you split all the states into their own nodes and scripts? Wouldn't it be simpler (both programming and project-wise) to just keep all of the state information in a single enemy script and switch states using bools and if statements in the update function?
I'm not super knowledgeable on this, but afaik states are usually isolated code so you can just call on it, and it'll do it's thing Kinda like an object does it's own thing. Putting it together seems to kill the purpose of that.
It's fine, having nodes it mostly more convenient: the Node class gives you hierarchical state machines for free, and if you're working within a team, it's nice for designers to be able to use and configure states in the inspector.
@@LedoCool1 Cost, you mean the time it'd take to port the code? Note we have a more recent version of this FSM, here are two projects with examples: 1. In 2d github.com/GDquest/godot-platformer-2d/ 2. In 3d github.com/GDquest/godot-3d-mannequin
This was impossible for someone who doesn't already know what you are talking about to follow. I hope the tutorials people are giving you money for are not as hard to understand.
I like your tutorials alot, but I see no need for a final state machine, seems like it just adds more complexity to the development. I mean, if i need to add a new feature i would have to write another state for that funktion too?
A Finite State Machine Adds to the Organization, also it makes things a hell of a lot easier in the future as it makes it harder for the game to fall apart with larger projects. You can use a Finite State Machine anywhere, and may require one for certain things, like for example, Special Moves in Super Smash Bros.