Unity枚举控制FSM无法按延迟时间切换状态问题排查
Unity FSM状态切换异常排查与修复
问题描述
在Unity中使用有限状态机(FSM)控制角色行为,包含Wait和Idle状态。预期触发事件后延迟10秒从Wait切换至Idle,但实际逻辑异常:
- 游戏启动后进入Wait状态并打印
WAIT OUT(Wait状态退出日志)提前打印- 10秒后才打印
AFTER(切换完成日志) waiting.....从未输出
核心问题分析
- Update方法强制覆盖状态:CharacterFSM的
Update方法中,只要GameManager.Instance.IsLevelStarted为true,就会根据输入或其他条件将executingState重置为IDLE/WALK/SPRINT,直接覆盖了Start中设置的WAIT状态。导致WaitState的UpdateState一执行就进入else分支调用ExitState,所以OUT提前打印,waiting.....永远无法输出。 - 状态类越权处理切换:WaitState的
ExitState中直接调用SwitchState,违背了FSM的职责划分——状态切换的控制权应统一由FSM管理,而非单个状态类。 - 延迟逻辑未生效:
InvokeMethod触发后延迟10秒修改executingState,但在此之前Update方法已经把状态强制改为IDLE,导致延迟切换逻辑完全失效。
修复方案与代码调整
1. 重构WaitState逻辑
移除状态类内的切换逻辑,仅保留状态生命周期内的行为:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class WaitState : CharacterStates { public override void EnterState(CharacterFSM fsm) { fsm.OnCharacterIdle.Invoke(); Debug.Log("WAIT"); } public override void UpdateState(CharacterFSM fsm) { // 仅在WAIT状态下输出等待日志 Debug.Log("waiting....."); } public override void ExitState(CharacterFSM fsm) { Debug.Log("OUT"); // 退出状态仅做清理,切换逻辑交给FSM处理 } }
2. 调整CharacterFSM的状态管理逻辑
将状态切换控制权收回FSM,保留WAIT状态的优先级:
using System.Collections; using UnityEngine; using UnityEngine.Events; public enum ExecutingState { IDLE, WALK, SPRINT, WAIT } [RequireComponent(typeof(CharacterController))] public class CharacterFSM : MonoBehaviour { #region FSM public ExecutingState executingState; public CharacterStates currentState; public IdleState idleState = new IdleState(); public WalkState walkState = new WalkState(); public SprintState sprintState = new SprintState(); public WaitState waitState = new WaitState(); #endregion void OnEnable() { EventManager.OnLevelAfterStart.AddListener(InvokeMethod); } void OnDisable() { EventManager.OnLevelAfterStart.RemoveListener(InvokeMethod); } void Start() { executingState = ExecutingState.WAIT; currentState = waitState; currentState.EnterState(this); } void InvokeMethod() { Invoke("StartLevelAfterCamMove", 10.0f); } void StartLevelAfterCamMove() { EventManager.OnNpcGetSmart.Invoke(); Debug.Log("AFTER"); // 延迟结束后主动切换状态 SwitchState(idleState); executingState = ExecutingState.IDLE; } void Update() { if (!GameManager.Instance.IsLevelStarted) return; if (GameManager.Instance.IsLevelFail || GameManager.Instance.IsLevelSuccess) return; // 保留WAIT状态优先级:处于WAIT时跳过后续状态判断 if (executingState == ExecutingState.WAIT) { currentState.UpdateState(this); return; } if (FsmManager.Instance.IsCharacterCommunicating) { executingState = ExecutingState.IDLE; } else { executingState = (Input.GetAxis("Vertical") != 0 || Input.GetAxis("Horizontal") != 0) ? (Input.GetKey(KeyCode.LeftShift) ? ExecutingState.SPRINT : ExecutingState.WALK) : ExecutingState.IDLE; } // 根据枚举值统一切换状态 SwitchStateByExecutingState(); currentState.UpdateState(this); } private void SwitchStateByExecutingState() { switch (executingState) { case ExecutingState.IDLE: if (currentState != idleState) SwitchState(idleState); break; case ExecutingState.WALK: if (currentState != walkState) SwitchState(walkState); break; case ExecutingState.SPRINT: if (currentState != sprintState) SwitchState(sprintState); break; } } public void SwitchState(CharacterStates nextState) { // 补全状态切换生命周期:先退出当前状态,再进入新状态 currentState.ExitState(this); currentState = nextState; currentState.EnterState(this); } }
关键修改说明
- WAIT状态优先级:在Update中增加判断,处于WAIT状态时跳过后续输入逻辑,保证延迟期间状态不被覆盖。
- 统一状态切换入口:新增
SwitchStateByExecutingState方法,由FSM根据枚举值主动管理状态切换,避免状态类越权操作。 - 完整状态生命周期:修改
SwitchState方法,增加当前状态的ExitState调用,确保状态切换时的逻辑完整性。 - 延迟触发直接切换:
StartLevelAfterCamMove中直接调用SwitchState切换到IdleState,同时更新枚举值,保证切换逻辑立即生效。
内容的提问来源于stack exchange,提问作者New One
相关产品推荐
相关产品推荐

