lua面向对象与基于lua的状态机实现
2016-04-18 22:15
525 查看
之前一篇文章只是简单的介绍lua的面向对象, 对这方面的理解还很浅,最近在游戏中实现了一个状态机, 这里简单介绍;
状态机里包含以唯一名字标记对应的状态, 由于状态对于一个游戏角色的行为来说, 可以看作是底层实现, 所以一些公共状态可以被; 状态之间的转换也通过一个类来实现, 这样 状态机就是有不同的状态实例, 状态转换实例组合而成, 这样做的目的自然是为了让状态机有更强的适应性, 针对不同的场景, 可以共用之前实现的状态, 由状态转换实例来决定状态机的运行;
注意:上面的_states, _transition是用table保存实例对象, 但这里一定要为nil, 因为如果这里写成了空表如
状态机成员函数:
注意: 所有的成员函数必须如python一样手动加上self, 虽然可以有自动self功能, 但这里如果省略self ,则后面继承时会出现错误, 因为子类调用该函数时用到的self还是基类的self, 数据也是基类的数据; (这个坑我花了不少时间解决)
下面的成员函数就是用于添加转换实例和状态实例的:
状态机的更新函数:
当然这里的nextstate其实没什么太大的作用, 不过先留着以防以后会有更多的内容添加;
小结: 上面就是状态机的主要结构, 主要提供了一个框架的东西在这, 里面的内容(state, conTransition)需要具体的去实现;
基于上面的基类, 后面各种具体状态可以添加一些自己的属性, 同时必须实现一个run执行函数, 否则就会调用基类的run函数;
下面给出我本人的几个状态, 这里为每个状态直接通过一个函数来创建, 最后通过一个相同的接口来提供, 这样做一是简便,而是封装:
类似的可以实现其它不同的状态;
该模块会提供如下的接口来统一创建模块:
思路介绍
状态有很多,需要有扩展性, 所以这里的状态有独立的类结构, 同时状态机作为一个功能要让每个角色对象身上都拥有自己的状态机, 因此也是一个类;状态机里包含以唯一名字标记对应的状态, 由于状态对于一个游戏角色的行为来说, 可以看作是底层实现, 所以一些公共状态可以被; 状态之间的转换也通过一个类来实现, 这样 状态机就是有不同的状态实例, 状态转换实例组合而成, 这样做的目的自然是为了让状态机有更强的适应性, 针对不同的场景, 可以共用之前实现的状态, 由状态转换实例来决定状态机的运行;
实现代码
行为状态机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] ~= nil end
下面的成员函数就是用于添加转换实例和状态实例的:
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 = self end
状态机的更新函数:
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 true end
当然这里的nextstate其实没什么太大的作用, 不过先留着以防以后会有更多的内容添加;
小结: 上面就是状态机的主要结构, 主要提供了一个框架的东西在这, 里面的内容(state, conTransition)需要具体的去实现;
状态类
前面的ASM只是一个空壳, 现在要实现各种不同的state去填充ASM; 所有的state都将继承与一个ActionState类, 并封装在模块内,这样对于后面可以对不同种类的状态好管理;-- state base class ActionState = {} 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 o end -- 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 s end
状态转换条件
有了状态, 那么当当前状态运行完了, 后面运行哪个状态, 这个完全有状态转换条件类来判断, 这个类可以完全针对不同的应用场景实现不同的转换类,每个类通过提供一个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) edn end
相关文章推荐
- 详解Lua中的表的概念及其相关操作方法
- Lua编程示例(二):面向对象、metatable对表进行扩展
- C#中面向对象编程机制之多态学习笔记
- 把Lua编译进nginx步骤方法
- Lua脚本自动生成APK包
- Lua中的元表(metatable)、元方法(metamethod)详解
- Lua中的metatable介绍
- Lua中ipair和pair的区别
- Lua中的函数精讲笔记
- 浅谈Lua的面向对象特性
- 详解Lua中的变量相关知识点
- Lua脚本语言入门笔记
- Lua脚本调用外部脚本
- 详解Lua中的if语句的使用方法
- Lua中调用函数使用点号和冒号的区别
- Lua中的闭合函数、非全局函数与函数的尾调用详解
- Lua中强大的元方法__index详解
- Lua中调用C++函数示例
- Python3 面向对象概述
- java面向对象之类和对象