基于Lua的State Pattern
代码来自于最近写的Pacman,更多请查看 – https://github.com/bennychen/Moai-based-Pacman
class.lua实现了在Lua中创建类的模拟,非常方便。class.lua参考自http://lua-users.org/wiki/SimpleLuaClasses
-- class.lua
-- Compatible with Lua 5.1 (not 5.0).
function class(base, init)
local c = {} -- a new class instance
if not init and type(base) == 'function' then
init = base
base = nil
elseif type(base) == 'table' then
-- our new class is a shallow copy of the base class!
for i,v in pairs(base) do
c[i] = v
end
c._base = base
end
-- the class will be the metatable for all its objects,
-- and they will look up their methods in it.
c.__index = c
-- expose a constructor which can be called by <classname>(<args>)
local mt = {}
mt.__call = function(class_tbl, ...)
local obj = {}
setmetatable(obj,c)
-- below 2 lines are updated based on the Comments from 'http://lua-users.org/wiki/SimpleLuaClasses'
-- if init then
-- init(obj,...)
if class_tbl.init then
class_tbl.init(obj,...)
else
-- make sure that any stuff from the base class is initialized!
if base and base.init then
base.init(obj, ...)
end
end
return obj
end
c.init = init
c.is_a = function(self, klass)
local m = getmetatable(self)
while m do
if m == klass then return true end
m = m._base
end
return false
end
setmetatable(c, mt)
return c
end
State基类,包含三个stub函数,enter()和exit()分别在进入和退出state时被执行,onUpdate()函数将会在state被激活时的每帧被执行。
require "class" State = class() function State:init( name ) self.name = name end function State:enter() end function State:onUpdate() end function State:exit() end
StateMachine类,该类集成了Moai的MOAIThread类。MOAIThread类似于Lua中的coroutine,但是在Moai中被yield的MOAIThread,会在game loop的每帧中被自动resume,见StateMachine:updateState函数,利用此特点,来实现每帧执行State:onUpdate函数。
require "State" StateMachine = class() function StateMachine:init() self.currentState = nil self.lastState = nil end function StateMachine:run() if ( self.mainThread == nil ) then self.mainThread = MOAIThread.new() self.mainThread:run( self.updateState, self ) end end function StateMachine:stop() if ( self.mainThread ) then self.mainThread:stop() end end function StateMachine:setCurrentState( state ) if ( state and state:is_a( State ) ) then if ( state == self.currentState ) then print( "WARNING @ StateMachine::setCurrentState - " .. "var state [" .. state.name .. "] is the same as current state" ) return end self.lastState = self.currentState self.currentState = state if ( self.lastState ) then print( "exiting state [" .. self.lastState.name .. "]" ) self.lastState:exit() end print( "entering state [" .. self.currentState.name .. "]" ) self.currentState:enter() else print( "ERROR @ StateMachine::setCurrentState - " .. "var [state] is not a class type of State" ) end end function StateMachine:updateState() while ( true ) do if ( self.currentState ~= nil ) then self.currentState:onUpdate() end coroutine.yield() end end
如何利用State和StateMachine类的示例,首先定义两个state。
SampleState.lua
require "State" State1 = class( State ) function State1:init() State.init( self, "State1" ) end function State1:enter() self.i = 0 end function State1:exit() self.i = 0 end function State1:onUpdate() print( self.name .. " is updated" ) self.i = self.i + 1 print( "self.i=" .. self.i ) if ( self.i == 10 ) then print( state2 ) SM:setCurrentState( state2 ) self.i = 0 end end ----------------------- State2 = class( State ) function State2:init() State.init( self, "State2" ) end function State2:onUpdate() print( "State2 is updated" ) end
test.lua
require "StateMachine" require "SampleState" SM = StateMachine() SM:run() state1 = State1() state2 = State2() SM:setCurrentState( state1 )


