Last Update: 2023-07-06
States API
The States API allows you to define individual states within a stategraph, controlling how entities behave in different situations.
State Structure
A state is created using the State
constructor with a table of properties:
State{
name = "idle", -- Unique name for the state
tags = { "idle", "canrotate" }, -- Tags used for querying state properties
onenter = function(inst) end, -- Called when entering the state
onexit = function(inst) end, -- Called when exiting the state
onupdate = function(inst, dt) end, -- Called every frame while in this state
timeline = { -- Timeline of events within the state
TimeEvent(10*FRAMES, function(inst) end),
TimeEvent(20*FRAMES, function(inst) end),
},
}
Key Properties
name
The unique identifier for the state, used when transitioning between states.
name = "attack",
tags
Tags are used to categorize states and query state properties. Common tags include:
idle
: Entity is in a neutral statemoving
: Entity is in motionrunning
: Entity is runningbusy
: Entity can't be interruptedattack
: Entity is attackingcanrotate
: Entity can change direction while in this state
tags = { "idle", "canrotate" },
onenter
A function called when the entity enters the state. This is where you typically:
- Play animations
- Start sounds
- Set up timers
- Initialize state variables
onenter = function(inst)
inst.AnimState:PlayAnimation("idle_loop", true)
inst.SoundEmitter:PlaySound("dontstarve/creatures/spider/spider_walk")
inst.sg:SetTimeout(2 + math.random()*1)
end,
onexit
A function called when the entity exits the state. Use this to:
- Clean up timers
- Stop looping sounds
- Reset variables
onexit = function(inst)
inst.SoundEmitter:KillSound("growl")
end,
onupdate
Called every frame while in this state. The dt
parameter is the time since the last update.
onupdate = function(inst, dt)
if inst.components.locomotor:WantsToMoveForward() then
inst.components.locomotor:WalkForward()
else
inst.sg:GoToState("idle")
end
end,
timeline
A sequence of timed events that occur while in the state. Use TimeEvent
to define when functions should be called relative to entering the state.
timeline = {
TimeEvent(0*FRAMES, function(inst)
inst.SoundEmitter:PlaySound("dontstarve/wilson/attack_weapon")
end),
TimeEvent(8*FRAMES, function(inst)
inst.components.combat:DoAttack()
end),
TimeEvent(12*FRAMES, function(inst)
inst.sg:RemoveStateTag("attack")
inst.sg:AddStateTag("idle")
end),
},
events
A list of events that can be handled while in this state. When an event is triggered, it can cause a state transition.
events = {
EventHandler("animover", function(inst)
inst.sg:GoToState("idle")
end),
},
State Manipulation
Within a state, you can use these methods to manipulate the current state:
inst.sg:GoToState(statename, [data])
: Transition to a new stateinst.sg:AddStateTag(tag)
: Add a tag to the current stateinst.sg:RemoveStateTag(tag)
: Remove a tag from the current stateinst.sg:HasStateTag(tag)
: Check if the current state has a taginst.sg:SetTimeout(time)
: Set a timeout for the current state
Common Patterns
Animation-driven State Transitions
-- Go to another state when animation finishes
events = {
EventHandler("animover", function(inst)
inst.sg:GoToState("idle")
end),
},
Timeout-driven State Transitions
-- Set a timeout when entering the state
onenter = function(inst)
inst.AnimState:PlayAnimation("idle_loop", true)
inst.sg:SetTimeout(2 + math.random()*1)
end,
-- Define what happens when timeout is reached
ontimeout = function(inst)
inst.sg:GoToState("alert")
end,
Complex State Logic
onenter = function(inst)
inst.AnimState:PlayAnimation("atk")
inst.components.locomotor:StopMoving()
inst.sg.statemem.target = inst.components.combat.target
inst.components.combat:StartAttack()
end,
timeline = {
TimeEvent(8*FRAMES, function(inst)
inst.components.combat:DoAttack(inst.sg.statemem.target)
end),
},
events = {
EventHandler("animover", function(inst)
inst.sg:GoToState("idle")
end),
},