狀態機制可以讓我們的代碼方便簡單,容易維護。
如果我們需要做一款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)