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