当前位置: 首页 > news >正文

【Unity】构建超实用的有限状态机管理类

1.什么是有限状态机?

有限状态机(Finite State Machine, 简称 FSM)。在游戏开发领域,它能够将一个游戏对象的所有行为拆分成一个个的“状态”。

比如,在游戏场景中正在巡逻的敌人,它可能会有“巡逻”、“追踪玩家”、“攻击玩家”、“死亡”等状态,状态与状态之间可以相互切换,同时,敌人在任何时刻都只会处于一个明确的状态,要么它处于巡逻状态,要么它处于追踪玩家状态……。

总结起来,有限状态机有如下的特征/优点:

1.状态唯一性:一个游戏对象在任何时刻只能处于一种明确的状态

2.逻辑清晰:你不再需要写一个巨大的 Update() 函数,里面塞满了 if-else 或 switch 语句。每个状态都是一个独立的模块,只关心自己的事情。

3.易于扩展:如果想创建新的状态,你只需要再添加一个状态子类,然后根据新的状态需要实现的行为,重写父类的方法即可。

2.构建有限状态机!

在学习如何构建有限状态机之前,我建议你对C#的“类的继承”的知识有一定的基础。

2.1 创建状态基类

状态基类不实现任何逻辑,它仅提供可重写的方法,即OnEnter、OnUpdate、OnLeave。状态基类的派生类(子类)需要重写这三个方法,以实现不同的状态。

/// <summary>
/// 有限状态机状态基类
/// 说明:该类用于创建子类,实现有限状态机的状态行为
/// </summary>
public class FSMStateBase
{protected FSM fSM; //状态机引用/// <summary>/// 构造函数/// </summary>/// <param name="target">状态机的所属对象</param>public FSMStateBase(object target, FSM fSM){this.fSM = fSM;    }#region 状态方法public virtual void OnEnter() { }public virtual void OnUpdate() { }public virtual void OnLeave() { }#endregion
}

2.2 创建有限状态机

using System.Collections.Generic;
using UnityEngine;/// <summary>
/// 有限状态机类
/// 说明:该类用于控制状态机的运行
/// </summary>
public class FSM
{#region 私有变量private List<FSMStateBase> _fsmStates;private FSMStateBase _currentState;#endregion/// <summary>/// 构造函数/// </summary>public FSM(){_currentState = null;}/// <summary>/// 添加状态机状态/// </summary>/// <param name="fSMStates">有限状态机的状态列表</param>public void AddFSMStates(params FSMStateBase[] fSMStates){//处理状态列表空值if (fSMStates == null){Debug.LogWarning("至少要有一个状态实例");return;}//添加状态实例到列表_fsmStates = new List<FSMStateBase>();foreach (var state in fSMStates){_fsmStates.Add(state);}}/// <summary>/// 启动状态/// </summary>/// <typeparam name="T"></typeparam>public void StartState<T>() where T : FSMStateBase{//阻止重复启动状态if (_currentState != null && _currentState.GetType() == typeof(T)){return;}//先停止上一个状态if (_currentState != null && _currentState.GetType() != typeof(T)){_currentState.OnLeave();}//从状态类的列表里面寻找指定类型的状态实例for (int i = 0; i < _fsmStates.Count; i++){var state = _fsmStates[i];if (state.GetType() == typeof(T)){_currentState = state;_currentState.OnEnter();return;}}Debug.LogWarning($"没有找到{typeof(T)}类型的状态实例,无法启动状态机");}/// <summary>/// 检查当前状态是否为指定的状态/// </summary>/// <typeparam name="T"></typeparam>/// <returns></returns>public bool CheckIsState<T>() where T : FSMStateBase{if (_currentState == null)return false;return _currentState.GetType() == typeof(T);}/// <summary>/// 获得当前的状态机的状态/// </summary>/// <returns></returns>public FSMStateBase GetCurrentState(){return _currentState;}/// <summary>/// 停止状态/// </summary>public void StopState(){if (_currentState == null) return;_currentState.OnLeave();_currentState = null;}#region 状态机状态函数/// <summary>/// 状态函数:进入状态/// </summary>public void OnEnter(){if (_currentState == null) return;_currentState.OnEnter();}/// <summary>/// 状态函数:帧更新/// </summary>public void OnUpdate(){if (_currentState == null) return;_currentState.OnUpdate();}/// <summary>/// 状态函数:离开状态/// </summary>public void OnLeave(){if (_currentState == null) return;_currentState.OnLeave();}#endregion
}

3.使用有限状态机(以敌人为例)

3.1 创建敌人类

using UnityEngine;public class Enemy : MonoBehaviour
{}

3.2 创建状态派生类(子类)

这里我就创建两个状态吧!

