思考并回答以下问题:
本章涵盖:
- FSM基类设计
- 子类设计
- 实体类设计
- 技能子类
- 游戏案例分享
- 小结
FSM全称是Finite State Machine,中文称为有限状态机,在游戏开发中应用非常广泛。它的修饰词Finite是有限的意思,运用FSM解决问题的前提条件是“有限”个状态。在游戏中经常使用它处理状态转化程序逻辑,尤其在动作游戏中,比如在ARPG游戏和运动类休闲游戏中,角色的动作会有不同的动作状态变换,比如idle->walk->run->attack->idle等。在使用FSM编程之前,先把FSM架构设计图介绍一下,如图1所示。
图1 FSM架构设计图
图1架构的最顶部FSM模块是所有状态的基类,所有子类都需要继承该类,FSMState类是定义状态的枚举,这些枚举变量与子类的有限状态机是一一对应的,也就是最底层所具体实现状态实体的逻辑。下面根据FSM架构图逐一实现它们的类编写工作,首先设计一下FSM基类。
FSM基类设计
有限状态机一定要有状态的定义。状态定义可以使用枚举或者是字符串,在二选一的情况下,我倾向于选择枚举,因为这样程序操作起来更方便,当然程序员各有所爱。表示状态变化的枚举代码如下所示。
FsmState.cs
1 | namespace Game.FSM |
定义了枚举FsmState,枚举值包括了游戏中定义角色的大部分状态,比如:FSM_STATE_RUN跑步状态、FSM_STATE_DEAD死亡状态等,还可以继续添加所需要的角色状态。下面开始设计基类代码。
EntityFSM.cs
1 | namespace Game.FSM |
上面的代码相对来说比较简单,代码量非常少。在FSM基类中,定义Enter函数表示的是进入状态,StateChange函数表示的是状态改变,Execute函数表示的是状态执行,Exit函数表示的是状态停止。眼尖的读者注意到它们的参数是Ientity。Ientity类非常重要,后面会给大家详细介绍。现在父类已经设计完成了,下面开始设计子类。
子类设计
有限状态机子类是根据具体的动作实体设计的,拆分每个动作单独作为一个状态实体类。从前面的FSM架构图中可以看出,每个子类都有自己的逻辑方法和属性,子类的共性在父类中已经定义了,在子类中只需实现而已,下面把子类的代码给大家展现一下。
EntityIdleFSM.cs
1 | namespace Game.FSM |
在子类文件中,它继承了父类的Enter、Execute、Exit函数,这些都是FSM通用的方法。这些函数的参数都有Ientity类,设计Ientity类的目的是对角色状态的切换做统一接口处理。它不仅实现了FSM的状态切换接口,同时也包含实体的一些基本功能函数,比如重生、复活、技能释放等,Ientity类是FSM的驱动类,由于该类包含的功能比较多,下面把Ientity类的实现给大家展示一下。
实体类设计
Ientity实体类是每个FSM子类都需要使用的,实体状态之间的转换需要通过实体类Ientity类去操作。为了统一管理,将FSM的状态转换放到了Ientity类中,当然也可以单独拿出来进行处理。Ientity类的核心代码是FSM的状态转换,它封装了统一的转换接口供角色不同的动作状态切换,Ientity类核心代码如下所示。
Ientity.cs
1 | public class Ientity |
在Ientity类中实现了两个重载函数,一个函数是public void OnFSMStateChange(EntityFSM fsm,float last),另一个函数是public void OnFSMStateChange(EntityFSM fsm)。
如果需要做状态变换,直接调用这两个函数中的一个即可实现状态切换。此外,在开发中也将技能作为状态变换的一个子类处理。下面给大家介绍一下技能子类,当然技能子类也可以作为技能系统去单独处理,这里介绍的目的是可以将FSM完全用于游戏玩法的架构设计。
技能子类
游戏中的玩家或者NPC都会释放技能,技能对于FSM来说也是一种状态的改变。游戏中的实时同步也是与状态有关系的。注意,架构没有好坏之分,开发者用着方便就是一个好的架构设计。用着方便包括两方面:一是根据需求可以随意扩展,二是代码模块之间的耦合性比较低,技能子类的代码如下所示。
EntityReleaseSkillFSM.cs
1 | using UnityEngine; |
技能子类的处理方式跟上面实现的实体子类类似,在这里介绍一下函数的功能,Enter函数执行的是技能释放,Execute函数执行的是攻击准备。这样FSM整个系统就完成了,其他子类读者按照这个模式照猫画虎就可以了,下面通过案例的方式讲解一下如何在游戏中使用FSM有限状态机。
游戏案例分享
前几节实现了FSM有限状态机的架构设计,同时利用该思想设计了很多的实体FSM,用于不同动作或者不同技能之间的转换,在这里限于篇幅就不给大家一一列举定义子类的FSM了,它们的书写方式都是类似的。下面给大家展示一下游戏中使用FSM设计的类,首先将编写的FSM代码统一放在Unity项目下的FSM文件夹下面,效果如图2所示。
图2 FSM脚本
在使用时,可以通过调用Ientity类中的函数进行状态切换。调用函数举例如下。1
OnFSMStateChange(Game.FSM.EntityIdleFSM.Instance);
该函数已在封装的Ientity类中给出,在这里提供了不同的动作状态,通过配置文件去配置操作,这也是策划的需求。游戏开发是数据驱动的,程序的设计架构、文本文件的读取也要在设计时考虑到,这样策划可以根据需求配置文件内容。程序加载读取文件内容并将它们显示出来,配置文件的XML文件内容如图3所示。
图3 配置动作文本文件
在XML文件中有n2RandomAttack字段,这个字段的内容表示的是角色动作名字,配置表配置的三个动作名字分别是:attack、attack2、attack3。本案例使用的是老动画系统,如果是新的动画系统可以用触发条件表示不同状态切换,它们的原理是一样的。老动画系统的参考设置如图4所示。
图4 老动画系统的参数设置
小结
以前做游戏架构设计时,开发MMORPG网络游戏时没有使用FSM有限状态机,所有的战斗技能都是在一个类里面封装的,导致后期扩展非常麻烦,所以我痛定思痛把架构重新调成FSM有限状态机架构,虽然花费了一些时间,但是后期开发非常快。一个好的游戏架构是非常重要的。