游戏中分层状态机的实现(转)

分层状态机学习 转载:原创作者 参考:参考作者 日志: 21.8.24:组内同事分享了一个多状态的项目,其中用到了分层状态机的概念,蛮有兴趣的,前来学习一下 决策方法: 有限状态机(Finite-State Machines) 分层状态机(Hierarchical Finite-State Machines) 行为树(Behavior Trees) 效用系统(Utility Systems) 目标导向型行动计划(Goal-Oriented Action Planners) 分层任务网络(Hierarchical Task 阅读详情
    游戏中最复杂的逻辑部分就是战斗部分。之前一直没有对状态机进行理论学习,以及于在设计游戏战斗逻辑的时候总是没有安全感。下边是一个写的不错的文章。

状态机的实现方式有很多种,一般都使用比较简单的switch case方式来实现对事件的处理和状态的转移,如下所示:

[html]  view plain copy
  1. void ProcessEvent(Event event)  
  2. {  
  3.     switch(state)  
  4.     {  
  5.         case StateA:  
  6.             StateAProcess(event);  
  7.             break;  
  8.   
  9.         case StateB:  
  10.             StateBProcess(event);  
  11.             break;  
  12.     }  
  13. }  

也有利用数组实现的行列式状态机,这样方便开发人员查看,如下所示:

[csharp]  view plain copy
  1. EventHandler stateHandler[] =  
  2. {  
  3.     StateA_Event1, StateA_Event2,  
  4.     StateB_Event1, StateB_Event2,  
  5. };  
  6.   
  7. void ProcessEvent(Event event)  
  8. {  
  9.     int index = state * EVENT_NUM + event;  
  10.     stateHandler[index]();  
  11. }  

但是这些状态机都有一个共同点,就是状态之间的转移需要在状态内部显示得指明目标状态。


----------------------------------------------------------------------------------------------


在游戏中,遇到一些复杂的情况时,如果使用普通的状态机,那需要写大量的状态,举一个星际争霸中一个机枪兵的例子:


1. 机枪兵在平时站立时,处于 空闲 状态;

2. 机枪兵发现敌人,并且敌人在射程范围内,机枪兵开始攻击敌人;此时,机枪兵进入 攻击 状态;

3. 敌人死亡,机枪兵停止攻击;此时,机枪兵回到 空闲 状态;

4. 此时玩家发出进攻命令,此进攻命令是用A键点了远处的一个地面 place1 ,也就是没有具体目标的进攻;此时,机枪兵进入 移动进攻 状态;

5. 在移动过程中,机枪兵发现了敌人,所以他要脱离原来的路径,走向发现的敌人;此时,机枪兵进入 追击 状态;

6. 机枪兵和敌人的距离小于了自己的射程之后,机枪兵停下来,并且攻击敌人;此时,机枪兵进入了 攻击 状态;

7. 敌人死亡后,机枪兵重新寻路到place1,继续前进;此时机枪兵回到步骤4,回到了 移动进攻 状态。


在上面这个过程中,从步骤2到步骤3,攻击 状态转移到 空闲 状态;从步骤6到步骤7,攻击 状态转移到 移动进攻 状态;源状态都是 攻击 状态,触发事件都是 敌人死亡,但是目标状态却不相同;

也就是说,步骤2的 攻击 状态和步骤6的 攻击 状态严格意义上是不同的两个状态,一般来说有两个解决方案来满足这种情况:

1. 做 攻击A 状态和 攻击B 状态;

2. 在攻击状态内保存一个变量,来实现状态结束后跳转到不同的状态;


其实这两种方法本质上都是一样的,而且都存在同样的缺点:如果在某种情况下如果 攻击 状态收到 敌人死亡 事件之后需要跳转到其他状态,(比如机枪兵在巡逻时发现敌人的情况),那就需要增加状态或者代码分支。


如何才能将上面的 攻击 状态合并为一个,而且可以支持以后的扩展呢?这个就是需要解决的问题。

仔细分析一下,可以发现 攻击 状态之所以需要跳转到不同的目标状态,是因为在其前,机枪兵进入了不同的状态;换句话说,机枪兵退出 攻击 状态的时候,实际上是回到了之前的某个阶段的状态。(步骤2是回到前一个状态,步骤7是回到了前两个状态)


----------------------------------------------------------------------------------------------


堆栈的特性为我们很好地解决了这个问题:压人变量A,栈顶的变量就是变量A;压入变量B,栈顶的变量变为变量B;弹出变量B,栈顶的变量变回到变量A。


所以根据这个特性,可以开发一个融合堆栈的状态机,其基础构造参照了《大型多人在线游戏开发》(《Massively Multiplayer Game Development》)里面的状态机实现,代码(c sharp格式)参考如下:

[csharp]  view plain copy
  1. public enum StateChange  
  2. {  
  3.     None,  
  4.     Switch,  
  5.     Enter,  
  6.     Exit,  
  7. }  
  8.   
  9. public class UnitSMBase  
  10. {  
  11.     public UnitState state;  
  12.     public StateChange change;  
  13.       
  14.     float _deltaTime;  
  15.     protected float _checkTime;  
  16.     protected Unit _unit;  
  17.       
  18.     public UnitSMBase(Unit unit)  
  19.     {  
  20.         _unit = unit;  
  21.         _deltaTime = 0;  
  22.     }  
  23.       
  24.     public virtual void Enter()  
  25.     {  
  26.         _checkTime = 1;  
  27.     }  
  28.       
  29.     public virtual void Exit()  
  30.     {  
  31.     }  
  32.       
  33.     public void ProcessEvent(UnitEvent evt)  
  34.     {  
  35.         state = UnitState.None;  
  36.         change = StateChange.None;  
  37.           
  38.         if(evt == UnitEvent.Update)  
  39.         {  
  40.             if(_deltaTime >= _checkTime)  
  41.             {  
  42.                 _deltaTime = 0;  
  43.                 evt = UnitEvent.UpdateFixTime;  
  44.             }  
  45.             else  
  46.             {  
  47.                 _deltaTime += Time.deltaTime;  
  48.             }  
  49.         }  
  50.           
  51.         DoProcess(evt);  
  52.     }  
  53.       
  54.     protected virtual void DoProcess(UnitEvent evt)  
  55.     {  
  56.     }  
  57.       
  58.     public virtual bool CanGo(UnitState unitState)  
  59.     {  
  60.         return true;  
  61.     }  
  62. }  
  63.   
  64.   
  65. public class UnitSmMgr  
  66. {  
  67.     List<UnitSMBase> _smList;  
  68.     Dictionary<UnitState, UnitSMBase> _smStateDict;  
  69.   
  70.     public UnitSmMgr(UnitSMBase initSM, UnitState initState)  
  71.     {  
  72.         _smList = new List<UnitSMBase>();  
  73.         _smList.Add(initSM);  
  74.           
  75.         _smStateDict = new Dictionary<UnitState, UnitSMBase>();  
  76.         _smStateDict[initState] = initSM;  
  77.   
  78.                 initSM.Enter();  
  79.     }  
  80.   
  81.     public void RegisterSM(UnitSMBase sm, UnitState state)  
  82.     {  
  83.         _smStateDict[state] = sm;  
  84.     }  
  85.   
  86.     public void ProcessEvent(UnitEvent evt)  
  87.     {  
  88.         _smList[0].ProcessEvent(evt);  
  89.           
  90.         switch(_smList[0].change)  
  91.         {  
  92.         case StateChange.Enter:  
  93.             _smList.Insert(0, _smStateDict[_smList[0].state]);  
  94.             _smList[0].Enter();  
  95.             break;  
  96.               
  97.         case StateChange.Switch:  
  98.              _smList[0].Exit();  
  99.             _smList[0] = _smStateDict[_smList[0].state];  
  100.             _smList[0].Enter();  
  101.             break;  
  102.               
  103.         case StateChange.Exit:  
  104.             _smList[0].Exit();  
  105.             _smList.RemoveAt(0);  
  106.             break;  
  107.         }  
  108.           
  109.         if(0 == _smList.Count)  
  110.         {  
  111.             Debug.LogError("state machine is empty");  
  112.         }  
  113.     }  
  114. }  

主要思路如下:


1. 每个状态都是一个类,他们继承于一个公共类,其包含进入,退出,处理事件的虚方法;

2. 状态机有一个状态堆栈,这里使用List来实现;

3. 状态机初始化时有一个初始状态,一般为idle状态,其成为堆栈的第一个元素;

4. 状态转移分为3种情况:a 进入目标状态,b 退出当前状态,c 切换到目标状态(即先退出当前状态,再进入目标状态);

5. 当前有效的状态就是状态堆栈里面栈顶的那个状态,即:_smList[0];


按照这个状态机模型来实现前面讲过的机枪兵的例子,其中状态机图中左边卫栈顶,右边为栈底:

1. 机枪兵在平时站立时,处于 空闲 状态;

初始化状态机,并将 空闲 状态作为初始状态放入状态机堆栈中;状态机堆栈:【空闲】


2. 机枪兵发现敌人,并且敌人在射程范围内,机枪兵开始攻击敌人;此时,机枪兵进入 攻击 状态;

进入 攻击 状态;状态机堆栈:【攻击】【空闲】


3. 敌人死亡,机枪兵停止攻击;此时,机枪兵回到 空闲 状态;

退出当前状态;状态机堆栈:【空闲】


4. 此时玩家发出进攻命令,此进攻命令是用A键点了远处的一个地面 place1 ,也就是没有具体目标的进攻;此时,机枪兵进入 移动进攻 状态;

进入 移动进攻 状态;状态机堆栈:【移动进攻】【空闲】


5. 在移动过程中,机枪兵发现了敌人,所以他要脱离原来的路径,走向发现的敌人;此时,机枪兵进入 追击 状态;

进入 追击 状态;状态机堆栈:【追击】【移动进攻】【空闲】


6. 机枪兵和敌人的距离小于了自己的射程之后,机枪兵停下来,并且攻击敌人;此时,机枪兵进入了 攻击 状态;

切换到 攻击 状态;状态机堆栈:【攻击】【移动进攻】【空闲】


7. 敌人死亡后,机枪兵重新寻路到place1,继续前进;此时机枪兵回到步骤4,回到了 移动进攻 状态。

退出当前状态;状态机堆栈:【移动攻击】【空闲】


这样的话,不需要记录之前状态的信息,就能完成状态之间的正确转移;开发逻辑时,只需要注意状态发生变化时应该使用3种方式里面的哪1种来做状态转移。

其他参考: http://zgx10.blog.163.com/blog/static/6490547201102012444301/
层次状态机设计指导 当前状态没有匹配到转换但其父状态可以匹配到对应的事件Event及警备条件Guard为真或无警备条件的转换,则执行父状态的转换,如果父状态仍然没有匹配到但父状态的父状态可以匹配则执行父状态的父状态匹配的转换……即当事件发生时如果子状态有匹配的转换则执行子状态的转换,否则不断向上匹配父状态的转换,直至匹配到合适的转换并执行或者所有祖先都不匹配什么转换也不执行。历史状态有向外的迁移,表示如果历史状态空白,那么迁移到指定的默认目标状态,否则迁移到原来记忆的状态。离开C时,执行C的出口活动y=1,y值为1。 阅读详情

相关推荐

Stateflow#3:层次化状态机的实现

什么是层次化状态机? 为什么要使用层次化的建模?  构建一个层次化的状态机  创建超级状态以及超级转移  创建子图  多级状态机的行为  恢复激活的子状态  不同层间交叉状态转移的语法规则 ...

东川 2558

游戏中分层状态机的实现

状态机的实现方式有很多种,一般都使用比较简单的switch case方式来实现对事件的处理和状态的转移,如下所示: void ProcessEvent(Event event) { switch(state) { case StateA: StateAProcess(event); break; case StateB: StateBProcess(event);

xtxy的专栏 1万+

Unity 2D游戏AI实战:动态敌人追踪光标系统设计与优化

游戏AI是提升游戏可玩性和沉浸感的核心技术之一,其本质是模拟智能行为,让非玩家角色(NPC)具备感知、决策与行动能力。在2D射击或动作游戏中,实现敌人对玩家光标或角色的动态追踪,是AI系统的基础且关键的应用场景。这通常涉及向量计算、状态机、转向行为等核心技术原理。一个设计良好的追踪系统不仅能提升游戏挑战性,还能显著优化性能,保障游戏流畅运行。本文基于Unity引擎与C#,深入解析如何构建一个包含感知、决策、移动三大模块的完整敌人AI系统,并分享性能优化与调试的工程实践,其中重点探讨了分层状态机与对象池等关键

weixin_27943395的博客 235

HFSM(HierarchyFiniteStateMachine)分层有限状态机 一

HFSM(HierarchyFiniteStateMachine)分层有限状态机

lq 9234

游戏AI之 有限状态机、分层有限状态机、行为树

内容会持续更新,有错误的地方欢迎指正,谢谢! FSM(有限状态机)、HFSM(分层有限状态机)、BT(行为树) 1.有限状态机:可用于做任务动画的切换,也可用于做简单的AI。将行为分为一个个的状态,状态与状态之间的过渡通过事件的触发来形成。 2.分层有限状态机:简单说,就是状态太多时,不好维护,于是将状态分类,将同类的状态作为一个状态机,然后再做一个大的状态机,来维护这些子状态机。 比如:...

陈云佳的专栏 5957

分层状态机(Hierarchical State Machine, HFSM)详解

HFSM 继承了 FSM 的基本结构,但增加了**子状态(Sub-States)**的概念。本篇博客将详细介绍 HFSM 的概念、优势、实现方式及在 Unity 中的应用。结合 HFSM,提高 AI 逻辑的可读性和可维护性。,传统 FSM 可能会导致代码混乱、难以维护。为了解决 FSM 的扩展性问题,我们可以使用。组织状态,使得状态切换更加清晰、可复用。在 Unity 中,我们可以使用。在 Unity 中,我们可以通过。:子状态可在不同父状态间复用。,无需处理太多状态切换逻辑。组件中,我们可以使用。

weixin_42216813的博客 2573

FSM(状态机)、HFSM(分层状态机)、BT(行为树)的区别

游戏人工智能AI中最常听见的就是这三个词拉: FSM 这个不用说拉,百度一大堆解释, 简单将就是将游戏AI行为分为一个一个的状态,状态与状态之间的过渡通过事件的触发来形成。 比如士兵的行为有“巡逻”,“追击敌人”,“攻击敌人”,“逃跑”等行为, 响应的事件就有“发现敌人”,“追到敌人”,“敌人逃跑”,“敌人死亡”,“自己血量不足”等。 那么可以写成这样一个状态机: 1.士兵 “巡逻”,如果 “发现...

昔拉的博客 1万+

游戏设计模式之状态机

正文 忏悔时间:我对这一章节的内容有点夸大其词。表面上是关于状态设计模式的探讨,但我不得不谈及游戏中关于有限状态机制(或称为 “FSM” )的基本概念。不过我一旦提及到这个,那么我想我也不妨介绍下分层状态机和下推自动机的概念以及相关原理。 这会涵盖多方面的知识点,为了尽可能地缩短文章篇幅,文中使用的代码示例省略了一些细节,这些是您必须自己填写的。不管怎样,我还是希望这些知识点仍然能够清晰以便能让你了解整个理念。 如果你从未听说过状态机,也请不要感到难过。状态机不像 AI 和编译器、黑客那样,它在编程圈子里没

weixin_56659504的博客 1098

HFSM(HierarchyFiniteStateMachine)分层有限状态机 二

HFSM(HierarchyFiniteStateMachine)分层有限状态机 二 继续上一篇HFSM(HierarchyFiniteStateMachine)分层有限状态机 一 说了一部分理论,下面说下每个状态之间的转换Transition 条件如何设置。 转换条件需要属性值作为依据,定义几个字段来帮助设置条件 float read = 0; // 看书 float homework = 0; // 写作业 float network = 0; //

lq 6043

Unity游戏AI开发:从状态机到分层状态机(HFSM)的实战重构

在游戏开发中,人工智能(AI)是提升游戏体验深度的关键技术。其核心原理在于通过逻辑模型模拟智能体的决策与行为。状态机(FSM)作为一种经典的设计模式,通过定义有限的状态及状态间的转换规则,为AI行为提供了清晰的结构化解决方案,有效解决了传统if-else逻辑带来的代码臃肿和维护难题,技术价值在于极大地提升了代码的可读性、可维护性与可调试性。随着游戏角色行为复杂度的增加,例如敌人AI需要同时管理巡逻、战斗(内含追击、攻击等子行为)等多层逻辑,基础状态机在应对嵌套行为时显得力不从心。此时,分层状态机(HFSM)

weixin_29682503的博客 281

谈一谈游戏AI -状态机

郑重说明:本文适合对游戏开发感兴趣的小白初学者,本人力图将事物用简单的语言表达清楚,但水平有限,能力一般,文章如有错漏之处,还望批评指正。

afriedegg的博客 745

游戏AI实现-分层状态机

记录分层状态机代码实现

weixin_50702814的博客 571

StateMachineCompiler for C 根据状态表生成【层次状态机】【C代码】

Python实现,根据状态表生成C代码的【层次状态机】,亦可退化成【平面状态机】。使用C模拟C++的一些特性。 2009.12.3: 里面有readme,在研究之前先读一下。 对于号称“专业研究。。。”的fazai001(无激发)同学,本人表示不屑。 压缩包内有现成的例子,使用如下: 安装python2.5.2 在该目录的cmd下,运行python smc.sm watch.sm 生成两个目录,以及gen_xxx.c, gen_xxx.h几只文件 用vc6创建空的工程,加入gen_xxx.c, watch_hsm.c等三支文件就可以编译运行了 完整的状态机转换图在watch.sm文件中。

FPS射击游戏状态机验证技术探秘

FPS射击游戏GamePlay开发中,状态机验证机制是核心技术难点。文章分析了并行分层状态机设计、状态步长验证和武器状态帧序列化回退三大挑战,涉及状态同步、网络异常处理和性能优化等问题。典型方案包括拆解独立状态机、帧级状态校验和局部回退机制。伪代码展示了客户端-服务器同步流程,并提出了优化输入同步、平滑回退等建议。该机制能有效保障游戏一致性,但需持续优化扩展性和性能表现。

本博客聚焦游戏开发技巧、创业实战心得及大学热门课程干货。这里既有技术深度,也有思维广度,无论你是开发者、创业者还是高校学子,都能在这里找到适合自己的成长之路! 1100

分层状态机

多层状态机,创建一个C#工程就可以运行,具体看打印,有兴趣的可以研究下

防止加入域主机脱离域的控制(管理员权限)

一、简介   当公司所有主机加入到域后,SA首要工作就是防止加入域的主机脱离域的控制。让我发现该工作的必要性是一程序猿,前一天刚配完禁止执行QQ、微信执行,第二天就看到那家伙在用微信聊天,肯定是用回旧的本地账号登陆了。   本人所在公司安装软件的类型繁杂,无管理员权限运行不少软件会报错,不得不授予「域账号」本地管理员权限。过大的权限带也来了一系列后果:用户可随意退出域、创建本地管理员...

der69615的博客 3173

状态机设计模式与实现:从红绿灯到智能游戏AI的进化之路

通过小陈的游戏AI开发之旅,我们深入探索了状态机设计模式的精髓。从简单的红绿灯系统到复杂的智能游戏AI,状态机展现了其强大的建模能力和广泛的适用性。它将复杂的行为逻辑分解为清晰、可管理的状态和转换,使得系统的设计、实现和维护都变得更加容易。

苦哈哈的 C++ 程序员,写这个技术博客不是为了装逼,也不是为了立人设,是为了收割流量 1079

DexProtector内存态匿名段检测绕过与dex重组技术

Dex文件是Android应用的核心执行单元,其结构规范(如header_item、class_def_item、code_item)决定了运行时加载与解析的底层逻辑。DexProtector通过将dex数据拆解为多个无文件名、受保护的内存匿名段(如[anon:dp_header]、[anon:dp_code]),规避传统基于文件特征或标准加载路径的检测机制。该策略依赖Android mmap内存管理与ART虚拟机对dex结构的刚性要求,形成可被观测的布局规律。理解其内存态组织原理,结合权限/大小/命名/内

weixin_30855761的博客 688

分层状态机的设计思路

文 / 韦易笑   转载自  很多人问游戏AI该怎么做?随着游戏类型的多元化,非 MMO或者卡牌的游戏越来越多,对AI的需求也越来越强了。而市面上关于 AI的书,网上找得到的文章,也都流于一些只言片语的认识,理论化的套路,和一些简单的 DEMO,离真正的项目差距甚远,无法前后衔接成一条线,更无法真正落地到编码。  国内真正做过游戏AI的少之又少,东拉西扯的人很多,真正做过项目的人很少,因为国内主要...

yuxikuo_1的专栏 7218

Flocking for Multi-Agent Dynamic Systems: Algorithms and Theory的理论简述和python仿真实现(附源码)

经典论文Flocking for Multi-Agent Dynamic Systems: Algorithms and Theory的学习与编程仿真,提供源码

m0_59701926的博客 1235

F5 Shape逆向工程实战:从美西南航空到xbk的风控突破

本文以美西南航空和xbk为例,深入剖析了F5 Shape风控系统的逆向工程实战。文章拆解了其核心防线,包括通信加密、JSVMP逆向分析和无孔不入的环境指纹检测,并分享了定位Header生成入口、模拟算法输出及构建真实浏览器环境的系统性方法与思路,为合规的技术研究与测试提供参考。

weixin_29327525的博客 1386
上一篇: 使用C++11新特性,实现用字符串作为switch的case子句(转)
下一篇: 跨平台版本Cocos Studio制作与使用(转)
woodcol
博客等级 码龄19年 288粉丝 429原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值