Godot Version
4.3
Background
I’m a game dev newbie and lately I’ve been trying to wrap my head around state machines. It’s a topic that I see coming up at lot as something everyone should try to learn early on. It’s something I find really interesting and want to master but every article I read and every video I watch seems to do handle it differently, and I’m just finding it all a bit overwhelming.
I’m creating this topic as a discussion in the hope that some of the more experienced devs here might be able to offer some advice.
Genre
My main interest is 2D RPGs/platformers etc. So when I talk about state machines I’m typically talking about:
- Object State (e.g. the state of a door)
- Player State (your controllable character)
- Enemy State (enemy AI)
State machine
I want to wrap my head around the enum state machine first. I appreciate there are other and potentially better ways of implementing a state machine but I think the enum state machine is the simpler option and fits most of my use cases right now.
My implementation
So my current implementation for a basic enum state machine is to first define the states:
enum PlayerState {
IDLE,
WALK,
JUMP,
FALL
}
Then add a variable to store the current state and set the default state:
var _state: PlayerState = PlayerState.IDLE
I handle input via separate functions using the InputMap. So I then then use velocity and other tools to help work out which state the player is currently in:
if is_on_floor():
if velocity.x == 0:
set_state(PlayerState.IDLE)
else:
set_state(PlayerState.RUN)
else:
if velocity.y > 0:
set_state(PlayerState.FALL)
else:
set_state(PlayerState.JUMP)
Finally I have a function that sets the new state and matches the current state to a set of behaviours:
func set_state(new_state: PlayerState) -> void:
if new_state == _state:
return
_state = new_state
match _state:
PlayerState.IDLE:
animation_player.play("idle")
PlayerState.RUN:
animation_player.play("run")
PlayerState.JUMP:
animation_player.play("jump")
PlayerState.FALL:
animation_player.play("fall")
Questions
Firstly, does this work?
- I mean it works but does it fit the state machine design pattern? Is it a proper state machine or am I breaking the rules?
Where should I implement Gravity?
- Currently I apply gravity separately from the state as I want to apply regardless of the state. However there may be situations where I either want no gravity or I want to behave differently such as floating or swimming. Should gravity be handled independently by each state?
How should I handle input?
- As mentioned above, currently I do this via a function using the InputMap that’s separate from the state but should I handle it within the state itself? For example, below is my jump code. It’s constantly being checked but only runs when both the jump button is pressed and the player is on a surface. Is it more fitting of the state pattern to code this into the states where jump is allowed? So for example, it only checks for the jump input from the IDLE or RUN state (likely means some code duplication)?
func jump() -> void:
if Input.is_action_just_pressed("jump") and is_on_floor():
velocity.y = JUMP_VELOCITY
- The same applies to movement. Should I just check for left & right input and and handle movement individually in each state? So in this case, all of them? Or should I stick to one global function that happens regardless of the state?
How should I handle animations?
- Here I’m keeping things simple with an AnimationPlayer and just playing the animation based on state. When handling the input I also flip the sprite based on the direction.
- Should I consider using an AnimationTree for this? I’ve done a bit of work in unity using a similar component that would change animation based on a variable (e.g. is_falling). I appreciate that this is a separate topic entirely but I’m still keen to hear thoughts on whether or not you think this is something I should be exploring at the same time.
Summary
Apologies that turned into a bit of a wall of text. Any help anyone can provide would be hugely appreciated. I really want to master this subject but I’m finding it a bit overwhelming so I’m trying to break it down into smaller chunks.