lua面向对象与基于lua的状态机实现

来源:互联网 发布:课程表排课软件 编辑:程序博客网 时间:2024/05/30 05:03

之前一篇文章只是简单的介绍lua的面向对象, 对这方面的理解还很浅,最近在游戏中实现了一个状态机, 这里简单介绍;

思路介绍

  1. 状态有很多,需要有扩展性, 所以这里的状态有独立的类结构, 同时状态机作为一个功能要让每个角色对象身上都拥有自己的状态机, 因此也是一个类;
  2. 状态机里包含以唯一名字标记对应的状态, 由于状态对于一个游戏角色的行为来说, 可以看作是底层实现, 所以一些公共状态可以被; 状态之间的转换也通过一个类来实现, 这样 状态机就是有不同的状态实例, 状态转换实例组合而成, 这样做的目的自然是为了让状态机有更强的适应性, 针对不同的场景, 可以共用之前实现的状态, 由状态转换实例来决定状态机的运行;

实现代码

行为状态机ASM

 #有限状态机ActionStateMechaine = {}function ActionStateMechaine:new(o)  o = o or {}  setmetatable(o,self)  self.__index = self  self._curState = nil  self._nextstate = nil  self._ASM = nil  # 包含该状态的状态机  self._states = nil   #保存当前状态机中的状态实例  self._transition  = nil #保存状态转换实例  --other attributes...

注意:上面的_states, _transition是用table保存实例对象, 但这里一定要为nil, 因为如果这里写成了空表如self._states={} 则后面的对states的保存和查找都是在这个表中进行, 那么就会出现所有状态机ASM实例都共享这个table, 即所有ASM有相同的_states表, 所以可以认为基类中的非空table 是类似static成员变量的功能, 类似python中的子类的list在基类中的功能, 浅拷贝问题;

状态机成员函数:

注意: 所有的成员函数必须如python一样手动加上self, 虽然可以有自动self功能, 但这里如果省略self ,则后面继承时会出现错误, 因为子类调用该函数时用到的self还是基类的self, 数据也是基类的数据; (这个坑我花了不少时间解决)

function ActionStateMechaine.ContainState(self, statename)  return self._staes[statename] ~= nilend

下面的成员函数就是用于添加转换实例和状态实例的:

function ActionStateMechaine.AddTranstion(self, fromname, toname,condTrans)  self._tansition = self._transition or {}  if self:COntainState(fromname) and self:ContainState(toname) then    if not self._transition[fromname] then      self._transition[fromname] = {}    end    self._transition[fromname][toname] = conTrans  end -- 状态function ActionStateMechaine.AddState(self, state)  asert(state)  self._states = self._state or {}  self._states[state._name] = staet  state._ASM = selfend

状态机的更新函数:

function ActionStateMechaine.Update(self,obj)  if self._crestate == nil and self._nextstate then    self._crestate = self._nextstate    self.nextstate=nil  end  -- 每次循环调用状态的执行函数  if self._curstate then    self._curstate:run(obj)  else    error("not found a state to fun!")    return false  end  -- get the next state based on the returnd result and priority of each statetrans  if self._transition[self._curstate._name] then    nextstate = nil; nextStaLvl = -1;    nxtSts = self._transition[self._curstate._name]    for nxtname in pairs(nxtSts) do      -- function Judge: condition is passed and the priority is higher      local bpassed, priLvl = nextstates[nxtname].Judge(obj)      if bpassed and priLvl > nextStaLvl then        nextStaLvl = self._states[nxtname]._lvl        nextState = self._states[nxtname]      end    end    self._nextstate = nextState  end  self._curstate = nil  return trueend

当然这里的nextstate其实没什么太大的作用, 不过先留着以防以后会有更多的内容添加;

小结: 上面就是状态机的主要结构, 主要提供了一个框架的东西在这, 里面的内容(state, conTransition)需要具体的去实现;


状态类

前面的ASM只是一个空壳, 现在要实现各种不同的state去填充ASM; 所有的state都将继承与一个ActionState类, 并封装在模块内,这样对于后面可以对不同种类的状态好管理;

 -- state base classActionState = {}function ActionState:new(o)  o = o or {}  setmetatable(o,self)  self.__index = self  self._name = o._name or "base"   -- the identify name for each state  self._ASM = nil   -- other attributes...  -- the default running function of each state  setf.run = function()    print("call base state run(doing nothing..)")  end  return oend -- other member function can alse defined below.... ....

基于上面的基类, 后面各种具体状态可以添加一些自己的属性, 同时必须实现一个run执行函数, 否则就会调用基类的run函数;

下面给出我本人的几个状态, 这里为每个状态直接通过一个函数来创建, 最后通过一个相同的接口来提供, 这样做一是简便,而是封装:

 -- 几个状态实现类 --wander state local function Create_Wander()   local state_wander = ActionState{_name="wander"}   function state_wander.run(self,obj)     obj.wander()   end   return state_wander end -- other state implementation .....

类似的可以实现其它不同的状态;
该模块会提供如下的接口来统一创建模块:

function CreateState(statename,...)  local s = nil  if statename=="wander" then    s = Create_Wander()  elseif statename =="search" then    ...  else    error("wrong state name ".. statename )  end  return send

状态转换条件

有了状态, 那么当当前状态运行完了, 后面运行哪个状态, 这个完全有状态转换条件类来判断, 这个类可以完全针对不同的应用场景实现不同的转换类,每个类通过提供一个Judge函数来返回状态是否可以运行,以及优先级就可以;


测试代码

下面创建了两个状态, CreDefStateTrans是用来创建默认的状态转换实例的函数, 这里两个状态相互转换构成一个循环;

require "ActionStateMechaine"local Ast = require "AttackState"require "StateTransCond"function StateTest(obj)  local ASM = ActionStateMechaine:new()  ASM.AddState(Ast.CreateState("wander"))  ASM.AddState(Ast.CreateState("search",OT.MONSTER))  ASM.AddTransition("wander","search",CreDefStateTrans())  ASM.AddTransition("search","wander",CreDefStateTrans())  ASM.RequestState("wander") --初始状态  while true do    ASM:Update(obj)  ednend
0 0
原创粉丝点击