状态机制可以让我们的代码方便简单,容易维护。
如果我们需要做一款RPG游戏的话,游戏中的玩家都会有一些状态,比如:站立、
行走、奔跑、攻击、跳跃、死亡、复活等。
如果我们用条件语句去进行判断的话,代码量会比较庞大,难以维护而且不易阅读。
状态机的原理就是将这些状态进行归类,在不同的状态去执行不同的操作,给不同
的状态绑定不同的回调函数,在状态改变的时候去调用。
我们需要知道怎么样在一个类中去拥有一个状态机:
第一步:当然是需要创建一个状态机咯。
第二部:初始化状态机。就是对状态机的事件(状态)和回调函数(执行的操作)
进行初始化。
我们怎么样去创建一个状态机呢,接下来我们需要做的就是去实现一下:
创建状态机:
self.fsm_ = {}
cc.GameObject.extend(self.fsm_)
:addComponent("components.behavior.StateMachine")
:exportMethods()
这样呢我们就创建好了一个状态机对象。
初始化状态机:
self.fsm_:setupState( 参数)
初始化就是重新写setupState函数,参数:
initial:状态机的初始状态
terminal:状态机的结束状态
events:状态改变时对应的事件
callbacks:状态改变时的回调函数
evnets:
在这里我们需要将events中的“事件”跟状态分清楚,例如:
events = {
{name = "move", from = {"idle", "jump"}, to = "walk"},
}
在这个events中中的name:move是事件,from:idle,jump是状态,to:walk也是
状态,这个代码意思就是,当执行的是move事件的时候,如果状态(from)是jump
或者idle的时候,就会跳转到walk状态上。
from中的状态可以是单一的也可以是一个集合,但是to的状态是唯一的。
所以当我们需要写一个状态机的时候我们就得先想好角色有哪些状态,在什么事件,
会从什么状态改变到哪种状态去。例如:
defaultEvents = {
{name = "move", from = {"idle", "jump"}, to = "walk" },
{name = "attack", from = {"idle", "jump"}, to = "walk" },
{name = "normal", from = {"walk", "jump"}, to = "idle" },
}
--这句代码的意思是如果继承类提供了其他的事件,则合并
table.insertto(defaultEvents, checktable(events))
callbacks
重点就是callbacks参数了,也就是回调函数,就是在状态改变的时候执行的操作。
- onbeforeEVNET: 在事件EVENT开始前被激活
- onleaveSTATE: 在离开旧状态STATE时被激活
- onenterSTATE 或 onSTATE:在进入新状态STATE时被激活
- onafterEVENT 或 onEVENT:在事件EVENT结束后被激活
例如:
defaultCallbacks = {
onchangestate = handler(self, self.onChangeState_),
onenteridle = function() --或者idle
print("idle")
end,
},
--该代码的意思是,如果继承类提供了其他回调,则合并
table.merge(defaultCallbacks, checktable(callbacks))
此外还有5种通用型的回调来捕获所有事件和状态的变化:
- onbeforeevent: 在任何事件开始前被激活
- onleavestate: 在离开任何状态时被激活
- onenterstate:在进入任何状态时被激活
- onafterevent :在任何事件结束后被激活
- onchangestate :当状态发生改变的时候被激活
这里面的名称是不可以被修改的,它是针对任何事件和任何状态的。
最后呢,就是调用这些事件了,
self.fsm_:doEvent(event)
参数event对应events参数名称。此外还有以下函数:
- fsm:isReady() :返回状态机是否就绪
- fsm:getState() :返回当前状态
- fsm:isState(state) :判断当前状态是否是参数state状态
- fsm:canDoEvent(eventName) :当前状态如果能完成eventName对应的event的状态转换,则返回true
- fsm:cannotDoEvent(eventName) :当前状态如果不能完成eventName对应的event的状态转换,则返回true
- fsm:isFinishedState() :当前状态如果是最终状态,则返回true
- fsm:doEventForce(name, ...) :强制对当前状态进行转换
贴个quick的例子:
创建以及初始化:
self.fsm_ = {}
cc.GameObject.extend(self.fsm_)
:addComponent("components.behavior.StateMachine")
:exportMethods()
self.fsm_:setupState({
events = {
{name = "start", from = "none", to = "green" },
{name = "warn", from = "green", to = "yellow"},
{name = "panic", from = "green", to = "red" },
{name = "panic", from = "yellow", to = "red" },
{name = "calm", from = "red", to = "yellow"},
{name = "clear", from = "red", to = "green" },
{name = "clear", from = "yellow", to = "green" },
},
callbacks = {
onbeforestart = function(event) self:log("[FSM] STARTING UP") end,
onstart = function(event) self:log("[FSM] READY") end,
onbeforewarn = function(event) self:log("[FSM] START EVENT: warn!", true) end,
onbeforepanic = function(event) self:log("[FSM] START EVENT: panic!", true) end,
onbeforecalm = function(event) self:log("[FSM] START EVENT: calm!", true) end,
onbeforeclear = function(event) self:log("[FSM] START EVENT: clear!", true) end,
onwarn = function(event) self:log("[FSM] FINISH EVENT: warn!") end,
onpanic = function(event) self:log("[FSM] FINISH EVENT: panic!") end,
oncalm = function(event) self:log("[FSM] FINISH EVENT: calm!") end,
onclear = function(event) self:log("[FSM] FINISH EVENT: clear!") end,
onleavegreen = function(event) self:log("[FSM] LEAVE STATE: green") end,
onleaveyellow = function(event) self:log("[FSM] LEAVE STATE: yellow") end,
onleavered = function(event)
self:log("[FSM] LEAVE STATE: red")
self:pending(event, 3)
self:performWithDelay(function()
self:pending(event, 2)
self:performWithDelay(function()
self:pending(event, 1)
self:performWithDelay(function()
self.pendingLabel_:setString("")
event.transition()
end, 1)
end, 1)
end, 1)
return "async"
end,
ongreen = function(event) self:log("[FSM] ENTER STATE: green") end,
onyellow = function(event) self:log("[FSM] ENTER STATE: yellow") end,
onred = function(event) self:log("[FSM] ENTER STATE: red") end,
onchangestate = function(event) self:log("[FSM] CHANGED STATE: " .. event.from .. " to " .. event.to) end,
},
})
self.fsm__:doEvent("start") -- 启动状态机
调用(doevent函数):
self.clearButton_ =
cc.ui.UIPushButton.new()
:setButtonLabel(cc.ui.UILabel.new({text = "clear", size = 32, color = display.COLOR_BLACK}))
:onButtonClicked(function()
if self.fsm_:canDoEvent("clear") then
self.fsm_:doEvent("clear")
end
end)
:align(display.CENTER, display.cx - 150, display.top - 540)
:addTo(self)