Finite State Machine implementation for Lua. Based on Mat Buckland's Game AI by Example
Put fsm.lua inside your project and require it:
local FSM = require "fsm"Finita uses the state class. Each state can be declared in separate file like this:
local MyMegaState = {}
function MyMegaState:new()
local obj = {}
setmetatable(obj, self)
self.__index = self
return obj
end
-- this method execute when the state is entered
function MyMegaState:enter(owner)
-- do staff...
end
-- this is called by the FSM's update function each update step
function MyMegaState:execute(owner, ...)
-- do staff...
end
-- this will execute when the state is exited
function MyMegaState:exit(owner)
-- do staff...
end
return MyMegaStateFinita automatically checks that the state implements required methods. If state failed verification, FSM raise incorrect state declaration error
local FSM = require "fsm"
local MenuState = require "menu_state"
local GameplayState = require "game_state"
local Game = {}
function Game:new()
-- create instance of FSM class and pass Game as FSM owner
self.fsm = FSM(self)
-- owner stores links to all states
self.states = {
menu = MenuState:new(),
gameplay = GameplayState:new()
}
-- set current state
self.fsm:set_current_state(self.states.menu)
local obj = {}
setmetatable(obj, self)
self.__index = self
return obj
end
function Game:update(dt)
-- update fsm and pass extra paremeter 'dt' to current_state execute method
self.fsm:update(dt)
end
function Game:handle_input(key)
if key == "Enter" then
-- change current state
self.fsm:change_state(self.states.gameplay)
end
end
return GameFSM(owner)
create new FSM object:
local fsm = FSM(owner):set_owner(owner)
passed owner of FSM. If owner is nil method raise try to set nil owner error
Methods for initialize FSM:
:set_current_state(state)
:set_previous_state(state)
:set_global_state(state)
set_current_state() and set_global_state() calls enter methods of passed state.
:update(...)
call this method to update FSM. You can pass variable numbers of arguments to update. Method executes the following sequence:
- if global state exist, call its
executemethod - same for the current state
:change_state(new_state)
method executes the following sequence:
- call the
exitmethod of the existing state - change current state to the new state
- call the
entermethod of the new state
This method can raise two errors:
current_state was nilif current sate not passed in FSMtrying to change to invalid stateif new state failed verification
:revent_to_previous_state()
change state back to the previous state
:is_in_state(state)
returns true if the current state is equal to the state passed as a parameter
Tests defined with busted test farmework. To run the suite, install busted and simply execute busted in the module directory.