> 文章列表 > Unity Game FrameWork—模块使用—有限状态机

Unity Game FrameWork—模块使用—有限状态机

Unity Game FrameWork—模块使用—有限状态机

官方说明: 提供创建、使用和销毁有限状态机的功能,一些适用于有限状态机机制的游戏逻辑,使用此模块将是一个不错的选择。
有限状态机并不是游戏中独有的,我们看一下其他的介绍:
有限状态机是一种用来进行对象行为建模的工具,其作用主要是描述对象在它的生命周期内所经历的状态序列,以及如何响应来自外界的各种事件。在计算机科学中,有限状态机被广泛用于建模应用行为、硬件电路系统设计、软件工程,编译器、网络协议、和计算与语言的研究。
[图片]
[图片]
图片来源:https://blog.csdn.net/DataAlchemist/article/details/98450010
参考上面两幅图,当玩家处于当前状态时,玩家会执行某些操作,当这些操作触发切换状态的条件时,则切换状态。如果用if else来判断则太过复杂不便于梳理流程,以及一堆条件变量控制,当状态改变或触发条件改变,整个流程的维护是很复杂的,我们不能专注当前状态要做的事,需要去看前后关联的触发条件是否会有问题,这些条件变量在其他状态是怎样的,会不会由于条件变量的改变导致其它问题,因此诞生了状态机。我们只需关注当前状态要干什么,干完你之后什么条件下会触发下一个状态。
前面有写过,流程是对状态机的封装,我们来理解一下流程的思路,首先进入游戏的初始状态完成版本构建、语言配置等工作即可触发Splash 动画流程展示,ProcedureSplash流程中选择资源加载模式,例如选择编辑器模式,则进入ProcedurePreload资源预加载流程。相关游戏资源加载完成后,则进入切换场景的流程ProcedureChangeScene。状态机所解决的问题与流程需要完成的事情是完全吻合的。
流程是对状态机做了一层封装,抽丝剥茧理解起来相对复杂,抓不住问题的本质,这里我们做一个简单地Demo来完成状态机学习。借用一下浅墨大哥写的设计模式和案例:(缅怀,人走了,知乎博客还在影响着我们)
【游戏设计模式】之三 状态模式、有限状态机 & Unity版本实现
状态机Demo:Behavioral Patterns-State Pattern-Exmaple4
接下来会参考这个案例用框架完成这个Demo的功能,Demo的流程图如下
[图片]

Demo工作流:

ProcedureFSM:流程类
FSM_Hero:英雄逻辑类
State:FsmState<FSM_Hero>状态类
创建ProcedureFSM流程类,在流程类中创建英雄实体并添加逻辑类FSM_Hero,英雄逻辑类中创建四种状态类,随后在四个状态类中完成触发及状态切换等工作。(流程启动,实体创建及数据表配置请参考本专栏之前的内容)
下面贴上代码:
下蹲状态:

public class DuckingState : FsmState<FSM_Hero>
{public override void OnEnter(IFsm<FSM_Hero> fsm){Debug.Log("------------------------Heroine in DuckingState~!(进入下蹲躲避状态!)");}public override void OnUpdate(IFsm<FSM_Hero> fsm, float elapseSeconds, float realElapseSeconds){HandleInput(fsm);}public void HandleInput(IFsm<FSM_Hero> fsm){if (Input.GetKeyDown(KeyCode.DownArrow)){Debug.Log("已经在下蹲躲避状态中!");return;}if (Input.GetKeyUp(KeyCode.UpArrow)){Debug.Log("get GetKeyUp.UpArrow!");ChangeState<StandingState>(fsm);}}
}

站立状态:

public class StandingState : FsmState<FSM_Hero>
{public override void OnEnter(IFsm<FSM_Hero> fsm){Debug.Log("------------------------Heroine in StandingState~!(进入站立状态!)");}public override void OnUpdate(IFsm<FSM_Hero> fsm, float elapseSeconds, float realElapseSeconds){HandleInput(fsm);}public void HandleInput(IFsm<FSM_Hero> fsm){if (Input.GetKeyDown(KeyCode.UpArrow)){Debug.Log("get KeyCode.UpArrow!");ChangeState<JumpingState>(fsm);}if (Input.GetKeyDown(KeyCode.DownArrow)){Debug.Log("get KeyCode.DownArrow!");ChangeState<DuckingState>(fsm);}}
}

下斩状态:

