cocos 2dxcocos2d-Lua

cocos2d-lua 3.10 框架无经典的quick3.3状

2017-07-14  本文已影响65人  人气小哥

貌似从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
上一篇下一篇

猜你喜欢

热点阅读