Install Asset
Install via Godot
To maintain one source of truth, Godot Asset Library is just a mirror of the old asset library so you can download directly on Godot via the integrated asset library browser
Quick Information
A finite state machine implemented purely in code (GDScript).There are various methods to implement an FSM. This one uses state objects, a state machine, and includes a factory to create the state machine.Logic is contained with the state objects/classes that you create which can tell the state machine to transition to other states. There is support for _on_enter_state and _on_leave_state methods.Other implementations support features like arbitrary events, timers, and probably just other ways of solving the FSM problem which this library does not do. Rather than your state machine having methods to control transitions, the code is handled via the states themselves which contain a reference to the target object (your player node for example).
godot-finite-state-machine
Godot FSM (Finite State Machine)
This is a second FSM for Godot that is purely code-only (no editor plugin).
Examples
Configuration-based Setup
This first example is the "best" way to create an FSM instance, by passing a configuration dictionary to the StateMachineFactory.create()
method.
Notice the states property is an array of dictionaries, and we pass IdleState and PatrolState. The state machine handles creating new instances of the states via this method.
const StateMachineFactory = preload("res://addons/fsm/StateMachineFactory.gd")
const IdleState = preload("IdleState.gd")
const PatrolState = preload("PatrolState.gd")
# Create new state machine factory
var smf = StateMachineFactory.new()
# Create state machine using a configuration dictionary
var sm = smf.create({
"target": $Enemy,
"current_state": "patrol",
"states": [
{"id": "idle", "state": IdleState},
{"id": "patrol", "state": PatrolState}
],
"transitions": [
{"state_id": "idle", "to_states": ["patrol"]},
{"state_id": "patrol", "to_states": ["idle"]}
]
})
Manual Setup
This method shows how to manually create and setup a FSM object.
const StateMachineFactory = preload("res://addons/fsm/StateMachineFactory.gd")
const IdleState = preload("IdleState.gd")
const PatrolState = preload("PatrolState.gd")
# Create state manually
var smf = StateMachineFactory.new()
var sm = smf.create()
# Create states
var idle_state = IdleState.new()
var patrol_state = PatrolState.new()
# Add the id, state machine, and target to the states (also handled by the state machine, just shown for completeness)
idle_state.id = "idle"
idle_state.state_machine = sm
idle_state.target = $Enemy
patrol_state.id = "patrol"
patrol_state.state_machine = sm
patrol_state.target = $Enemy
# Convenience method to set state machine on array of states
sm.set_state_machine([idle_state, patrol_state])
# Add state to state machine, which also adds the state machine to the state
sm.add_state("idle", idle_state)
sm.add_state("patrol", patrol_state)
# Set states method which accepts the same array of state dictionaries as the create factory method
sm.set_states([
{"id": "idle", "state": IdleState},
{"id": "patrol", "state": PatrolState}
])
# Add transitions. Must be done after adding states, as the state ids are validated
sm.add_transition("idle", ["patrol"])
sm.add_transition("patrol", ["idle"])
# Set transitions method which accepts the same array of transition dictionaries as the create factory method
sm.set_transitions([
{"state_id": "idle", "to_states": ["patrol"]},
{"state_id": "patrol", "to_states": ["idle"]}
])
# Add a single transition
sm.add_transition("idle", "patrol")
sm.add_transition("patrol", "idle")
# Add the target node/object to the state machine
sm.set_target(get_tree().get_root().get_node("enemy"))
# Set the initial state of the state machine
sm.set_current_state("patrol")
State Machine
State Machine Target
# Set the state machine's target (recursively sets on states)
sm.set_target($Player)
sm.target = $Player
# Get state machine's target object
var player = sm.target
State Machine Current State
# Set the state machine's current state. This really should be called internally by the state machine only.
sm.set_current_state("patrol")
sm.current_state = "patrol"
# Get the state machine's current state
var state = sm.get_state(sm.current_state)
State Machine Transition
The transition method of the state machine will validate that the id passed is a valid state. It will call the current state's _on_leave_state
callback if implemented. It will then call the to state's _on_enter_state
method.
# Transition to new state
sm.transition("patrol")
State Machine Callbacks
The state manager exposes callback methods for _process(delta)
, _physics_process(delta)
, and _input(event)
. Calls to these methods are proxied down to the current state's method if it has implemented them.
# State machine callbacks which are proxied down to the current state object
sm._physics_process(delta)
sm._process(delta)
sm._input(event)
State
State ID
Each state stores its own ID.
var smf = StateMachineFactory.new()
var sm = smf.create()
var state = IdleState.new()
state.sm = sm
state.id = "idle"
var state_id = state.id
State Target
The state and state machine have a target object, which is assumed to be where any extra required context is coded against.
var smf = StateMachineFactory.new()
var sm = smf.create()
var state = IdleState.new()
state.state_machine = sm
state.id = "idle"
state.target = $Player
var player = state.target
State Callbacks
A state class can implement callbacks for _process(delta)
, _physics_process(delta)
, _input(event)
, _on_enter_state()
, and _on_leave_state()
.
extends "res://addons/fsm/StateMachine.gd".State
var memory = 0
func _init().():
physics_process_enabled = true
func _physics_process(delta: float) -> void:
scan_for_enemy(delta)
move_to_random(delta)
func scan_for_enemy(delta: float) -> void:
pass
func move_to_random(delta: float) -> void:
# some logic to move to a random location
...
# Transition to idle state if we have reached our destination
if arrived_at_location():
state_machine.transition("idle")
func _on_enter_state() -> void:
memory = 100
A finite state machine implemented purely in code (GDScript).
There are various methods to implement an FSM. This one uses state objects, a state machine, and includes a factory to create the state machine.
Logic is contained with the state objects/classes that you create which can tell the state machine to transition to other states. There is support for _on_enter_state and _on_leave_state methods.
Other implementations support features like arbitrary events, timers, and probably just other ways of solving the FSM problem which this library does not do. Rather than your state machine having methods to control transitions, the code is handled via the states themselves which contain a reference to the target object (your player node for example).
Reviews
Quick Information
A finite state machine implemented purely in code (GDScript).There are various methods to implement an FSM. This one uses state objects, a state machine, and includes a factory to create the state machine.Logic is contained with the state objects/classes that you create which can tell the state machine to transition to other states. There is support for _on_enter_state and _on_leave_state methods.Other implementations support features like arbitrary events, timers, and probably just other ways of solving the FSM problem which this library does not do. Rather than your state machine having methods to control transitions, the code is handled via the states themselves which contain a reference to the target object (your player node for example).