lua版本的一个状态机

发布时间:2017-7-9 7:19:10编辑:www.fx114.net 分享查询网我要评论
本篇文章主要介绍了"lua版本的一个状态机 ",主要涉及到lua版本的一个状态机 方面的内容,对于lua版本的一个状态机 感兴趣的同学可以参考一下。

-- 不实现。该模式定义不良好。多个嵌套的子并行状态相对复杂,流程不清晰,数据结构也不直接。 -- 还没找到比较良好的并行状态语义。 -- only one substate can run at one time by default singleman = "singleman", -- the state will be enabled always, substate in parallel region can't transit to none-parallel region parallel = "parallel", } return sc_enum

-- Date 2016-7-13
-- yanfei

--[[
    一个sc_fsm也是一个sc_state,状态机本身也可以作为一个状态,但目前子状态机只能视为状态,未能作为独立的机制运行
    sc_fsm
    {
        type2states         -- save all states accord to their name in a dictionary
    }
--]]
sc_helper = require("fsm.sc_helper")
require("utils.sc_list")
local sc_queue = require("utils.sc_queue")
local sc_enum = require("fsm.sc_enum")
local sc_state = require("fsm.sc_state")
local classprototype = require("utils.middleclass")
-- a fsm is a state 
local fsm = classprototype("sc_fsm", sc_state)

-- 建立类型-状态表
local function buildstates(fsm, astate)
    fsm.type2states[astate.statename] = astate
    if astate.substates then 
        for statename_, state_ in ipairs(astate.substates) do
            assert(fsm.type2states[state_.statename] == nil, string.format("[%s]: has a state named [%s] already!", fsm.statename, state_.statename))
            buildstates(fsm, state_)
        end
    end
end

-- 重载initialize
-- @statename_      状态名字
-- @basestate_      父状态
-- @statemode_      运行模式
-- @operation_      激活操作
function fsm:initialize(statename_, basestate_, statemode_, operation_)
    sc_state.initialize(self, statename_, basestate_, statemode_, operation_)
end

function fsm:init(initstatename)
    assert(type(self) == 'table', "Make sure that you are using 'fsm:init' instead of 'fsm.init'")

    self.type2states = {}
    buildstates(self, self)

    local initstate = self.type2states[initstatename]
    assert(initstate and initstate:isInstanceOf(sc_state), string.format("[%s] hasn't state named [%s]", self.statename, initstatename))

    -- 建立主激活列表
    local astate = initstate
    while astate do
        astate.activesubstate = astate.substates and astate.substates[1] or nil
        self.ineststate = astate.activesubstate and astate.activesubstate or astate
        astate = astate.activesubstate
    end

    astate = initstate
    while astate ~= self do
        astate.basestate.activesubstate = astate
        astate = astate.basestate
    end

    astate = self
    while astate.activesubstate ~= nil do
        if astate.activesubstate.enter then astate.activesubstate:enter(self) end
        astate = astate.activesubstate
    end

    if not self.eventqueue then self.eventqueue = sc_queue:newqueue() end
    self.suspend = false
    self.__lockqueue = false
    self.__transiting = false

    return true
end

