Designer-friendly Finite State Machine implemented in "Godotic" way
This version is only compatible with Godot 4.x, check out godot3 branch for older version
-
Designer-friendly
Design
StateMachine
in a flowchart-like editor -
Remote Debug
Visualize flow of
StateMachine
& inspect parameters in realtime -
Self-explanatory
Visualize game/UI state from flowchart
-
Zero learning curve
Similar workflow as using
AnimationTree
, and not required to inherit any custom class, just plug and play -
Nested FSM
Nested Finite State Machine workflow supported to create complex state machine with ease
-
Reusability
As a
Resource
,StateMachine
can be used repeatedly in different scenarios(StateMachinePlayer
) and provide different outcome based on the input. -
Minimal
Compact data structure for
StateMachine
resource file
For more detail, see CHANGELOG.md
- Install directly from Godot Asset Library
or
-
Download this respository, move
addons/imjp94.yafsm
to your{project_dir}
-
Enable it from Project -> Settings -> Plugins
or
- Install with gd-plug
plug("imjp94/gd-YAFSM")
-
Select created node and the state machine editor should shows up.
-
Click on "Create StateMachine" button to get started.
Finally, Right-Click
on graph to add state node and Shift + Drag
on node to start connect(Shift + Drag
again on line to reconnect)
Special states:
- Entry: Entry point of a
StateMachine
, always required - Exit:
State
that break the flow ofStateMachine
, unless restarted withStateMachinePlayer.restart()
, mainly used in nested-StateMachine
.
After setup StateMachine
with editor, you can connect to the following signals from a StateMachinePlayer
:
transited(from, to)
: Transition of stateupdated(state, delta)
: Time to update(defined byprocess_mode
), up to user to handle anything, for example, update movement ofKinematicBody
Example code snippet of KinematicBody connect "updated" signal
And control StateMachinePlayer
by accessing parameter:
var smp = get_node("StateMachinePlayer")
smp.set_trigger("jump")
smp.set_param("jump_count", 1)
smp.get_param("on_floor", false)
smp.has_param("velocity")
That's it!
For most of the case, you don't have to inherit from any custom class by this plugin, simply just connect signals to your existing node and you're good to go.
See documentation for more details
The only different between nested/normal FSM is how state/parameters are accessed.
- normal state - "State"
- nested state - "BaseState/AnotherState/EndState"
var normal_state = "Idle"
var nested_state = "App/Game/Play" # EndState can be Entry/Exit
StateDirectory
class is provided to traverse state path like file directory:
const StateDirectory = preload("addons/imjp94.yafsm/src/StateDirectory.gd")
# Handle "transited" signal
func _on_normal_state_transited(from, to):
match to:
"Entry":
print("Enter")
"Game":
print("Game")
"Exit":
print("Exit")
# Handle "transited" signal
func _on_nested_state_transited(from, to):
var to_dir = StateDirectory.new(to)
match to_dir.next(): # Initial next() required to move to base state
"Entry":
print("Enter")
"Game":
match to_dir.next(): # It can be called recursively, until return null
"Entry":
print("Game Enter") # Game/Entry
"Exit":
print("Exit")
Behind the scene, StateMachinePlayer
always differentiate parameters into 2 types: global & local
- global parameter
- Just a normal parameter - "param_name"
- Never erased automatically, unless
erase_param
/clear_param
called by user
- local parameter
- Parameter that local to nested state - "BaseState/TargetState/param_name"
- Erased upon Exit, for example, "App/Game/Exit" will cause all local parameter("App/Game/{param_name}") to be erased
var smp = get_node("StateMachinePlayer")
var global_param = smp.get_param("state")
var local_param = smp.get_param("App/Game/playing")
local_param = smp.get_nested_param("App/Game", "playing")
smp.set_param("App/Game/End/victory", true)
smp.set_nested_param("App/Game", "paused", true)
Besides of controlling StateMachinePlayer
, it's useful to set arbitrary value with set_param
var smp = get_node("StateMachinePlayer")
smp.set_param("game", preload("game.scn"))
var game_scn = smp.get_param("game")
- Editor
When playing scene, select
StateMachinePlayer
node in remote scene tree to view flow ofStateMachine
in realtime - In-game
Add
res://addons/imjp94.yafsm/src/debugger/StackPlayerDebugger.tscn
toStackPlayer
(so asStateMachinePlayer
) to visualize the stack on screen.
Check out gd-YAFSM-demo for how you can integrate gd-YAFSM into you project and manage app state with StateMachine
Refer to Documentation located in addons/imjp94.yafsm/README.md