There is a trick I learned from Firebelley Games (a youtube channel) that is just as simple to spin up and use as the Enum + match strategy but without sacrificing any versatility.
I actually like it better than the Node-based pattern because you don’t have to set up much boilerplate, and you really don’t need to think about how different state classes might share data. Plus, none of it will clog up your scene tree or need to be pointlessly instantiated by the engine.
Tap for code
If you’re on mobile, I would recommend reading this in horizontal view.
This is all it takes to spin one up:
class_name Player2D extends Node2D
var _state_machine := CallableStateMachine.new();
func _ready() -> void:
_state_machine.add_state(
_state_idle_update,
Callable(),
Callable()
);
_state_machine.add_state(
_state_jump_update,
_state_jump_enter,
Callable()
);
# Set first state
_state_machine.switch_to(_state_idle_update);
func _process(_delta: float) -> void:
_state_machine.update();
# These are your state functions.
func _state_idle_update() -> void;
func _state_jump_update() -> void;
func _state_jump_enter() -> void;
The only thing your state machine actually needs to know is which functions are paired together. You can use Callable() to fill in any steps you’re not actually using.
States are keyed by their own update step, so there’s no extra overhead for string names or Enums or the like, and you still get your IDE’s tab autocomplete to help you with 'em.
func _state_idle_update() -> void:
if Input.is_action_pressed('jump'):
_state_machine.switch_to(_state_jump_update);
All state functions exist within the Player2D script, so you have complete access to any shared data or component that Player2D does.
My state machine automatically knows which step each function is for by the keyword after the double-unders (e.g. ‘__update’), as well as that the nameless _state_idle() is the enter step and the key that I use to switch_to().
There is a trick I learned from Firebelley Games (a youtube channel) that is just as simple to spin up and use as the Enum + match strategy but without sacrificing any versatility.
I actually like it better than the Node-based pattern because you don’t have to set up much boilerplate, and you really don’t need to think about how different state classes might share data. Plus, none of it will clog up your scene tree or need to be pointlessly instantiated by the engine.
Tap for code
If you’re on mobile, I would recommend reading this in horizontal view.
This is all it takes to spin one up:
The only thing your state machine actually needs to know is which functions are paired together. You can use
Callable()to fill in any steps you’re not actually using.You call
update()yourself, so its timing is completely under your control.States are keyed by their own update step, so there’s no extra overhead for string names or Enums or the like, and you still get your IDE’s tab autocomplete to help you with 'em.
All state functions exist within the Player2D script, so you have complete access to any shared data or component that Player2D does.
A basic implementation of
CallableStateMachineis none too complicated, and you can reuse it anywhere.You can do a lot from this base setup, too. I have mine setup such that if I name my functions like this:
My state machine automatically knows which step each function is for by the keyword after the double-unders (e.g. ‘__update’), as well as that the nameless _state_idle() is the enter step and the key that I use to switch_to().
The best code is always in the comments