如果让一个类拥有状态机,主要有两步:
1.创建状态机对象
2.初始化状态机,主要包括事件和回调函数
1.创建状态机组件(对象)
self.fsm = {} cc.GameObject.extend(self.fsm):addComponent("components.behavior.StateMachine"):exportMethods()
2.初始化状态机(设置状态逻辑)
设置状态逻辑是重写setupState方法,其中有以下几个字段参数,
initial:状态机的初始状态
terminal (final):结束状态
events:状态发生转变时对应的事件
callbacks:发生转变时的回调函数
一般我们会设置initial,events和callbacks这三个。
events:
在events中需要分清楚“事件”和“状态”,events采用table结构,例如我们来写一个
events = { {name = "move", from = {"idle", "jump"}, to = "walk"},}
这其中move是事件,就像触摸事件event.name那样,name表示事件名称,而from和to后面跟的idle,jump,walk表示状态。
所以上面的意思就是:当执行move事件时,如果状态是idle或者jump,那么都会跳转到walk状态上。
from的状态可以是单一状态,也可以是集合状态,但to的状态必须唯一。
例如:
events = { {name = "move", from = {"idle", "jump"}, to = "walk"}, {name = "attack", from = {"idle", "walk"}, to = "jump"}, {name = "normal", from = {"walk", "jump"}, to = "idle"}, } 这里如果是normal事件,不管主角在走路walk还是跳跃jump,都会变成闲置idle状态。其他同理。
callbacks:
即所谓回调了,就是事件触发,会执行一系列的函数。
onbeforeEVNET: 在事件EVENT开始前被激活
onleaveSTATE: 在离开旧状态STATE时被激活
onenterSTATE 或 onSTATE:在进入新状态STATE时被激活
onafterEVENT 或 onEVENT:在事件EVENT结束后被激活
例如:
callbacks = { onenteridle = function () --或者 onidle print("idle") end, },
此外还有5种通用型的回调来捕获所有事件和状态的变化:
onbeforeevent: 在任何事件开始前被激活
onleavestate: 在离开任何状态时被激活
onenterstate:在进入任何状态时被激活
onafterevent :在任何事件结束后被激活
onchangestate :当状态发生改变的时候被激活
这里面的名称是不可以修改的,它是针对于任何事件和任何状态的
最后,就是调用这些事件了,通过self.fsm:doEvent(event)就可以了,参数event对应events参数名称。
还有如下api方法:
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, ...) :强制对当前状态进行转换
示例演示:
创建一个Player类,为其添加一个状态机
local Player = class("Player", function () return display.newSprite("icon.png") end) function Player:ctor() self:addStateMachine() end function Player:doEvent(event) self.fsm:doEvent(event) end function Player:addStateMachine() self.fsm = {} cc.GameObject.extend(self.fsm):addComponent("components.behavior.StateMachine"):exportMethods() self.fsm:setupState({ initial = "idle", events = { {name = "move", from = {"idle", "jump"}, to = "walk"}, {name = "attack", from = {"idle", "walk"}, to = "jump"}, {name = "normal", from = {"walk", "jump"}, to = "idle"}, }, callbacks = { onenteridle = function () local scale = CCScaleBy:create(0.2, 1.2) self:runAction(CCRepeat:create(transition.sequence({scale, scale:reverse()}), 2)) end, onenterwalk = function () local move = CCMoveBy:create(0.2, ccp(100, 0)) self:runAction(CCRepeat:create(transition.sequence({move, move:reverse()}), 2)) end, onenterjump = function () local jump = CCJumpBy:create(0.5, ccp(0, 0), 100, 2) self:runAction(jump) end, }, }) end return Player
为Player添加一个doEvent函数,调用状态机中doEvent。
MyScene.lua中的调用
local Player = import("..views.Player") local MyScene = class("MyScene", function () return display.newScene("MyScene") end) function MyScene:ctor() local player = Player.new() player:setPosition(display.cx, display.cy) self:addChild(player) local function menuCallback(tag) if tag == 1 then player:doEvent("normal") elseif tag == 2 then player:doEvent("move") elseif tag == 3 then player:doEvent("attack") end end local mormalItem = ui.newTTFLabelMenuItem({text = "normal", x = display.width*0.3, y = display.height*0.2, listener = menuCallback, tag = 1}) local moveItem = ui.newTTFLabelMenuItem({text = "move", x = display.width*0.5, y = display.height*0.2, listener = menuCallback, tag = 2}) local attackItem = ui.newTTFLabelMenuItem({text = "attack", x = display.width*0.7, y = display.height*0.2, listener = menuCallback, tag = 3}) local menu = ui.newMenu({mormalItem, moveItem, attackItem}) self:addChild(menu) end return MyScene