You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity枚举控制FSM无法按延迟时间切换状态问题排查

Unity FSM状态切换异常排查与修复

问题描述

在Unity中使用有限状态机(FSM)控制角色行为,包含Wait和Idle状态。预期触发事件后延迟10秒从Wait切换至Idle,但实际逻辑异常:

  • 游戏启动后进入Wait状态并打印WAIT
  • OUT(Wait状态退出日志)提前打印
  • 10秒后才打印AFTER(切换完成日志)
  • waiting.....从未输出

核心问题分析

  1. Update方法强制覆盖状态:CharacterFSM的Update方法中,只要GameManager.Instance.IsLevelStarted为true,就会根据输入或其他条件将executingState重置为IDLE/WALK/SPRINT,直接覆盖了Start中设置的WAIT状态。导致WaitState的UpdateState一执行就进入else分支调用ExitState,所以OUT提前打印,waiting.....永远无法输出。
  2. 状态类越权处理切换:WaitState的ExitState中直接调用SwitchState,违背了FSM的职责划分——状态切换的控制权应统一由FSM管理,而非单个状态类。
  3. 延迟逻辑未生效: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 08:42:20