//敌人的巡逻状态
public class EnemyState_Patrol : FsmStateBase
{//假设我有一个敌人类private Enemy enemy;public EnemyState_Patrol(object target, FSM fSM) : base(target, fSM){//这个状态机是属于enemy的状态机enemy = target as Customer;}public override void OnEnter(){Debug.Log("进入巡逻状态");}public override void OnUpdate(){Debug.Log("巡逻状态更新");}public override void OnLeave(){Debug.Log("离开巡逻状态");}
}//敌人的追踪玩家状态
public class EnemyState_TracePlayer : FsmStateBase
{//假设我有一个敌人类private Enemy enemy;public EnemyState_Patrol(object target, FSM fSM) : base(target, fSM){//这个状态机是属于enemy的状态机enemy = target as Customer;}public override void OnEnter(){Debug.Log("进入追踪玩家状态");}public override void OnUpdate(){Debug.Log("追踪玩家状态更新");}public override void OnLeave(){Debug.Log("离开追踪玩家状态");}
}

3.3 初始化状态机

public class Enemy : MonoBehaviour
{public Fsm fsm;private void Awake(){fsm = new FSM();fsm.AddFSMStates(new EnemyState_Patrol(this, fsm), //巡逻状态new EnemyState_TracePlayer(this, fsm), //追踪玩家状态);}
}

3.4 接入状态机的函数

最主要的还是将OnUpdate函数接入到Update生命周期函数中。OnEnter和OnLeave在启动/切换状态的时候会自动执行。

using UnityEngine;public class Enemy : MonoBehaviour
{public Fsm fsm;private void Awake(){fsm = new FSM();fsm.AddFSMStates(new EnemyState_Patrol(this, fsm), //巡逻状态new EnemyState_TracePlayer(this, fsm), //追踪玩家状态);}private void Start(){//启动状态fsm.StartState<EnemyState_Patrol>();}private void Update(){   //更新fsm.OnUpdate();}
}

3.5 再加两个调试函数测试一下~

using UnityEngine;public class Enemy : MonoBehaviour
{public Fsm fsm;private void Awake(){fsm = new FSM();fsm.AddFSMStates(new EnemyState_Patrol(this, fsm), //巡逻状态new EnemyState_TracePlayer(this, fsm), //追踪玩家状态);}private void Start(){//启动状态fsm.StartState<EnemyState_Patrol>();}private void Update(){   //更新fsm.OnUpdate();}[ContextMenu("切换到巡逻状态")]public void SwitchToPatrol(){fsm.StartState<EnemyState_Patrol>();}[ContextMenu("切换到追踪玩家状态")]public void SwitchToTracePlayer(){fsm.StartState<EnemyState_TracePlayer>();}
}

3.6 说明

在实际的有限状态机使用中,是在状态派生类里面进行状态的切换(不然你以为状态基类的构造函数里面为什么要引用状态机实例?就是用来给你切换状态的!),而在MonoBehaviour类里面一般只需要初始化和启动有限状态机足矣~

http://www.dtcms.com/a/469745.html

相关文章:

  • redis基础命令和深入理解底层
  • Java中第三方报告库-Allure
  • 高端公司网站建设连云港做网站制作
  • Google 智能体设计模式:优先级排序
  • 网站做不做百度云加速手游代理平台哪个好
  • 【国内电子数据取证厂商龙信科技】邮件如何取证?
  • 手机网站模板 psd做网站建设分哪些类型
  • 做网站需要哪些框架网站没备案可以访问吗
  • Git下载和安装教程(附安装包)
  • go的学习2---》并发编程
  • 高端网站建设企业公司网页版qq空间登录入口官网
  • 麒麟系统安装达梦数据库遇到的问题
  • VScode怎么使用Jupyter并且设置内核
  • LwIP UDP RAW
  • VI-SLAM定位方案对比
  • TCP/IP 协议族—理论与实践(一)
  • 手持小风扇MCU方案,智能风扇方案设计开发
  • 网站设计深圳网站建设公司网页设计与制作100例怎么写
  • Linux -- 网络层
  • 建设班级网站 沟通无限网络黄页进入有限公司
  • Labview项目01:标准可配置序列测试框架
  • 拌合楼软件开发(23)监测客户端在线情况并联动企业微信提醒客户端离线和恢复
  • 雄安网建 网站建设莞城微信网站建设
  • 基于Python的交通数据分析应用-hadoop+django
  • [特殊字符] 教程|打造一个 Telegram 币圈波场交易记录检测机器人
  • 高弹性不锈钢材质在技工钳应用中的优势分析
  • 东土科技连投三家核心企业 发力具身机器人领域
  • 建设行政主管部门官方网站张槎建网站服务
  • 【AI4S】利用大语言模型 LLM 进行分子设计
  • 零用贷网站如何做p2p网站建设公司排名