public class DrivingState : FsmState<FSM_Hero>
{public override void OnEnter(IFsm<FSM_Hero> fsm){Debug.Log("------------------------Heroine in DrivingState~!(进入下斩状态!)");}public override void OnUpdate(IFsm<FSM_Hero> fsm, float elapseSeconds, float realElapseSeconds){HandleInput(fsm);}public void HandleInput(IFsm<FSM_Hero> fsm){if (Input.GetKeyDown(KeyCode.UpArrow)){Debug.Log("get KeyCode.UpArrow!");ChangeState<StandingState>(fsm);}}
}

跳跃状态:

public class JumpingState : FsmState<FSM_Hero>
{public override void OnEnter(IFsm<FSM_Hero> fsm){Debug.Log("------------------------Heroine in JumpingState~!(进入跳跃状态!)");}public override void OnUpdate(IFsm<FSM_Hero> fsm, float elapseSeconds, float realElapseSeconds){HandleInput(fsm);}public void HandleInput(IFsm<FSM_Hero> fsm){if (Input.GetKeyDown(KeyCode.UpArrow)){Debug.Log("get GetKeyDown.UpArrow! but already in Jumping! return!(已经在跳跃状态中!)");return;}if (Input.GetKeyDown(KeyCode.DownArrow)){Debug.Log("get KeyCode.DownArrow!");ChangeState<DrivingState>(fsm);}}
}

英雄逻辑类

public class FSM_Hero : EntityLogic
{public override void OnInit(object userData){base.OnInit(userData);FsmState<FSM_Hero>[] heroState ={new StandingState(),new DuckingState(),new JumpingState(),new DrivingState(),};var heroFsm = StarForce.GameEntry.Fsm.CreateFsm(this, heroState);heroFsm.Start<StandingState>();}public override void OnShow(object userData){base.OnShow(userData);}public override void OnHide(bool isShutdown, object userData){base.OnHide(isShutdown, userData);StarForce.GameEntry.Fsm.DestroyFsm<FSM_Hero>();}public override void OnUpdate(float elapseSeconds, float realElapseSeconds){base.OnUpdate(elapseSeconds, realElapseSeconds);}
}

Demo流程启动:

public class ProcedureFSM : ProcedureBase
{public override bool UseNativeDialog{get{return true;}}public override void OnEnter(ProcedureOwner procedureOwner){base.OnEnter(procedureOwner);GameEntry.Entity.ShowEntity<FSM_Hero>(99999, "Assets/GameMain/Entities/Hero.prefab", "Aircraft");}
}

数据交互

状态机内部不同状态间通常需要数据交互,GetData,SetData,HasData,RemoveData四个接口提供了数据的获取、设置、是否存在、删除的功能。数据以key-value的形式存储于字典中。
框架对于数据做了一层封装,获取数据时,不用关心是什么类型,用统一的接口获取,强制转换。传入数据时,也可根据自己的需要自定义数据类型。
如下代码,是框架定义的数据类型,演示了四种接口的使用

VarBoolean a = new VarBoolean();
a.Value = true;
heroFsm.SetData("name", a);
g = (bool)heroFsm.GetData("name").GetValue();
if (heroFsm.HasData("name"))
{heroFsm.RemoveData("name");
}

自定义数据类型的两种写法如下,用bool类型做一个演示。
方式一:

public class Boolable : Variable
{public bool? value;public Boolable(bool? value = null){this.value = value;}public override Type Type => value.GetType();public override void Clear(){value = null;}public override object GetValue(){return value;}public override void SetValue(object value){this.value = (bool)value;}
}

方式二:

public class Boolean : Variable<bool>
{/// <summary>/// 初始化 System.Boolean 变量类的新实例。/// </summary>public Boolean(){}/// <summary>/// 从 System.Boolean 到 System.Boolean 变量类的隐式转换。/// </summary>/// <param name="value">值。</param>public static implicit operator Boolean(bool value){Boolean varValue = ReferencePool.Acquire<Boolean>();varValue.Value = value;return varValue;}/// <summary>/// 从 System.Boolean 变量类到 System.Boolean 的隐式转换。/// </summary>/// <param name="value">值。</param>public static implicit operator bool(Boolean value){return value.Value;}
}

两种方式没有本质上的区别,Variable是框架的抽象变量类,变量的类型用泛型表示,如果继承于该类,只需定义构造函数即可。如果对于SetData,GetData有其他的实现形式,可以直接继承于Variable。