Venus Lua API › Systems
FSM
Attach YAML-defined state machines to UI objects.
lua/venus_lua/src/lua_fsm.rs
FSM
A module singleton (global functions only, no handle) for wiring a hierarchical FSM — a YAML
template plus a unified Lua script of per-state callbacks — onto a UI object. The script’s
state sub-tables become the machine’s states; module-level lifecycle/event functions (on_create,
on_enable, on_disable, on_destroy, OnClickRelease, update, late_update) run alongside.
Each state sub-table may define any of the per-state callbacks update, entering_update,
exiting_update, enter_start, enter_complete, exit_start, exit_complete, child_start,
child_complete, child_update, and can_transition. For the 3D/scene-graph equivalent driven
off the fixed step, see FixedUpdateFSM.
The global FSM table exposes one function: AttachToObject. Transitions are requested on the
FSM component itself — see UIFsm.
Per-state callbacks are dispatched by the FSM, so it hands itself in as argument 2:
function(self, fsm, data, time, dt, ...). It is pre-resolved because these run every frame.
Exception — can_transition: this one does not take the update-family shape. It is called
function(self, fsm, targetStateName), where arg 3 is the candidate target state’s name (a
string) — not data, and with no time/dt. Return a boolean: a falsey value blocks that
transition; an unset callback or a runtime error in it defaults to allowing the transition.
Module-level UI events (OnClickRelease, OnHoverBegin, …) are dispatched by
UIScriptBehavior, the general UI scripting component, which knows nothing about state machines
and injects nothing: function(self, source_name). A handler that needs the FSM reaches it with
UIFsm.Get(self.object).
self is the per-instance context table, shared by both. The engine populates object and
behavior on it and reads nothing else — the rest is yours.
Template limits. A template holds at most 256 states, and each state’s data carries up to
8 named f32 parameters (MAX_STATES / MAX_STATE_PARAMS) — the same limits as
FixedUpdateFSM.
Functions
FSM.AttachToObject( UIObject uiObject, string yamlPath, string scriptPath ) : boolean or nil, string
FSM.AttachToObject( UIObject uiObject, string yamlPath, string scriptPath, string componentName ) : boolean or nil, string
Loads the FSM template from yamlPath (parsed once, then cached by path) and the unified Lua
script from scriptPath (both resolved relative to the FSM manager’s base directory), then
attaches a UIScriptBehavior + FsmComponent pair to uiObject. The optional componentName
names the behavior component. Returns true (a single value) on success, or nil plus an error
string (two values) on failure — a null uiObject, a resolved path that is too long, an unreadable
or malformed YAML file, a template with no states, a full template/script cache, a script that
fails to load, or a component add that fails. yamlPath/scriptPath are read with lua_tolstring,
so a missing or non-string path produces the nil, "…missing…" error rather than raising.
Removed:
FSM.RequestTransitionno longer exists. It had always been a registered no-op —
it resolved the component, then discarded the work under a// TODO: Direct transition APIand
returned no values, so a caller’s request vanished with no error and no log. The mechanism that
actually worked was assigning a magic string field,self._pending = "state_name", on the shared
context table; that is gone too. Both are replaced byfsm:RequestTransition("state_name"),
which returns a real result.
Example
-- Attach the menu state machine to a UI panel.
local ok, err = FSM.AttachToObject(panel, "ui/menu_fsm.yaml", "scripts/menu_fsm.lua", "MenuFSM")
if not ok then
print("FSM attach failed:", err)
end
-- Inside scripts/menu_fsm.lua, a state requests the next state like this:
-- M.main_menu = {
-- update = function(self, fsm, data, time, dt)
-- if start_clicked then fsm:RequestTransition("game") end
-- end,
-- }