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

Unity有限状态机的简易实现

本人嘴笨,不会说。
该代码实现一个功能较为齐全的有限状态机,可用于大部分的应用场景。
大致实现几个功能

  • 状态更新
  • 状态转换
  • 状态消息处理
  • 全局状态转换和反转状态(转换为前一个状态)

代码分为

  • 状态类
  • 状态管理类
  • 枚举(状态枚举和消息枚举)

简易通用框架代码

状态接口

using System;namespace FSM
{public interface IStateBase<T, TState, TMessage>where TState : Enumwhere TMessage : Enum{//确定该状态枚举public TState GetState();//进入状态void OnEnter(T owner);//更新状态void OnUpdate(T owner);//退出状态void OnExit(T owner);// 转换状态 (-1 为返回上一个状态,0 为保持不变,1 为转换为下一个状态)int OnTransfer(T owner, out TState newState);//消息接收void OnMessage(T owner, TMessage e);}
}

全局状态接口

using System;namespace FSM
{public interface IGlobalState<T, TState, TMessage> : IStateBase<T, TState, TMessage>where TState : Enumwhere TMessage : Enum{//全局转换状态bool OnGlobalTransfer(T owner);}
}

状态管理类

using System;
using System.Collections.Generic;
using UnityEngine;namespace FSM
{public class FsmMachine<T, TState, TMessage>where TState : Enumwhere TMessage : Enum{//状态类存储private Dictionary<TState, IStateBase<T, TState, TMessage>> _states =new Dictionary<TState, IStateBase<T, TState, TMessage>>();//全局状态类存储private Dictionary<TState, IGlobalState<T, TState, TMessage>> _globalStates =new Dictionary<TState, IGlobalState<T, TState, TMessage>>();//Fsm的使用对象private T _owner;//状态枚举private TState _stateID;//上一个状态private IStateBase<T, TState, TMessage> _previousState;//状态类private IStateBase<T, TState, TMessage> _state;//初始化public void Init(T owner, TState stateID){if (owner == null)throw new NullReferenceException();if (!_states.ContainsKey(stateID))throw new AggregateException(stateID + "不存在");_owner = owner;_stateID = stateID;_state = _states[stateID];_state.OnEnter(_owner);}//注册状态类public void RegisterState(IStateBase<T, TState, TMessage> state){if (_states == null)_states = new Dictionary<TState, IStateBase<T, TState, TMessage>>();if (_states.ContainsKey(state.GetState()))Debug.Log(state.GetState() + "已存在");_states.Add(state.GetState(), state);}//删除状态类public void RemoveState(TState stateID){if (_states == null || !_states.ContainsKey(stateID))return;_states.Remove(stateID);}//注册全局状态类public void RegisterGlobalState(IGlobalState<T, TState, TMessage> globalState){if (_globalStates == null)_globalStates = new Dictionary<TState, IGlobalState<T, TState, TMessage>>();if (_globalStates.ContainsKey(globalState.GetState()))Debug.Log(globalState.GetState() + "已存在");_globalStates.Add(globalState.GetState(), globalState);RegisterState(globalState);}//删除全局状态类public void RemoveGlobalState(TState stateID){if (_globalStates == null || !_globalStates.ContainsKey(stateID))return;_globalStates.Remove(_stateID);RemoveState(stateID);}//更新状态public void Update(){if (_state == null)return;//状态更新_state.OnUpdate(_owner);//全局状态转换foreach (var globalState in _globalStates.Values){if (globalState.OnGlobalTransfer(_owner)){ChangeState(globalState.GetState());return;}}//状态转换switch (_state.OnTransfer(_owner, out TState stateID)){case -1:RevertToPreviousState();break;case 0://不做任何事break;case 1:ChangeState(stateID);break;}}//状态反转(返回上一个状态)private void RevertToPreviousState(){ChangeState(_previousState.GetState());}//转换状态public void ChangeState(TState stateID){if (_states == null || !_states.ContainsKey(stateID)) return;if (_stateID.Equals(stateID)) return;_state.OnExit(_owner);_stateID = stateID;_previousState = _state;_state = _states[stateID];_state.OnEnter(_owner);}}
}

实现代码

敌人枚举部分

public enum EnemyState
{None,//闲置Idle,//追逐Chase,
}public enum EnemyMessage
{None,//消息Log,
}

敌人状态基类

using FSM;namespace Enemy
{public abstract class EnemyStateBase : IGlobalState<EnemyEntity, EnemyState, EnemyMessage>{public abstract EnemyState GetState();public abstract void OnEnter(EnemyEntity owner);public abstract void OnUpdate(EnemyEntity owner);public abstract void OnExit(EnemyEntity owner);public abstract int OnTransfer(EnemyEntity owner, out EnemyState newState);public virtual bool OnGlobalTransfer(EnemyEntity owner) => false;public void OnMessage(EnemyEntity owner, EnemyMessage e){//可以通过虚方法的方式,让子类继承实现对应消息的方法if (e == EnemyMessage.Log)OnLog();}protected virtual void OnLog() { }}
}

实现了两个状态 Idle 和 Chase

using UnityEngine;namespace Enemy.State
{public class EnemyIdleState : EnemyStateBase{public override EnemyState GetState() => EnemyState.Idle;public override void OnEnter(EnemyEntity owner){owner.SetVelocity(Vector3.zero);}public override void OnUpdate(EnemyEntity owner) { }public override void OnExit(EnemyEntity owner) { }public override int OnTransfer(EnemyEntity owner, out EnemyState newState){if (Vector3.SqrMagnitude(owner.target.transform.position - owner.transform.position) <= owner.chaseRange * owner.chaseRange){newState = EnemyState.Chase;return 1;}newState = GetState();return 0;}}
}
using UnityEngine;namespace Enemy.State
{public class EnemyChaseState : EnemyStateBase{public override EnemyState GetState() => EnemyState.Chase;public override void OnEnter(EnemyEntity owner) { }public override void OnUpdate(EnemyEntity owner){Vector3 v = owner.target.transform.position - owner.transform.position;//靠太近就挺下来,但状态还是追逐if (Vector3.SqrMagnitude(v) < 1)owner.SetVelocity(Vector3.zero);elseowner.SetVelocity(v.normalized * owner.speed);}public override void OnExit(EnemyEntity owner) { }public override int OnTransfer(EnemyEntity owner, out EnemyState newState){if (Vector3.SqrMagnitude(owner.target.transform.position - owner.transform.position) > owner.escapeRange * owner.escapeRange){newState = EnemyState.Idle;return 1;}newState = GetState();return 0;}}
}

敌人部分

using Enemy;
using Enemy.State;
using FSM;
using UnityEngine;public class EnemyEntity : MonoBehaviour
{//移动速度public float speed = 2f;//追逐范围public float chaseRange = 5f;//逃脱范围public float escapeRange = 8f;//追逐目标public GameObject target;private Rigidbody _rigidbody;private FsmMachine<EnemyEntity, EnemyState, EnemyMessage> _machine;public void SetVelocity(Vector3 v3) => _rigidbody.velocity = v3;private void Awake(){ _rigidbody = GetComponent<Rigidbody>();//创建Fsm并添加状态_machine = new FsmMachine<EnemyEntity, EnemyState, EnemyMessage>();_machine.RegisterState(new EnemyIdleState());_machine.RegisterState(new EnemyChaseState());_machine.Init(this, EnemyState.Idle);}private void Update(){_machine.Update();}
}

玩家部分

using System;
using UnityEngine;public class PlayerEntity : MonoBehaviour
{public float speed = 4f;private void Update(){float hor = Input.GetAxis("Horizontal");float ver = Input.GetAxis("Vertical");Vector3 v = new Vector3(hor, 0, ver).normalized;transform.position += (speed * Time.deltaTime) * v;}
}

敌人主要实现追逐和闲置两个状态,玩家靠近就追逐,远离就闲置。
画面就不展示了,可以复制代码自己尝试

http://www.lryc.cn/news/196194.html

相关文章:

  • 什么是NetApp的DQP和如何安装DQP?
  • Vue之Vue的介绍安装开发实例生命周期钩子
  • 【计网】计算机网络概述
  • 初识Java 14-1 测试
  • react常用的hooks有哪些?
  • Nacos config 配置中心详解
  • C++ UDP通信
  • 自由程序员想接私活?那你还不得知道这几个接单平台!最后一个就是宝藏!!
  • 二叉树与递归的相爱相杀
  • Docker 安装 reids
  • opensl学习——base16编码解码、base64编码解码、ASCII码表、扩展ASCII码
  • gazebo各种插件
  • C语言Free空指针会怎样?
  • 软件测试全套教程,软件测试自学线路图
  • 禁止浏览器缩放
  • 前端食堂技术周刊第 100 期:TS 5.3 Beta、ViteConf2023、Rspress 1.0、Fresh 1.5、Chrome 118
  • 汇川IT7000系列HMI使用脚本实现画面跳转时自动切换手自动模式
  • FDTD Solutions笔记
  • SQL SELECT DISTINCT(选择不同) 语法
  • 常见的数据结构及应用
  • 基于模型预测人工势场的船舶运动规划方法,考虑复杂遭遇场景下的COLREG(Matlab代码实现)
  • 【UE5 Cesium】19-Cesium for Unreal 建立飞行跟踪器(4)
  • TrustZone
  • ✔ ★【备战实习(面经+项目+算法)】 10.16学习时间表(总计学习时间:5h)
  • React + Router
  • 微信小程序设置动态变量设值
  • 闪站侠洗衣洗鞋多门店多用户管理系统,洗鞋店干洗店小程序开发;
  • JDBC增删改查示例
  • emqx broker安装
  • 如何选择国产压力测试工具?