-- 跳转到目标状态
function fsm:transit( targetname_ )
    assert(type(self) == 'table', "Make sure that you are using 'fsm:transit' instead of 'fsm.transit'")

    if self.__transiting then
        self.__transiting = false
        return sc_enum.none
    end

    self.__transiting = true;

    local targetstate = self.type2states[targetname_]
    assert(targetstate and targetstate:isInstanceOf(sc_state), string.format("[%s] hasn't state named [%s]", self.statename, targetname_))

    -- detect the whole active state list
    local astate = self.ineststate
    -- 
    local bstate = targetstate
    local dis = astate.depth - bstate.depth
    if dis < 0 then 
        bstate, astate = astate, targetstate
    end -- bstate is topper

    dis = math.abs(dis)
    for i = 1, dis do astate = astate.basestate end --now they are both same depth

    if astate == bstate then -- is family
        self.__transiting = false
        return sc_enum.none
    end

    -- find  nearest ancestor both
    while astate ~= bstate do
        astate, bstate = astate.basestate, bstate.basestate
    end

    -- first we will call exit chain
    while self.ineststate ~= astate do
        if self.ineststate.exit then self.ineststate:exit(self) end
        self.ineststate = self.ineststate.basestate
    end

    -- now we will decide the enter chain

     -- build active chain down side
    local astate = targetstate
    while astate do
        astate.activesubstate =  (astate.substates and astate.statemode == sc_enum.shallow) 
                                 and astate.substates[1] or astate.activesubstate
        self.ineststate = astate.activesubstate and astate.activesubstate or astate
        astate = astate.activesubstate
    end

    -- build active chain up side
    astate = targetstate
    while astate ~= self do
        astate.basestate.activesubstate, astate = astate, astate.basestate
    end

    -- call enter chain
    while bstate.activesubstate ~= nil do
        if bstate.activesubstate.enter then bstate.activesubstate:enter(self) end
        bstate = bstate.activesubstate
    end

    return sc_enum.none
end

function fsm:process_event( eventname_, ...)
    assert(type(self) == 'table', "Make sure that you are using 'fsm:process_event' instead of 'fsm.process_event'")
    if self.suspend then return nil end
    if self.__lockqueue then return nil end
    
    local pevent = {...}
    pevent.eventname = eventname_
    if not self.eventqueue then self.eventqueue = sc_queue:newqueue() end
    local queue = self.eventqueue
    queue:push_back(pevent)

    self.__lockqueue = true
    --local eventcount = queue:count()
    while true do
        --eventcount = eventcount - 1
        local astate = self
        local processflag_ = sc_enum.forward
        pevent = queue:pop_front() 
        if not pevent then break end
        while astate.activesubstate ~= nil do
            processflag_ = astate.activesubstate:_internal_process(self, pevent)
            if processflag_ == sc_enum.none then 
                return processflag_ 
           end
           astate = astate.activesubstate
        end
    end

    self.__lockqueue = false

    return processflag_
end

 -- 
function fsm:post_event( eventname_, ... )
    local pevent = {...}
    pevent.eventname = eventname_
    self.eventqueue:push_back(pevent)
end

function fsm:context( statename_ )
    local targetstate = self.type2states[targetname_]
    --assert(targetstate and targetstate:isInstanceOf(sc_state), string.format("[%s] hasn't state named [%s]", self.statename, targetname_))
    return targetstate
end

return fsm
-- Date 2016-7-13
-- yanfei

--[[
--  sc_state
--  {
        statename,          -- 状态名
        basestate,          -- 父状态
        statemode,          -- 历史模式
        operation,          -- 操作,完成并行,筛选。目前保留字段

        substates,          -- 子状态列表
        activestate,        -- 当前激活状态

        events,,            -- event-callback表
--  }
--  一个状态是一个节点,basestate指向父状态,substates指向子状态列表,所有状态形成一棵树
--]]

require("utils.sc_list")
local sc_enum = require("fsm.sc_enum")
--require("fsm.sc_event")
local classprototype = require("utils.middleclass")
local sc_state = classprototype("sc_state")

local rawget = rawget
local rawset = rawset
local string = string

-- 重载initialize
-- @statename_      状态名字
-- @basestate_      父状态
-- @statemode_      运行模式
-- @operation_      激活操作
function sc_state:initialize(statename_, basestate_, statemode_, operation_)
    assert(type(statename_) == "string","statename must be a string")
    self.statename = statename_
    self.statemode = statemode_ or sc_enum.shallow
    --self.operation = operation_ or sc_enum.singleman

    -- 处理父子关系
    assert(basestate_ and basestate_:isInstanceOf(sc_state) or basestate_ == nil, string.format("[%s]: base state must be a sc_state", statename_))
    self.basestate = basestate_
    if basestate_ then basestate_:addsubstate(self) end

    -- 状态深度 top state is 1
    self.depth = self.basestate and self.basestate.depth + 1 or 1

end

-- 创建该状态的子状态
function sc_state:createstate(statename_, statemode_, operation_)
    return sc_state:new(statename_, self, statemode_, operation_)
