cocos2d-lua 3.10 框架无经典的quick3.3状态机组件

貌似从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

最终演示效果.gif
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容