• Unity-FSM有限状态机


    我们在进行开发时,到了一定程度上,会遇到数十种状态,继续使用Unity的Animator控制器会出现大量的bool,float类型的变量,而这些错综复杂的变量与Animatator控制器如同迷宫版连线相结合会变得极其的复杂且无法良好维护扩展,出现一个BUG会导致开发过程中开发者承受极大的精神力,而这时候,使用有限状态机或者AI行为树便成为了一个极佳的选择,本文只记录了有限状态机的开发使用有限状态机进行状态管理与切换可以大幅度的减少开发时候的难度,在开发过程中只需要关注各个状态间的切换即可

    1. public interface IState
    2. {
    3. //进入状态
    4. void Enter() { }
    5. //退出状态
    6. void Exit() { }
    7. //状态逻辑更新
    8. void LogicUpdate() { }
    9. }
    1. //
    2. /// 持有所有状态类,并对他们进行管理和切换
    3. /// 负责当前状态的更新
    4. ///
    5. public class StateMachine : MonoBehaviour
    6. {
    7. IState currentState; //当前状态
    8. public Dictionary stateTable; //字典,用于保存状态以及查询状态,方便状态切换时 //进行查找
    9. public void Update()
    10. {
    11. currentState.LogicUpdate(); //执行每个状态的逻辑切换函数,可以使状态实现检测 //变换,类似于Update内的函数实时接收信息
    12. }
    13. //切换状态
    14. protected void SwitchOn(IState newState)
    15. {
    16. //当前状态变为新状态
    17. currentState = newState;
    18. //进入新状态
    19. currentState.Enter();
    20. }
    21. //
    22. public void SwitchState(IState newState)
    23. {
    24. //退出状态
    25. currentState.Exit();
    26. //进入新状态
    27. SwitchOn(newState);
    28. }
    29. //切换状态函数重载
    30. public void SwitchState(System.Type newStateType)
    31. {
    32. SwitchState(stateTable[newStateType]); //将字典内的状态传入
    33. }
    34. }
    1. public class PlayerState : ScriptableObject, IState
    2. {
    3. /*************物理检测*************/
    4. protected bool isGround; //是否在地面
    5. /*************基础信息*************/
    6. protected bool isRun; //是否跑步
    7. protected bool isJump; //是否跳跃
    8. protected bool isIdle; //是否静止
    9. /*************相关组件*************/
    10. protected Rigidbody2D my_Body2D; //刚体组件,用于获取物体刚体属性
    11. protected Animator animator; //动画组件,用来播放动画
    12. protected PlayerStateMachine stateMachine; //PlayerStateMachine,玩家状态机类,执行状态间的切换
    13. public void Initiatize(Animator animator, Rigidbody2D my_Body2D, PlayerStateMachine stateMachine)
    14. {//获取PlayerStateMachine传递进来的 动画,刚体,状态机类
    15. this.animator = animator;
    16. this.my_Body2D = my_Body2D;
    17. this.stateMachine = stateMachine;
    18. }
    19. public void PhysicalDetection(bool isGround)
    20. {
    21. this.isGround = isGround;
    22. }
    23. ///
    24. /// 状态信息传递
    25. ///
    26. public void BasicInformation(bool isRun,bool isJump,bool isIdle)
    27. {
    28. this.isRun = isRun;
    29. this.isJump = isJump;
    30. this.isIdle = isIdle;
    31. }
    32. //进入状态
    33. public virtual void Enter() { }
    34. //离开状态
    35. public virtual void Exit() { }
    36. //逻辑切换
    37. public virtual void LogicUpdate() { }
    38. }
    1. ///
    2. /// 玩家状态机类
    3. ///
    4. public class PlayerStateMachine : StateMachine
    5. {
    6. /*************************检测信息***************************/
    7. PlayerPhysicalDetection playerPhysicalDetection; //物理检测组件,这个是继承 //MonoBehaviour,挂载到玩家身上来检测 //玩家的物理信息,例如是否位于地面
    8. /*************************状态信息***************************/
    9. //PlayerState资源文件
    10. [SerializeField] PlayerState[] states;
    11. Animator animator; //获取动画组件
    12. Rigidbody2D my_Body2D; //获取刚体组件
    13. void Awake()
    14. {
    15. /*************************物理检测信息***************************/
    16. playerPhysicalDetection=GetComponent(); //获取物理检测组件
    17. /*************************状态信息组件***************************/
    18. stateTable = new Dictionary(states.Length); //初始化字典
    19. animator = GetComponent(); //获取动画组件
    20. my_Body2D = GetComponent(); //获取刚体组件
    21. //迭代器循环获取状态
    22. foreach (PlayerState state in states)
    23. {
    24. state.Initiatize(animator, my_Body2D, this);//将动画组件,刚体组件以及PlayerStateMachine传入进去
    25. //状态存入字典
    26. stateTable.Add(state.GetType(), state);
    27. }
    28. }
    29. private void Start()
    30. {//在开始时执行Idle,进入Idle状态
    31. SwitchOn(stateTable[typeof(PlayerState_Idle)]);
    32. }
    33. private new void Update()
    34. {
    35. base.Update();//执行父类StateMachine的Update函数
    36. foreach (PlayerState state in states)
    37. {//将检测信息传入进去
    38. state.PhysicalDetection(playerPhysicalDetection.isGround);
    39. state.BasicInformation( isRun, isJump, isIdle);
    40. }
    41. }
    42. }
    1. [CreateAssetMenu(menuName = "StateMachine/PlayerState/Idle", fileName = "PlayerState_Idle")]//创建文件
    2. public class PlayerState_Idle : PlayerState
    3. {
    4. /*****************物理检测*******************/
    5. public override void Enter()
    6. {
    7. //执行该状态数据文件,首先执行进入状态函数,在进入状态函数执行相关的行为
    8. //进入状态,默认播放Idle动画
    9. animator.Play("PlayerIdle");
    10. }
    11. //逻辑切换函数,当检测到处于某种状态,立刻执行该状态的数据文件
    12. public override void LogicUpdate()
    13. {
    14. if(isRun)
    15. {
    16. stateMachine.SwitchState(stateMachine.stateTable[typeof(PlayerState_Run)]);
    17. }
    18. if(isJump)
    19. {
    20. stateMachine.SwitchState(stateMachine.stateTable[typeof(PlayerState_Jump)]);
    21. }
    22. if(my_Body2D.velocity.y<0&&!isGround)
    23. {
    24. stateMachine.SwitchState(stateMachine.stateTable[typeof(PlayerState_Fall)]);
    25. }
    26. }
    27. }

  • 相关阅读:
    NSS [HUBUCTF 2022 新生赛]checkin
    CSS 介绍
    程序员的晋升困境
    JVM相关
    设计模式之观察者模式(优先使用对象组合的原则)的C++实现
    1.Golang语法学习总结
    idea 一直卡在maven正在解析maven依赖
    对加密世界的经济误解:现金是储蓄?稀缺性创造价值?
    玄机-第一章 应急响应-Linux日志分析
    算法常见技巧 -快速幂及其相关应用
  • 原文地址:https://blog.csdn.net/softshow1026/article/details/134248527