end

-- 添加子状态
function sc_state:addsubstate(...)
    -- 填充子状态列表
    self.substates = self.substates or {}
    for i, state_ in ipairs{...} do 
        assert(state_:isInstanceOf(sc_state), "substate must be a sc_state")
        assert(self.substates[state_.statename] == nil, string.format("%s has a substate named [%s] already!", self.statename, state_.statename))
        table.insert(self.substates, state_)
    end
end

-- 绑定事件
-- @eventname_  事件名
-- @callbackname_   事件处理函数名
function sc_state:bind_event(eventname_, callbackname_)
    assert(type(self) == 'table', "Make sure that you are using 'sc_state:process_event' instead of 'sc_state.process_event'")
    assert(type(eventname_) == 'string', "Make sure that callbakname is a string")
    assert(type(callbackname_) == 'string', "Make sure that callbakname is a string")
    --local callback_ =  self[callbackname_]
    local callback_ = rawget(self, callbackname_)
    assert(callback_, string.format("[%s] bind_event event[%s]: hasn't a function named [%s]", self.statename, eventname_, callbackname_))
    assert(type(callback_) == "function", string.format("[%s]:bind_event event[%s]: callback must be a function", self.statename, eventname_))

    self.events = self.events or {}
    self.events[eventname_] = function(fsm, ...) callback_(self, fsm, ...) end
end

function sc_state:_internal_process(fsm, pevent)
    if not self.events then return sc_enum.forward end
    local callback_ = self.events[pevent.eventname]
    if not callback_ then return sc_enum.forward end
    return callback_(fsm, unpack(pevent))
end

return sc_state

使用

-- 只是一个例子

local sc_enum = require("fsm.sc_enum")
local sc_fsm_pt = require("fsm.sc_fsm")

local gamemain_fsm = sc_fsm_pt:new("gamemain_fsm")

local print = print

--[[
--  gamemain_state
--]]
local gamemain_state = gamemain_fsm:createstate("gamemain_state")
function gamemain_state:enter(fsm)
    print("enter "..self.statename)
end

function gamemain_state:exit(fsm)
    print("exit "..self.statename)
end

function gamemain_state:update(fsm, delta)
    print("update "..self.statename)
    print("timedelta: "..tostring(delta))
end

gamemain_state:bind_event("game_update", "update")

--[[
--  gamemain_init_state
--]]
local gamemain_init_state = gamemain_state:createstate("gamemain_init_state")

function gamemain_init_state:enter(fsm)
    print("enter "..self.statename)
end

function gamemain_init_state:exit(fsm)
    print("exit "..self.statename)
end

--[[
--  gamemain_init_waiting_state
--]]
local gamemain_init_waiting_state = gamemain_init_state:createstate("gamemain_init_waiting_state")

function gamemain_init_waiting_state:enter(fsm)
    print("enter "..self.statename)
end

function gamemain_init_waiting_state:exit(fsm)
    print("exit "..self.statename)
end


--[[
--  gamemain_idle_state
--]]
local gamemain_idle_state = gamemain_state:createstate("gamemain_idle_state")

function gamemain_idle_state:enter(fsm)
    print("enter "..self.statename)
end

function gamemain_idle_state:exit(fsm)
    print("exit "..self.statename)
end

function gamemain_idle_state:update(fsm, delta)
    print("update "..self.statename)
    print("timedelta: "..tostring(delta))
    --fsm:process_event("game_upadte", 0.0001)
end

function gamemain_idle_state:tolobby(fsm, msg1, msg2)
    print(msg1..' '..msg2)
    print("ready to taransit to lobby") 
    return fsm:transit("gamemain_lobby_state")
end

gamemain_idle_state:bind_event("game_update", "update")
gamemain_idle_state:bind_event("event_tolobby", "tolobby")
--[[
--  gamemain_net_state
--]]
local gamemain_net_state = gamemain_state:createstate("gamemain_net_state")
function gamemain_net_state:enter(fsm)
    print("enter "..self.statename)
end

function gamemain_net_state:exit(fsm)
    print("exit "..self.statename)
