State Machine
States, transitions and conditions on parameters; each state can play an animation on an AnimationPlayer. Scripts get on_enter_
Type: StateMachineNode · Category: Animation & Effects · Also found as: statemachine, state_machine
A finite state machine for gameplay and animation. Its states and
transitions are edited as a graph in the State Machine tab of the bottom
panel (it follows the selected State Machine node) and saved in the scene as
"state_machine".
- States play an animation (on an Animation Player) or are pure gameplay states; a state can hold a child machine.
- Transitions fire when their conditions hold: parameters
(
speed > 10), triggers (jump!), script functions (sees_player()), or groups of them (a | b), with a blend time. - Guards let a state refuse to be left (for example Leave After a
minimum time) -
sm_requestrespects them,sm_travelforces the change. - Scripts get
on_enter_<state>,on_update_<state>andon_exit_<state>callbacks (right-click a state > Add Script Callbacks).
function Player:update(dt) local sm = self.node:get_node("StateMachine") ---@type StateMachineNode local vx = self.node:character_get_velocity() sm:sm_set("speed", math.abs(vx)) if Input.is_action_pressed("jump") then sm:sm_trigger("jump") endend
function Player:on_enter_hurt() Audio.play_sound("audio/hurt.wav")endWhile the game runs (Simulate or Play) the graph lights the current state and lists the history of changes.
Properties
Section titled “Properties”No reflected properties of its own: its settings are described above.
Plus the common properties every node has.
Lua methods
Section titled “Lua methods”Call them on the node (self.node in its script, or self.node:get_node("path")). Every node also has the Node methods.
StateMachineNode:connect(event, handler)
Section titled “StateMachineNode:connect(event, handler)”Connects a handler to one of this node’s events: state_changed (The state machine entered a state); the tree events; custom ones.
self.node:connect("state_changed", function(value, source) Log.info("state_changed") end)eventStateMachineNodeEvent|stringhandlerfun(value:any, source: StateMachineNode)
Returns integer id Connection id for disconnect
StateMachineNode:sm_set(name, value)
Section titled “StateMachineNode:sm_set(name, value)”Sets a parameter that transition conditions test (booleans become 1 / 0).
local sm = self.node:get_node("StateMachine"); sm:sm_set("on_floor", true)nameStateMachineParam|stringvaluenumber|boolean
StateMachineNode:sm_get(name)
Section titled “StateMachineNode:sm_get(name)”Gets the value of a state machine parameter.
local speed = self.node:get_node("StateMachine"):sm_get("speed")nameStateMachineParam|string
Returns number value
StateMachineNode:sm_trigger(name)
Section titled “StateMachineNode:sm_trigger(name)”Sets a trigger: a one-shot event such as a button press. A trigger is a parameter
(Inspector > Parameters, starting value 0) that a condition tests with the
trigger operator (the condition row’s second dropdown). This sets it to 1; it
stays set (also while a guard blocks the change) until a transition testing it
fires, which resets it to 0. An arrow with no conditions fires at once, so give an
arrow a trigger condition to change state only when the script says.
if Input.is_action_pressed("fire") then self.node:get_node("StateMachine"):sm_trigger("punch") endnameStateMachineParam|string
StateMachineNode:sm_travel(state, blend)
Section titled “StateMachineNode:sm_travel(state, blend)”Switches to a state now, ignoring conditions.
local ok = self.node:get_node("StateMachine"):sm_travel("hurt", 0.1)stateStateMachineState|stringblend?numberCrossfade seconds for the state’s animation
Returns boolean ok false when there is no such state
StateMachineNode:sm_request(state, blend)
Section titled “StateMachineNode:sm_request(state, blend)”Asks to go to a state: allowed when an arrow leads there from the current state (or from Any state) and the guards agree (the current state’s leave rules - leave after, interruptible, its leave conditions, can_exit_
local ok, why = self.node:get_node("StateMachine"):sm_request("dash")stateStateMachineState|stringblend?numberCrossfade seconds (default: the arrow’s)
Returns boolean ok true when it changed state, string? reason why not (e.g. “attack: leave after 0.4s (0.12s so far)”)
StateMachineNode:sm_state()
Section titled “StateMachineNode:sm_state()”Gets the name of the current state.
if self.node:get_node("StateMachine"):sm_state() == "attack" then return endReturns string state The current state (“” before it starts)
StateMachineNode:sm_previous()
Section titled “StateMachineNode:sm_previous()”Gets the name of the state before the current one.
local prev = self.node:get_node("StateMachine"):sm_previous()Returns string state The state before the current one
StateMachineNode:sm_history()
Section titled “StateMachineNode:sm_history()”The last state changes (up to 32), oldest first.
for _, c in ipairs(self.node:get_node("Brain"):sm_history()) do Log.info(c.from, "->", c.to, c.why) endReturns SM_Change[] changes
StateMachineNode:sm_time()
Section titled “StateMachineNode:sm_time()”Gets the time in seconds spent in the current state.
if self.node:get_node("StateMachine"):sm_time() > 2 then self:give_up() endReturns number seconds Time spent in the current state
StateMachineNode:sm_start(state)
Section titled “StateMachineNode:sm_start(state)”Starts (or restarts) in a state, or in the initial state.
self.node:get_node("StateMachine"):sm_start("idle")state?string
StateMachineNode:sm_stop()
Section titled “StateMachineNode:sm_stop()”Stops checking transitions (the current state stays).
self.node:get_node("StateMachine"):sm_stop()In a scene file
Section titled “In a scene file”{ "type": "StateMachineNode", "name": "StateMachine", "position": [ 0, 0 ]}