貌似从3.4时代就没有了 也许是3.6时代framework删了很多东西
但是状态机这么经典的组件怎么能没有呢
废话少说 翻出原来的代码
模仿3.10组件event改进
I:\flipCard\simulator\win32\src\cocos\framework\init.lua
增加
cc.register("event", require("cocos.framework.components.event"))--3.10框架自带
cc.register("StateMachine", require("cocos.framework.components.StateMachine"))--新增注册组件
修改后的状态机代码
--local Component = import("..Component")
local StateMachine = class("StateMachine")
local EXPORTED_METHODS = {
"setupState",
"isReady",
"getState",
"isState",
"canDoEvent",
"cannotDoEvent",
"isFinishedState",
"doEventForce",
"doEvent",
}
--[[--
port from Javascript State Machine Library
https://github.com/jakesgordon/javascript-state-machine
JS Version: 2.2.0
]]
StateMachine.VERSION = "2.2.0"
-- the event transitioned successfully from one state to another
StateMachine.SUCCEEDED = 1
-- the event was successfull but no state transition was necessary
StateMachine.NOTRANSITION = 2
-- the event was cancelled by the caller in a beforeEvent callback
StateMachine.CANCELLED = 3
-- the event is asynchronous and the caller is in control of when the transition occurs
StateMachine.PENDING = 4
-- the event was failure
StateMachine.FAILURE = 5
-- caller tried to fire an event that was innapropriate in the current state
StateMachine.INVALID_TRANSITION_ERROR = "INVALID_TRANSITION_ERROR"
-- caller tried to fire an event while an async transition was still pending
StateMachine.PENDING_TRANSITION_ERROR = "PENDING_TRANSITION_ERROR"
-- caller provided callback function threw an exception
StateMachine.INVALID_CALLBACK_ERROR = "INVALID_CALLBACK_ERROR"
StateMachine.WILDCARD = "*"
StateMachine.ASYNC = "ASYNC"
-- function StateMachine:ctor()
-- StateMachine.super.ctor(self, "StateMachine")
-- end
function StateMachine:init_()
self.target_ = nil
end
function StateMachine:bind(target)
self:init_()
cc.setmethods(target, self, EXPORTED_METHODS)
self.target_ = target
end
function StateMachine:unbind(target)
cc.unsetmethods(target, EXPORTED_METHODS)
self:init_()
end
function StateMachine:setupState(cfg)
assert(type(cfg) == "table", "StateMachine:ctor() - invalid config")
-- cfg.initial allow for a simple string,
-- or an table with { state = "foo", event = "setup", defer = true|false }
if type(cfg.initial) == "string" then
self.initial_ = {state = cfg.initial}
else
self.initial_ = clone(cfg.initial)
end
self.terminal_ = cfg.terminal or cfg.final
self.events_ = cfg.events or {}
self.callbacks_ = cfg.callbacks or {}
self.map_ = {}
self.current_ = "none"
self.inTransition_ = false
if self.initial_ then
self.initial_.event = self.initial_.event or "startup"
self:addEvent_({name = self.initial_.event, from = "none", to = self.initial_.state})
end
for _, event in ipairs(self.events_) do
self:addEvent_(event)
end
if self.initial_ and not self.initial_.defer then
self:doEvent(self.initial_.event)
end
return self.target_
end
--返回状态机是否就绪
function StateMachine:isReady()
return self.current_ ~= "none"
end
--返回当前状态
function StateMachine:getState()
return self.current_
end
--判断当前状态是否是参数state状态
function StateMachine:isState(state)
if type(state) == "table" then
for _, s in ipairs(state) do
if s == self.current_ then return true end
end
return false
else
return self.current_ == state
end
end
--当前状态如果能完成eventName对应的event的状态转换,则返回true
function StateMachine:canDoEvent(eventName)
return not self.inTransition_
and (self.map_[eventName][self.current_] ~= nil or self.map_[eventName][StateMachine.WILDCARD] ~= nil)
end
--当前状态如果不能完成eventName对应的event的状态转换,则返回true
function StateMachine:cannotDoEvent(eventName)
return not self:canDoEvent(eventName)
end
--当前状态如果是最终状态,则返回true
function StateMachine:isFinishedState()
return self:isState(self.terminal_)
end
--强制对当前状态进行转换
function StateMachine:doEventForce(name, ...)
local from = self.current_
local map = self.map_[name]
local to = (map[from] or map[StateMachine.WILDCARD]) or from
local args = {...}
local event = {
name = name,
from = from,
to = to,
args = args,
}
if self.inTransition_ then self.inTransition_ = false end
self:beforeEvent_(event)
if from == to then
self:afterEvent_(event)
return StateMachine.NOTRANSITION
end
self.current_ = to
self:enterState_(event)
self:changeState_(event)
self:afterEvent_(event)
return StateMachine.SUCCEEDED
end
function StateMachine:doEvent(name, ...)
assert(self.map_[name] ~= nil, string.format("StateMachine:doEvent() - invalid event %s", tostring(name)))
local from = self.current_
local map = self.map_[name]
local to = (map[from] or map[StateMachine.WILDCARD]) or from
local args = {...}
local event = {
name = name,
from = from,
to = to,
args = args,
}
if self.inTransition_ then
self:onError_(event,
StateMachine.PENDING_TRANSITION_ERROR,
"event " .. name .. " inappropriate because previous transition did not complete")
return StateMachine.FAILURE
end
if self:cannotDoEvent(name) then
self:onError_(event,
StateMachine.INVALID_TRANSITION_ERROR,
"event " .. name .. " inappropriate in current state " .. self.current_)
return StateMachine.FAILURE
end
if self:beforeEvent_(event) == false then
return StateMachine.CANCELLED
end
if from == to then
self:afterEvent_(event)
return StateMachine.NOTRANSITION
end
event.transition = function()
self.inTransition_ = false
self.current_ = to -- this method should only ever be called once
self:enterState_(event)
self:changeState_(event)
self:afterEvent_(event)
return StateMachine.SUCCEEDED
end
event.cancel = function()
-- provide a way for caller to cancel async transition if desired
event.transition = nil
self:afterEvent_(event)
end
self.inTransition_ = true
local leave = self:leaveState_(event)
if leave == false then
event.transition = nil
event.cancel = nil
self.inTransition_ = false
return StateMachine.CANCELLED
elseif string.upper(tostring(leave)) == StateMachine.ASYNC then
return StateMachine.PENDING
else
-- need to check in case user manually called transition()
-- but forgot to return StateMachine.ASYNC
if event.transition then
return event.transition()
else
self.inTransition_ = false
end
end
end
-- function StateMachine:exportMethods()
-- self:exportMethods_({
-- "setupState",
-- "isReady",
-- "getState",
-- "isState",
-- "canDoEvent",
-- "cannotDoEvent",
-- "isFinishedState",
-- "doEventForce",
-- "doEvent",
-- })
-- return self.target_
-- end
function StateMachine:onBind_()
end
function StateMachine:onUnbind_()
end
function StateMachine:addEvent_(event)
local from = {}
if type(event.from) == "table" then
for _, name in ipairs(event.from) do
from[name] = true
end
elseif event.from then
from[event.from] = true
else
-- allow "wildcard" transition if "from" is not specified
from[StateMachine.WILDCARD] = true
end
self.map_[event.name] = self.map_[event.name] or {}
local map = self.map_[event.name]
for fromName, _ in pairs(from) do
map[fromName] = event.to or fromName
end
end
local function doCallback_(callback, event)
if callback then return callback(event) end
end
-- 在任何事件开始前被激活
function StateMachine:beforeAnyEvent_(event)
return doCallback_(self.callbacks_["onbeforeevent"], event)
end
-- 在任何事件结束后被激活
function StateMachine:afterAnyEvent_(event)
return doCallback_(self.callbacks_["onafterevent"] or self.callbacks_["onevent"], event)
end
-- 在离开任何状态时被激活
function StateMachine:leaveAnyState_(event)
return doCallback_(self.callbacks_["onleavestate"], event)
end
-- 在进入任何状态时被激活
function StateMachine:enterAnyState_(event)
return doCallback_(self.callbacks_["onenterstate"] or self.callbacks_["onstate"], event)
end
-- 当状态发生改变的时候被激活
function StateMachine:changeState_(event)
return doCallback_(self.callbacks_["onchangestate"], event)
end
--在事件EVENT开始前被激活
function StateMachine:beforeThisEvent_(event)
return doCallback_(self.callbacks_["onbefore" .. event.name], event)
end
--在事件EVENT结束后被激活
function StateMachine:afterThisEvent_(event)
return doCallback_(self.callbacks_["onafter" .. event.name] or self.callbacks_["on" .. event.name], event)
end
--在离开旧状态STATE时被激活
function StateMachine:leaveThisState_(event)
return doCallback_(self.callbacks_["onleave" .. event.from], event)
end
--在进入新状态STATE时被激活
function StateMachine:enterThisState_(event)
return doCallback_(self.callbacks_["onenter" .. event.to] or self.callbacks_["on" .. event.to], event)
end
function StateMachine:beforeEvent_(event)
if self:beforeThisEvent_(event) == false or self:beforeAnyEvent_(event) == false then
return false
end
end
function StateMachine:afterEvent_(event)
self:afterThisEvent_(event)
self:afterAnyEvent_(event)
end
function StateMachine:leaveState_(event, transition)
local specific = self:leaveThisState_(event, transition)
local general = self:leaveAnyState_(event, transition)
if specific == false or general == false then
return false
elseif string.upper(tostring(specific)) == StateMachine.ASYNC
or string.upper(tostring(general)) == StateMachine.ASYNC then
return StateMachine.ASYNC
end
end
function StateMachine:enterState_(event)
self:enterThisState_(event)
self:enterAnyState_(event)
end
function StateMachine:onError_(event, error, message)
printf("%s [StateMachine] ERROR: error %s, event %s, from %s to %s", tostring(self.target_), tostring(error), event.name, event.from, event.to)
printError(message)
end
return StateMachine
使用样例 3.15版本 hello world 亲测可用
cc.bind(self.fsm, "StateMachine") --给表添加组件
local Player = class("Player", function ()
return display.newSprite("HelloWorld.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()
cc.bind(self.fsm, "StateMachine")
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
local Player = import(".Player")
local MainScene = class("MainScene", cc.load("mvc").ViewBase)
function MainScene:onCreate()
--cc.bind(self, "StateMachine")
--[[
-- add background image
display.newSprite("HelloWorld.png")
:move(display.center)
:addTo(self)
-- add HelloWorld label
cc.Label:createWithSystemFont("Hello World", "Arial", 40)
:move(display.cx, display.cy + 200)
:addTo(self)
]]
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)
local s_arialPath = "fonts/arial.ttf"
local label = cc.Label:createWithTTF("normal", s_arialPath, display.DEFAULT_TTF_FONT_SIZE)
label:setAnchorPoint(cc.p(0.5, 0.5))
local MenuItem = cc.MenuItemLabel:create(label)
MenuItem:registerScriptTapHandler(menuCallback)
MenuItem:setTag(1)
local Menu = cc.Menu:create()
Menu:addChild(MenuItem)
Menu:setPosition(0, 0)
MenuItem:setPosition(display.width*0.3, display.height*0.2)
self:addChild(Menu)
local label = cc.Label:createWithTTF("move", s_arialPath, display.DEFAULT_TTF_FONT_SIZE)
label:setAnchorPoint(cc.p(0.5, 0.5))
local MenuItem = cc.MenuItemLabel:create(label)
MenuItem:registerScriptTapHandler(menuCallback)
MenuItem:setTag(2)
local Menu = cc.Menu:create()
Menu:addChild(MenuItem)
Menu:setPosition(0, 0)
MenuItem:setPosition(display.width*0.5, display.height*0.2)
self:addChild(Menu)
local label = cc.Label:createWithTTF("attack", s_arialPath, display.DEFAULT_TTF_FONT_SIZE)
label:setAnchorPoint(cc.p(0.5, 0.5))
local MenuItem = cc.MenuItemLabel:create(label)
MenuItem:registerScriptTapHandler(menuCallback)
MenuItem:setTag(3)
local Menu = cc.Menu:create()
Menu:addChild(MenuItem)
Menu:setPosition(0, 0)
MenuItem:setPosition(display.width*0.7, display.height*0.2)
self:addChild(Menu)
end
return MainScene