游戏角色并行状态机之boost::msm

时间:2022-12-15 07:26:05
   最近对角色状态机做了一个分析,首先分析一下串行状态机存在的问题,以及扩展的局限性。然后谈一下并行状态机的设计,最后我将简单介绍一下boost.msm,以及如何运用这个高效的状态机库,实现的并行的角色状态机。
         1,将游戏状态机设计成串行的,一般包括下面的一些状态,空闲,移动,走,跑,跳,飞行,骑行,游泳,技能请求,技能吟唱,技能蓄力,施法,击倒,击退,恐惧,不可移动恐惧,不可控制,不可移动,死亡,死卧等等状态。串行状态机的优点在于设计简单,比较好调试,缺点是扩展性不强,无法很好的表达现实中确实存在的并行状态。比如现实游戏中,可能一边骑行,一边施放技能,或者一边眩晕,一边被击退。要用串行状态机来实现这些概念的话,只能通过组合的方式,生成类似于眩晕击退这样的状态机。这种设计严重影响了扩展性,随着需求的增加,这种组合状态数量将很难控制,而且状态转换也更复杂。
          2,角色并行状态机,类似于《大型多人在线游戏开发》这本书里提到的并行状态机,对于玩家状态我大致切割成以下几个层次。
         移动层:空闲,移动,移动到  (都只产生位移,不产生动作,移动方向,方式,目标点这些,可以通过事件参数传弟进来)
         姿势层:空闲,静止,走,跑,飞,泳,骑,跳(这一层主要对应移动相关的动作层,除了跳,其它的状态只产生实际的动作,跳做成子状态机,实现跳跃各阶段,以及多连跳)
         行动层:空闲,眩晕,击倒,击退,拾取,死亡,休闲动作,技能施放(这个用子状太机实现,技能施放的各阶段)
         旋转层:空闲,左转,右转。    
         这几个层做成并发的,都同时会收到状态机关心的游戏事件,各层做各层的事情,并根据各自己的逻辑进行状态机切换,移动层产生实际的位移,姿势层主要根据移动类型产生实际的角色动作,跳跃处理复杂的跳跃逻辑,当处理跳跃状态时,移动层还是可以根据事件产生位移,只是动作还是跳跃动作。行动层主要处理行为相关的事件,这个层次会和前两个层次产生并行,这个时候需要考虑到动作融合的问题,比如边跑,边放技能这一块。我觉得这一块可以通过把角色的动作分成上半身和下半身来进行分别处理,上半身播施放技能手上的动作,下半身还是继续放跑步的动作。当然,这个需要底层支持。旋转层作为一个特效的层来进行处理,他实际上可以和移动层,姿势层并行,当然要考虑动作优先级的问题。另外,还有一个问题,移动时的速度和走,跑是有关系的,这个我们可以在姿势层切换状态时,通过状态机属性,记录下当前的移动类型,在移动层取得这个属性,决定角色的移动速度。还有一种情况,是并行,就存在一个阻止问题,比如当玩家在被眩晕的状态下,有可能不能移动,这种情况下就可以由状态机设计一个标志位,比如不可移动标志,当有移动事件产生时,状态机不作响应。
       3,前段时间比较全面的学习了一下boost.msm状态机,他采用泛型编程思想,基于boost库和uml,可以直接通过一个表,映射uml设计的状态机。详细的msm和uml我就不在此详解,只是大概讲一下msm中几个重要的概念,以及如何通过这些功能实现我第二点中提到的并行状态机设计。msm中有一个region的概念,他其实就是一个并行状态机的概念,每个区域并行触发,同时处理他们关心的事件。状态机有条件判断,进入,退出,动作这几个函数接口,可以实现每个转换过程需要处理的逻辑。msm中还有子状态机的概念,可以实现我上面提到的复杂的状太机,比如跳跃。flags,这个可以作为一个标位,比如进入眩晕状态,设计MoveDisable标志位。在转换过程中可以取得状态机的标志,用以转换条件判断等。事件是结构体或类对象,他可以有自己的成员变量,通过他可以将一些参数传给状态机,比如移动事件中可以包含方向属性,状态机可以和他保存的当前移动方向比较,当方向不同时,向底层发出命令,向某个方向移动。msm的状态机里面有一个内部转换概念,就是在转换表中只设计事件,以及动应的action,这样每个状态机就可以争对他们关心的事件,设计不同的响应接口,而并不一定要切换状态。如此就不用传统的事件响应函数,写一堆关于事件的switch, 并且,不同的事件,还可以指向同一个action函数,或者不同的状态机内部对同一个事件的响应,也可以指向同一个action函数,或函数对象。另外,关于键盘事件,按下去后,窗口丢失焦点,抬起事件无法获得的问题,常常导致玩家移动停不下来的类似问题,有一个小技巧,可以每帧扫描键盘状态,产生移动事件,如此就可以解决类似的问题。
       我这里只是一个大概的设计,在实际的运用中,肯定会有大量的细节问题和设计问题,不过相信在boost.msm中总能找到不错的解决方案。