Лучший урок по стейтам, что я видел. Не только дал удобную ФСМ, но ещё и очень просто объяснил как всё это работает и зачем нужна каждая строчка. Блин, если бы все на ютубе объясняли как ты - я бы уже закончил свою игру. Спасибо!
This is a very useful video for all beginners, not just godot users! It explains exactly what a FSM is and how it should work, not to mention it is well edited and easy to understand. Good job, keep up the good work :)
tysm for this tutorial, ngl the code here is like identical to a tutorial about the same subject from Bitlytic (not sure if that's intentional or a happy accident) but like, you actually explain it in a thorough and easy to understand way? Bitlytic skims over so much important info for beginners but you hit the nail right on the head with the explanations, super comprehensive and easy to follow, tysm again king ily
Finite State Machine extends Node class_name FiniteStateMachine var states : Dictionary = {} export var initial_state = NodePath() onready var initstate:State = get_node(initial_state) var current_state:State var change_state:State func _ready(): for child in self.get_children(): if child is State: states[child.name.to_lower()] = child child.connect("state_transition", self,"change_state") if initial_state: initstate.Enter() current_state = initstate func change_state(source_state:State, new_state_name:String): #redundancy checks if source_state != current_state: print("Invalid change_state trying from: " + source_state.name + "but current in: " + current_state.name) return var new_state = states.get(new_state_name.to_lower()) if !new_state: print("New state is empty") return #only relevant lines if current_state: current_state.Exit() new_state.Enter() current_state =new_state func force_change_state(new_state:State): var newState = states.get(new_state.name.to_lower()) if !newState: print(str(new_state) + " does not exist in the dictionary of states") return if current_state ==newState: print("State is same, aborting") return if current_state: current_state.call("Exit") current_state=newState newState.Enter() func _process(delta): if current_state: current_state.Update(delta) func _physics_process(delta): if current_state: current_state.Update(delta)
Player_Idle State extends State class_name Player_Idle export var sprite = NodePath() onready var _sprite :AnimatedSprite = get_node(sprite) #gets FSM node onready var fsm = get_parent() #onready var fsm = $".." onready var moving = $"../Moving" func Enter(): #_sprite.play("Idle") pass func Exit(): pass func Update(_delta:float): if (Input.get_vector("move_left","move_right","move_forward","move_backward")): print("moving") #Transition to move state fsm.change_state(self, "Moving") pass if (Input.is_action_just_pressed("attack")): #transition to attack state pass pass
I had a project where I tried a sort of state machine but used timed animations that would trigger follow up states. I ran into issues when trying to do a forced state while animations were still running. Im pretty sure a more defined approach like what you have here would have sorted out my issues, being able to end animation delegates in the current state Exit method before killing the object. Cool explanation, thanks.
This looks like a very organized way to do it. I just throw all of the states in a switch function in my player’s script, and each state gets its own function, and then each state’s function has logic for the other states it can switch to… I like this idea though!
I don't think I actually need a state machine but i want to learn them, this is like the third video I've watched on them and they haven't become any less intimidating. Question about your force state function, how exactly can it break your game? Aren't exit functions meant to prevent the state machine from breaking or have I misunderstood their purpose? Oh wait the problem is with decentralised state machines, right? Since each state is only accounting for it's own transitions and trying to transition to something it doesn't know how to could break things? I don't know...
Think of state machines as applying a script to each game mechanic. It isn't important for small projects but it makes doing big projects like RPGs or fighting games easier.
"Break your game" was me being a bit dramatic. Creating a system of transitions only to then override them with force can lead to unexpected behaviour I guess. I don't want to make a tutorial where I use a 'force-change' function to ignore what I am trying to teach in the video
I've been watching through a couple of different videos after finishing my player movement system in my first game and decided to switch it over to a state machine, since I plan on adding even more movement mechanics later on and I want to make it easier on myself. This is the first video I've found that explains things in such detail, so thank you! However, in every video I've watched and tried to follow along, I keep getting stuck on specific tidbits that I can't seem to find an answer to either in the video, or on specific searches on Reddit/Google/Etc. Where would be the best resource for figuring out solutions to problems like "In my Finite State Machine script, there are 'Could not parse global class "State" from "res"//Scripts/PlayerState.gd" over every mention of the variable 'initial_state'. How do I fix this?". I want to find a community or resource where I can easily look up these errors and find out why its not working. And if the reason I can't figure this out is due to a lack of very basic knowledge of GDScript, where should I start learning that basic knowledge?
Any tips on how to combine this with composition (stuff like making components and composing entities from them, like VelocityComponent, HealthComponent, WanderAroundComponent, etc)
This video helped me more than most to figure out state machines. thank you. Do you know if this code allows for switching scenes? For example i have a state called Driving (my character goes invisible, stops processing, etc) and i take control of another characterbody2d which has its own set of states .
Absolutely. An interaction script activates the vehicle and tells the fsm to transition to driving state which 'deactivates' the player. A player state should not be responsible for anything outsider the player i think 👍
amazing video, but i feel it's definitely important to show and explain how to use state_transition and explain what it does, and how it connects the scripts. great otherwise though, i needed someone to explain FSM to me like i'm 5 lmao
Is this the same as Finite State Automata? Where you have a Deterministic Finite Automaton, DFA, a Non Deterministic one, NFA, an Epsion Non Deterministic one, e-NFA, and you work on reducing whatever automata you have into a minimal deterministic one? I took this as part of an introductory course to compilers and it seems awfully similar
I don't know why but state machines seem so much more difficult and less straightforward than in C, C++, or C#. It must be the whole dynamic programming thing. In C like languages, I'd just create a state and subState enum, subState would have Init, Run, End and state would have animation names, then just create one long script for everything. Signals are just Events and Callbacks but they call them signals. I guess these days this isn't a modern style of programming, but I'd have as few scripts as possible as well.
i dont know how i should transition the state for my enemy. so it detects when the player entered his territory and then it should switch to chase state. What do I have to put in there? # In the Idle State: func _on_view_area_body_entered(body): if body.is_in_group("Player"): FiniteStateMachine.change_state(State, "BaseNPCChaseState")
@@ForlornU sorry for lack of context in my comment, it throws me error in 2:58 part in statemachine main script in func _ready > initial.state.Enter() i guess i missed something.
Please for the love of my sanity can you choose a notation and stick with it? Changing between snake and camel notation randomly is making my brain hurt.
I feel like you sort of skipped over the part where you explain how states communicate with each other? I looked through the github to try to understand the code better and there's this line from the Idle state: state_transition.emit(self, "Moving") that was never explained. I'm sorry, I'm very new to coding and I wanted to figure out state machines, but I'm not sure how this piece of code works and how it's picked up by other codes. My guy is still not walking
state_transition is the signal that is defined in the IdleState's script. This line emits the signal of a transition from IdleState to MovingState to the state machine class. The state machine has `current_state.state_transition.connect(handle_transition)`. It makes the FSM subscribe to the transition event that is emitted from the current state. On that event, it calls the `handle_transition` function which is defined in the FSM. It has two arguments: previous_state and new_state. Both of them correspond to the two arguments `self` and `Moving` respectively from the IdleState's script. "Moving" is the name of the MovingState node, which plays as the accessor for the MovingState. It's used in the `handle_transition` function to get the state node by the string, like `current_state = get_children()[new_state]`
Good you fucking explained what inherited means and how to do it, im sooo fucking glad
Лучший урок по стейтам, что я видел. Не только дал удобную ФСМ, но ещё и очень просто объяснил как всё это работает и зачем нужна каждая строчка. Блин, если бы все на ютубе объясняли как ты - я бы уже закончил свою игру. Спасибо!
This is a very useful video for all beginners, not just godot users! It explains exactly what a FSM is and how it should work, not to mention it is well edited and easy to understand. Good job, keep up the good work :)
Thank you, means a lot coming from a fellow dev!
tysm for this tutorial, ngl the code here is like identical to a tutorial about the same subject from Bitlytic (not sure if that's intentional or a happy accident) but like, you actually explain it in a thorough and easy to understand way? Bitlytic skims over so much important info for beginners but you hit the nail right on the head with the explanations, super comprehensive and easy to follow, tysm again king ily
they're implementing the same thing, its all finite state machine in its core, so the logic will look really similar if not the same.
You are a godsend! Explanations were short concise and easily understandable.
Thank you from the bottom of my heart! I can't wait for more!
Thank you for the kind words. More videos are coming!
I'm nearly done with porting this to Godot 3, thank you for explaining it clearly.
Finite State Machine
extends Node
class_name FiniteStateMachine
var states : Dictionary = {}
export var initial_state = NodePath()
onready var initstate:State = get_node(initial_state)
var current_state:State
var change_state:State
func _ready():
for child in self.get_children():
if child is State:
states[child.name.to_lower()] = child
child.connect("state_transition", self,"change_state")
if initial_state:
initstate.Enter()
current_state = initstate
func change_state(source_state:State, new_state_name:String):
#redundancy checks
if source_state != current_state:
print("Invalid change_state trying from: " + source_state.name + "but current in: " + current_state.name)
return
var new_state = states.get(new_state_name.to_lower())
if !new_state:
print("New state is empty")
return
#only relevant lines
if current_state:
current_state.Exit()
new_state.Enter()
current_state =new_state
func force_change_state(new_state:State):
var newState = states.get(new_state.name.to_lower())
if !newState:
print(str(new_state) + " does not exist in the dictionary of states")
return
if current_state ==newState:
print("State is same, aborting")
return
if current_state:
current_state.call("Exit")
current_state=newState
newState.Enter()
func _process(delta):
if current_state:
current_state.Update(delta)
func _physics_process(delta):
if current_state:
current_state.Update(delta)
Player_Idle State
extends State
class_name Player_Idle
export var sprite = NodePath()
onready var _sprite :AnimatedSprite = get_node(sprite)
#gets FSM node
onready var fsm = get_parent()
#onready var fsm = $".."
onready var moving = $"../Moving"
func Enter():
#_sprite.play("Idle")
pass
func Exit():
pass
func Update(_delta:float):
if (Input.get_vector("move_left","move_right","move_forward","move_backward")):
print("moving")
#Transition to move state
fsm.change_state(self, "Moving")
pass
if (Input.is_action_just_pressed("attack")):
#transition to attack state
pass
pass
extends Node
class_name State
signal state_transition
func Enter():
pass
func Exit():
pass
func Update(_delta:float):
pass
func Physics_Update(_delta:float):
pass
I had a project where I tried a sort of state machine but used timed animations that would trigger follow up states. I ran into issues when trying to do a forced state while animations were still running. Im pretty sure a more defined approach like what you have here would have sorted out my issues, being able to end animation delegates in the current state Exit method before killing the object. Cool explanation, thanks.
That punch animation was personal
This looks like a very organized way to do it. I just throw all of the states in a switch function in my player’s script, and each state gets its own function, and then each state’s function has logic for the other states it can switch to… I like this idea though!
Hey as long as it works that's what matters!
Just found your channel and I fell in love with it
Lovely intro on how FSM work, as well as separating them all out to a manager function. I believe this is how Godot is meant to be programmed.
I don't think I actually need a state machine but i want to learn them, this is like the third video I've watched on them and they haven't become any less intimidating. Question about your force state function, how exactly can it break your game? Aren't exit functions meant to prevent the state machine from breaking or have I misunderstood their purpose? Oh wait the problem is with decentralised state machines, right? Since each state is only accounting for it's own transitions and trying to transition to something it doesn't know how to could break things? I don't know...
Think of state machines as applying a script to each game mechanic. It isn't important for small projects but it makes doing big projects like RPGs or fighting games easier.
"Break your game" was me being a bit dramatic. Creating a system of transitions only to then override them with force can lead to unexpected behaviour I guess. I don't want to make a tutorial where I use a 'force-change' function to ignore what I am trying to teach in the video
This is fantastic! Thank you so much!
Thanks for this very comprehensive tutorial about FSM. 🤗
I've been watching through a couple of different videos after finishing my player movement system in my first game and decided to switch it over to a state machine, since I plan on adding even more movement mechanics later on and I want to make it easier on myself. This is the first video I've found that explains things in such detail, so thank you! However, in every video I've watched and tried to follow along, I keep getting stuck on specific tidbits that I can't seem to find an answer to either in the video, or on specific searches on Reddit/Google/Etc. Where would be the best resource for figuring out solutions to problems like "In my Finite State Machine script, there are 'Could not parse global class "State" from "res"//Scripts/PlayerState.gd" over every mention of the variable 'initial_state'. How do I fix this?". I want to find a community or resource where I can easily look up these errors and find out why its not working. And if the reason I can't figure this out is due to a lack of very basic knowledge of GDScript, where should I start learning that basic knowledge?
Fantastic video! Very high quality and helpful, thank you!
Any tips on how to combine this with composition (stuff like making components and composing entities from them, like VelocityComponent, HealthComponent, WanderAroundComponent, etc)
This video helped me more than most to figure out state machines. thank you. Do you know if this code allows for switching scenes?
For example i have a state called Driving (my character goes invisible, stops processing, etc) and i take control of another characterbody2d which has its own set of states .
Absolutely. An interaction script activates the vehicle and tells the fsm to transition to driving state which 'deactivates' the player. A player state should not be responsible for anything outsider the player i think 👍
amazing video, but i feel it's definitely important to show and explain how to use state_transition and explain what it does, and how it connects the scripts. great otherwise though, i needed someone to explain FSM to me like i'm 5 lmao
Noted, sorry I didn't explain that more clearly! Glad it was still helpful :)
Is this the same as Finite State Automata?
Where you have a Deterministic Finite Automaton, DFA, a Non Deterministic one, NFA, an Epsion Non Deterministic one, e-NFA, and you work on reducing whatever automata you have into a minimal deterministic one?
I took this as part of an introductory course to compilers and it seems awfully similar
Thank you for your hard work 😊
Where/When the state_transition is emitted?
I don't know why but state machines seem so much more difficult and less straightforward than in C, C++, or C#. It must be the whole dynamic programming thing.
In C like languages, I'd just create a state and subState enum, subState would have Init, Run, End and state would have animation names, then just create one long script for everything. Signals are just Events and Callbacks but they call them signals. I guess these days this isn't a modern style of programming, but I'd have as few scripts as possible as well.
You can do it with enums in Godot. It just feels messy in comparison to the node method
i dont know how i should transition the state for my enemy. so it detects when the player entered his territory and then it should switch to chase state. What do I have to put in there?
# In the Idle State:
func _on_view_area_body_entered(body):
if body.is_in_group("Player"):
FiniteStateMachine.change_state(State, "BaseNPCChaseState")
doesnt work
How did you animate your video btw?
I use Davinci resolve for editing, the coding animations are done in good 'ol powerpoint!
which code is connected to which?
it keeps saying could not find type state in the current scope!
:( State_Machine.gd:16 - Parse Error: Cannot call non-static function "Enter()" on the class "State" directly. Make an instance instead.
It's telling you to call the 'Enter()' function on one of the states, not the 'State' superclass itself. Are you trying to make new states?
@@ForlornU sorry for lack of context in my comment, it throws me error in 2:58 part in statemachine main script in func _ready > initial.state.Enter() i guess i missed something.
I'm sry I understand how a statemachine works in theory, but I can not make sense of this line:
child.state_transition.connect(change_state)
so i am using godot 4.2 can it work
platformer
Grab the 1.4 release for Godot 4.2. Yes you can make it into a platformer, just add gravity basically :)
when i downlorded it i shows godot 4.3
This is 4.2 Compatible, but I would also suggest just upgrading :) : github.com/ForlornU/TopdownStarter/tree/1.4
Please for the love of my sanity can you choose a notation and stick with it? Changing between snake and camel notation randomly is making my brain hurt.
Indeed it was getting out of hand, resolved in latest version!
😂 good sport
An standard naming convention is very good for team work, but for a solo small project it really is not that deep
"press enter to leave"
huh?
That does sound strange when you point it out 😅
Tutorial swim in 2d platform game godot 4 please😣
I feel like you sort of skipped over the part where you explain how states communicate with each other? I looked through the github to try to understand the code better and there's this line from the Idle state:
state_transition.emit(self, "Moving")
that was never explained. I'm sorry, I'm very new to coding and I wanted to figure out state machines, but I'm not sure how this piece of code works and how it's picked up by other codes. My guy is still not walking
state_transition is the signal that is defined in the IdleState's script. This line emits the signal of a transition from IdleState to MovingState to the state machine class. The state machine has `current_state.state_transition.connect(handle_transition)`. It makes the FSM subscribe to the transition event that is emitted from the current state. On that event, it calls the `handle_transition` function which is defined in the FSM. It has two arguments: previous_state and new_state. Both of them correspond to the two arguments `self` and `Moving` respectively from the IdleState's script.
"Moving" is the name of the MovingState node, which plays as the accessor for the MovingState. It's used in the `handle_transition` function to get the state node by the string, like `current_state = get_children()[new_state]`
not helping
what about unity? =)
Useless, you left out the most important part about how to actually trigger a state transition from the idle state class