end


--[[
--  gamemain_net_send_state
--]]
local gamemain_net_send_state = gamemain_net_state:createstate("gamemain_net_send_state")
function gamemain_net_send_state:enter(fsm)
    print("enter "..self.statename)
end

function gamemain_net_send_state:exit(fsm)
    print("exit "..self.statename)
end


--[[
--  gamemain_net_receive_state
--]]
local gamemain_net_receive_state = gamemain_net_state:createstate("gamemain_net_receive_state")
function gamemain_net_receive_state:enter(fsm)
    print("enter "..self.statename)
end

function gamemain_net_receive_state:exit(fsm)
    print("exit "..self.statename)
end




--[[
--  gamemain_lobby_state
--]]
local gamemain_lobby_state = gamemain_state:createstate("gamemain_lobby_state")
function gamemain_lobby_state:enter(fsm)
    print("enter "..self.statename)
end

function gamemain_lobby_state:exit(fsm)
    print("exit "..self.statename)
end


--[[
--  gamemain_lobby_shop_state
--]]
local gamemain_lobby_shop_state = gamemain_lobby_state:createstate("gamemain_lobby_shop_state")
function gamemain_lobby_shop_state:enter(fsm)
    print("enter "..self.statename)
    fsm:post_event("event_opendoor", "opendoor")  
end

function gamemain_lobby_shop_state:exit(fsm)
    print("exit "..self.statename)
end

function gamemain_lobby_shop_state:update(fsm, delta)
    print("update "..self.statename)
    print("timedelta: "..tostring(delta))
end

function gamemain_lobby_shop_state:opendoor(fsm)
    print("open the door")
end

gamemain_lobby_shop_state:bind_event("game_update", "update")
gamemain_lobby_shop_state:bind_event("event_opendoor", "opendoor")

--[[
--  gamemain_lobby_dungeon_state
--]]
local gamemain_lobby_dungeon_state = gamemain_lobby_state:createstate("gamemain_lobby_dungeon_state")
function gamemain_lobby_dungeon_state:enter(fsm)
    print("enter "..self.statename)
end

function gamemain_lobby_dungeon_state:exit(fsm)
    print("exit "..self.statename)
end

return gamemain_fsm
--region *.lua
--Date 

sc_helper = require("fsm.sc_helper")
local sc_event = require("fsm.sc_event")
local queue = require("utils.sc_queue")

-- 获取定义好的gamemain状态机
local gamemain_fsm = require("game.gamemain_fsm")
-- 打印状态树
sc_helper:printstates(gamemain_fsm)

-- 初始化状态机,初始状态为gamemain_init_state,进入gamemain_init_state时将自动进入其子状态gamemain_init_waiting_state
-- 进入某个状态后,如果有子状态,会尝试进入其子状态
gamemain_fsm:init("gamemain_init_state")

-- 自顶向下打印当前激活的状态链
sc_helper:printactivestates(gamemain_fsm)

-- 状态迁移到gamemain_idle_state
gamemain_fsm:transit("gamemain_idle_state")

sc_helper:printactivestates(gamemain_fsm)

-- 发送事件game_update, 在gamemain_idle_state中注册了对该事件的响应
gamemain_fsm:process_event("game_update", 0.0333333)
-- 发送事件event_tolobby,在gamemain_idle_state中注册了对该事件的响应。在响应中将调用gamemain_fsm:transit迁移到gamemain_lobby_state
gamemain_fsm:process_event("event_tolobby", "hello", "world")
sc_helper:printactivestates(gamemain_fsm)
gamemain_fsm:process_event("game_update", 0.0333333)

print("over")
--endregion


上一篇:wireshark 学习 2
下一篇:Android Studio 通过 git update 或者 pull 的时候出错及解决办法

相关文章

相关评论

本站评论功能暂时取消,后续此功能例行通知。

一、不得利用本站危害国家安全、泄露国家秘密,不得侵犯国家社会集体的和公民的合法权益,不得利用本站制作、复制和传播不法有害信息!

二、互相尊重,对自己的言论和行为负责。

最近更新

好贷网好贷款