状态模式是一种行为设计模式,允许对象在内部状态改变时改变其行为,使对象看起来像是修改了其类。

1. 状态模式基本概念

1.1 状态模式的核心思想

  • 将状态封装成独立的类

  • 将状态相关的行为委托给当前状态对象

  • 允许状态对象在运行时切换

1.2 状态模式的三个主要组件

  • Context(上下文):维护当前状态,并将与状态相关的操作委托给当前状态对象

  • State(状态接口):定义所有具体状态的通用接口

  • ConcreteState(具体状态):实现特定状态的行为

2. 基础状态模式实现

2.1 基本接口和类结构

csharp

// 状态接口
public interface IState
{
    void Enter();
    void Update();
    void Exit();
}

// 上下文基类
public abstract class StateContext : MonoBehaviour
{
    protected IState currentState;
    
    public void ChangeState(IState newState)
    {
        if (currentState != null)
            currentState.Exit();
        
        currentState = newState;
        currentState.Enter();
    }
    
    protected virtual void Update()
    {
        if (currentState != null)
            currentState.Update();
    }
}

3. Unity中的具体实现示例:玩家状态机

3.1 玩家状态接口和具体状态

csharp

// 玩家状态接口
public interface IPlayerState : IState
{
    void HandleInput(PlayerInput input);
}

// 玩家输入结构
public struct PlayerInput
{
    public float Horizontal;
    public float Vertical;
    public bool JumpPressed;
    public bool AttackPressed;
}

// 空闲状态
public class IdleState : IPlayerState
{
    private PlayerController player;
    private Animator animator;
    
    public IdleState(PlayerController player)
    {
        this.player = player;
        this.animator = player.GetComponent<Animator>();
    }
    
    public void Enter()
    {
        Debug.Log("进入空闲状态");
        animator.SetBool("IsWalking", false);
        animator.SetBool("IsRunning", false);
    }
    
    public void Update()
    {
        // 空闲状态下的更新逻辑
    }
    
    public void HandleInput(PlayerInput input)
    {
        if (Mathf.Abs(input.Horizontal) > 0.1f || Mathf.Abs(input.Vertical) > 0.1f)
        {
            if (input.Horizontal > 0.5f || input.Vertical > 0.5f)
                player.ChangeState(new RunState(player));
            else
                player.ChangeState(new WalkState(player));
        }
        
        if (input.JumpPressed)
        {
            player.ChangeState(new JumpState(player));
        }
    }
    
    public void Exit()
    {
        Debug.Log("退出空闲状态");
    }
}

// 行走状态
public class WalkState : IPlayerState
{
    private PlayerController player;
    private Animator animator;
    private float walkSpeed = 3f;
    
    public WalkState(PlayerController player)
    {
        this.player = player;
        this.animator = player.GetComponent<Animator>();
    }
    
    public void Enter()
    {
        Debug.Log("进入行走状态");
        animator.SetBool("IsWalking", true);
        animator.SetBool("IsRunning", false);
    }
    
    public void Update()
    {
        player.Move(walkSpeed);
    }
    
    public void HandleInput(PlayerInput input)
    {
        if (Mathf.Abs(input.Horizontal) < 0.1f && Mathf.Abs(input.Vertical) < 0.1f)
        {
            player.ChangeState(new IdleState(player));
        }
        else if (input.Horizontal > 0.5f || input.Vertical > 0.5f)
        {
            player.ChangeState(new RunState(player));
        }
        
        if (input.JumpPressed)
        {
            player.ChangeState(new JumpState(player));
        }
    }
    
    public void Exit()
    {
        Debug.Log("退出行走状态");
    }
}

// 奔跑状态
public class RunState : IPlayerState
{
    private PlayerController player;
    private Animator animator;
    private float runSpeed = 6f;
    
    public RunState(PlayerController player)
    {
        this.player = player;
        this.animator = player.GetComponent<Animator>();
    }
    
    public void Enter()
    {
        Debug.Log("进入奔跑状态");
        animator.SetBool("IsWalking", false);
        animator.SetBool("IsRunning", true);
    }
    
    public void Update()
    {
        player.Move(runSpeed);
    }
    
    public void HandleInput(PlayerInput input)
    {
        if (Mathf.Abs(input.Horizontal) < 0.1f && Mathf.Abs(input.Vertical) < 0.1f)
        {
            player.ChangeState(new IdleState(player));
        }
        else if (input.Horizontal < 0.5f && input.Vertical < 0.5f)
        {
            player.ChangeState(new WalkState(player));
        }
        
        if (input.JumpPressed)
        {
            player.ChangeState(new JumpState(player));
        }
    }
    
    public void Exit()
    {
        Debug.Log("退出奔跑状态");
    }
}

// 跳跃状态
public class JumpState : IPlayerState
{
    private PlayerController player;
    private Animator animator;
    private float jumpForce = 8f;
    private bool isGrounded;
    
    public JumpState(PlayerController player)
    {
        this.player = player;
        this.animator = player.GetComponent<Animator>();
    }
    
    public void Enter()
    {
        Debug.Log("进入跳跃状态");
        animator.SetTrigger("Jump");
        player.Jump(jumpForce);
        isGrounded = false;
    }
    
    public void Update()
    {
        isGrounded = player.IsGrounded();
        
        if (isGrounded)
        {
            player.ChangeState(new IdleState(player));
        }
        
        player.Move(4f); // 跳跃中的移动速度
    }
    
    public void HandleInput(PlayerInput input)
    {
        // 跳跃中可能处理一些输入,如攻击
        if (input.AttackPressed)
        {
            // 跳跃攻击
        }
    }
    
    public void Exit()
    {
        Debug.Log("退出跳跃状态");
    }
}

3.2 玩家控制器(上下文)

csharp

public class PlayerController : StateContext
{
    [SerializeField] private float moveSpeed = 5f;
    [SerializeField] private float gravity = -9.81f;
    [SerializeField] private LayerMask groundLayer;
    
    private CharacterController characterController;
    private Vector3 velocity;
    private PlayerInput currentInput;
    
    private void Start()
    {
        characterController = GetComponent<CharacterController>();
        // 初始状态
        ChangeState(new IdleState(this));
    }
    
    protected override void Update()
    {
        // 收集输入
        currentInput = new PlayerInput
        {
            Horizontal = Input.GetAxis("Horizontal"),
            Vertical = Input.GetAxis("Vertical"),
            JumpPressed = Input.GetButtonDown("Jump"),
            AttackPressed = Input.GetButtonDown("Fire1")
        };
        
        // 处理重力
        if (characterController.isGrounded && velocity.y < 0)
        {
            velocity.y = -2f;
        }
        velocity.y += gravity * Time.deltaTime;
        characterController.Move(velocity * Time.deltaTime);
        
        // 更新当前状态
        if (currentState is IPlayerState playerState)
        {
            playerState.HandleInput(currentInput);
            base.Update();
        }
    }
    
    public void Move(float speed)
    {
        Vector3 moveDirection = new Vector3(currentInput.Horizontal, 0, currentInput.Vertical);
        if (moveDirection.magnitude > 0.1f)
        {
            // 旋转朝向移动方向
            float targetAngle = Mathf.Atan2(moveDirection.x, moveDirection.z) * Mathf.Rad2Deg;
            transform.rotation = Quaternion.Euler(0, targetAngle, 0);
            
            // 移动
            characterController.Move(moveDirection.normalized * speed * Time.deltaTime);
        }
    }
    
    public void Jump(float force)
    {
        velocity.y = Mathf.Sqrt(force * -2f * gravity);
    }
    
    public bool IsGrounded()
    {
        return characterController.isGrounded;
    }
}

4. 高级状态模式实现

4.1 带参数的状态机

csharp

// 带参数的状态接口
public interface IParamState<T> : IState
{
    void SetContext(StateContext context);
    void SetParameter(T parameter);
}

// 泛型状态机
public class StateMachine<T> : MonoBehaviour where T : System.Enum
{
    private Dictionary<T, IState> states = new Dictionary<T, IState>();
    private IState currentState;
    private T currentStateType;
    
    public void RegisterState(T stateType, IState state)
    {
        states[stateType] = state;
    }
    
    public void ChangeState(T newStateType)
    {
        if (states.TryGetValue(newStateType, out IState newState))
        {
            if (currentState != null)
                currentState.Exit();
            
            currentState = newState;
            currentStateType = newStateType;
            currentState.Enter();
        }
    }
    
    public T GetCurrentStateType()
    {
        return currentStateType;
    }
    
    protected virtual void Update()
    {
        if (currentState != null)
            currentState.Update();
    }
}

4.2 动画集成状态机

csharp

// 动画状态机
public class AnimatorStateMachine : MonoBehaviour
{
    [System.Serializable]
    public class StateAnimationPair
    {
        public string StateName;
        public AnimationClip Animation;
        public float TransitionDuration = 0.1f;
    }
    
    [SerializeField] private Animator animator;
    [SerializeField] private List<StateAnimationPair> stateAnimations;
    
    private Dictionary<string, int> animationHashes = new Dictionary<string, int>();
    private string currentState;
    
    private void Start()
    {
        // 预计算动画哈希
        foreach (var pair in stateAnimations)
        {
            animationHashes[pair.StateName] = Animator.StringToHash(pair.Animation.name);
        }
    }
    
    public void ChangeState(string stateName)
    {
        if (currentState == stateName) return;
        
        if (animationHashes.ContainsKey(stateName))
        {
            animator.CrossFade(animationHashes[stateName], 
                GetTransitionDuration(stateName));
            currentState = stateName;
        }
    }
    
    private float GetTransitionDuration(string stateName)
    {
        var pair = stateAnimations.Find(p => p.StateName == stateName);
        return pair != null ? pair.TransitionDuration : 0.1f;
    }
}

5. 状态模式的最佳实践

5.1 状态工厂模式

csharp

// 状态工厂
public class StateFactory
{
    private Dictionary<Type, IState> stateCache = new Dictionary<Type, IState>();
    
    public T GetState<T>(object context) where T : IState, new()
    {
        Type stateType = typeof(T);
        
        if (!stateCache.ContainsKey(stateType))
        {
            T state = new T();
            
            // 如果状态需要上下文,设置上下文
            if (state is IContextState contextState)
            {
                contextState.SetContext(context);
            }
            
            stateCache[stateType] = state;
        }
        
        return (T)stateCache[stateType];
    }
}

// 需要上下文的接口
public interface IContextState : IState
{
    void SetContext(object context);
}

5.2 状态模式在AI中的应用

csharp

// AI敌人状态机
public class AIEnemy : MonoBehaviour
{
    public enum AIState { Patrol, Chase, Attack, Flee }
    
    private StateMachine<AIState> stateMachine;
    private Transform player;
    
    private void Start()
    {
        player = GameObject.FindGameObjectWithTag("Player").transform;
        stateMachine = gameObject.AddComponent<StateMachine<AIState>>();
        
        // 注册状态
        stateMachine.RegisterState(AIState.Patrol, new PatrolState(this));
        stateMachine.RegisterState(AIState.Chase, new ChaseState(this, player));
        stateMachine.RegisterState(AIState.Attack, new AttackState(this, player));
        stateMachine.RegisterState(AIState.Flee, new FleeState(this, player));
        
        // 初始状态
        stateMachine.ChangeState(AIState.Patrol);
    }
}

// 巡逻状态
public class PatrolState : IState
{
    private AIEnemy enemy;
    private Vector3[] patrolPoints;
    private int currentPointIndex = 0;
    
    public PatrolState(AIEnemy enemy)
    {
        this.enemy = enemy;
        // 初始化巡逻点
    }
    
    public void Enter()
    {
        Debug.Log("开始巡逻");
    }
    
    public void Update()
    {
        // 巡逻逻辑
        if (Vector3.Distance(enemy.transform.position, patrolPoints[currentPointIndex]) < 0.5f)
        {
            currentPointIndex = (currentPointIndex + 1) % patrolPoints.Length;
        }
        
        // 检测玩家
        // 如果检测到玩家,切换到追逐状态
    }
    
    public void Exit()
    {
        Debug.Log("结束巡逻");
    }
}

6. 状态模式的优缺点

优点:

  1. 单一职责原则:每个状态都有自己的类,代码更清晰

  2. 开闭原则:易于添加新状态而不修改现有代码

  3. 消除条件语句:减少了大量的if-else或switch语句

  4. 状态转换逻辑明确:状态转换集中管理

缺点:

  1. 类数量增加:每个状态都需要一个类

  2. 过度设计:对于简单状态机可能过度复杂

  3. 状态间依赖:状态之间可能有复杂的依赖关系

7. 实际应用建议

  1. 何时使用状态模式

    • 对象有多个状态,且状态间频繁转换

    • 状态相关行为复杂,且有大量条件语句

    • 需要清晰的状态管理结构

  2. Unity特定优化

    • 结合Unity的Animator Controller

    • 使用ScriptableObject创建可配置的状态

    • 利用Unity的协程处理状态中的时序逻辑

  3. 性能考虑

    • 避免频繁创建状态对象(使用对象池或缓存)

    • 对于简单状态机,考虑使用枚举+switch

    • 使用状态模式时注意内存占用

状态模式在Unity中特别适合用于角色控制、AI行为、游戏流程管理等场景,能够使代码更加模块化和可维护。

补充与优化

1. 状态机的性能优化策略

1.1 状态缓存与对象池

csharp

// 状态工厂与缓存
public class StateFactory
{
    private static readonly Dictionary<Type, IState> stateCache = new Dictionary<Type, IState>();
    private static readonly Dictionary<Type, object> stateContexts = new Dictionary<Type, object>();
    
    public static T GetState<T>() where T : IState, new()
    {
        Type type = typeof(T);
        
        if (!stateCache.ContainsKey(type))
        {
            stateCache[type] = new T();
        }
        
        return (T)stateCache[type];
    }
    
    public static T GetState<T>(object context) where T : IState, new()
    {
        Type type = typeof(T);
        
        if (!stateCache.ContainsKey(type))
        {
            T state = new T();
            
            // 如果状态需要初始化上下文
            if (state is IContextableState contextable)
            {
                contextable.SetContext(context);
            }
            
            stateCache[type] = state;
            stateContexts[type] = context;
        }
        else if (stateCache[type] is IContextableState cachedContextable)
        {
            // 更新上下文
            cachedContextable.SetContext(context);
        }
        
        return (T)stateCache[type];
    }
}

// 支持上下文的状态接口
public interface IContextableState : IState
{
    void SetContext(object context);
}

1.2 批处理状态切换

csharp

// 延迟状态切换管理器
public class StateQueueManager : MonoBehaviour
{
    private class StateRequest
    {
        public IState State;
        public object Param;
        public float Delay;
        public System.Action OnComplete;
    }
    
    private Queue<StateRequest> stateQueue = new Queue<StateRequest>();
    private Coroutine currentTransition;
    
    public void QueueState(IState state, object param = null, float delay = 0f, System.Action onComplete = null)
    {
        stateQueue.Enqueue(new StateRequest
        {
            State = state,
            Param = param,
            Delay = delay,
            OnComplete = onComplete
        });
        
        if (currentTransition == null)
        {
            currentTransition = StartCoroutine(ProcessQueue());
        }
    }
    
    private IEnumerator ProcessQueue()
    {
        while (stateQueue.Count > 0)
        {
            var request = stateQueue.Dequeue();
            
            if (request.Delay > 0)
                yield return new WaitForSeconds(request.Delay);
            
            // 执行状态切换
            request.State.Enter(request.Param);
            
            request.OnComplete?.Invoke();
            
            yield return null;
        }
        
        currentTransition = null;
    }
    
    public void ClearQueue()
    {
        stateQueue.Clear();
        if (currentTransition != null)
        {
            StopCoroutine(currentTransition);
            currentTransition = null;
        }
    }
}

2. 复合状态模式(状态嵌套)

2.1 子状态机系统

csharp

// 子状态机支持
public class SubStateMachine : IState
{
    private StateMachine innerStateMachine;
    private IState parentState;
    
    public SubStateMachine(IState parent)
    {
        parentState = parent;
        innerStateMachine = new StateMachine();
    }
    
    public void Enter(object param = null)
    {
        innerStateMachine.Start();
    }
    
    public void Update()
    {
        innerStateMachine.Update();
        
        // 检查是否应该退出子状态机
        if (ShouldExitSubStateMachine())
        {
            parentState.Exit();
        }
    }
    
    public void Exit()
    {
        innerStateMachine.Stop();
    }
    
    public void RegisterSubState(IState state, System.Func<bool> exitCondition = null)
    {
        innerStateMachine.RegisterState(state, exitCondition);
    }
    
    private bool ShouldExitSubStateMachine()
    {
        // 根据游戏逻辑判断是否退出
        return false;
    }
}

// 支持子状态的状态机
public class AdvancedStateMachine : MonoBehaviour
{
    private Stack<IState> stateStack = new Stack<IState>();
    private Dictionary<Type, SubStateMachine> subStateMachines = new Dictionary<Type, SubStateMachine>();
    
    public void PushState(IState state)
    {
        if (stateStack.Count > 0)
            stateStack.Peek().Exit();
        
        stateStack.Push(state);
        state.Enter();
    }
    
    public IState PopState()
    {
        if (stateStack.Count > 0)
        {
            IState state = stateStack.Pop();
            state.Exit();
            
            if (stateStack.Count > 0)
                stateStack.Peek().Enter();
            
            return state;
        }
        
        return null;
    }
    
    public void RegisterSubStateMachine(Type parentStateType, SubStateMachine subStateMachine)
    {
        subStateMachines[parentStateType] = subStateMachine;
    }
    
    public SubStateMachine GetSubStateMachine(Type parentStateType)
    {
        subStateMachines.TryGetValue(parentStateType, out SubStateMachine result);
        return result;
    }
}

3. 可序列化的状态配置

3.1 ScriptableObject状态配置

csharp

// 可序列化的状态配置
[CreateAssetMenu(fileName = "NewStateConfig", menuName = "State Machine/State Config")]
public class StateConfig : ScriptableObject
{
    [System.Serializable]
    public class StateTransition
    {
        public string FromState;
        public string ToState;
        public string Condition;
        public float Delay;
    }
    
    [System.Serializable]
    public class StateData
    {
        public string StateName;
        public AnimationClip Animation;
        public float AnimationSpeed = 1f;
        public AudioClip EnterSound;
        public AudioClip ExitSound;
        public float MinDuration = 0f;
        public float MaxDuration = float.MaxValue;
        public List<StateTransition> Transitions = new List<StateTransition>();
    }
    
    public List<StateData> States = new List<StateData>();
    
    public StateData GetStateData(string stateName)
    {
        return States.Find(s => s.StateName == stateName);
    }
}

// 基于配置的状态工厂
public class ConfigurableStateFactory : MonoBehaviour
{
    [SerializeField] private StateConfig stateConfig;
    
    private Dictionary<string, IState> createdStates = new Dictionary<string, IState>();
    private StateMachine stateMachine;
    
    private void Start()
    {
        stateMachine = GetComponent<StateMachine>();
        InitializeStates();
    }
    
    private void InitializeStates()
    {
        foreach (var stateData in stateConfig.States)
        {
            var state = CreateStateFromConfig(stateData);
            createdStates[stateData.StateName] = state;
            stateMachine.RegisterState(state);
        }
    }
    
    private IState CreateStateFromConfig(StateConfig.StateData data)
    {
        return new ConfigurableState(data);
    }
    
    private class ConfigurableState : IState
    {
        private readonly StateConfig.StateData data;
        private float timeInState;
        
        public ConfigurableState(StateConfig.StateData data)
        {
            this.data = data;
        }
        
        public void Enter()
        {
            timeInState = 0f;
            
            if (data.EnterSound != null)
            {
                // 播放音效
            }
            
            Debug.Log($"Entering state: {data.StateName}");
        }
        
        public void Update()
        {
            timeInState += Time.deltaTime;
            
            // 检查转换条件
            foreach (var transition in data.Transitions)
            {
                if (CheckTransitionCondition(transition))
                {
                    // 执行状态转换
                    break;
                }
            }
            
            // 检查最大持续时间
            if (timeInState >= data.MaxDuration)
            {
                // 强制转换状态
            }
        }
        
        public void Exit()
        {
            if (data.ExitSound != null)
            {
                // 播放音效
            }
            
            Debug.Log($"Exiting state: {data.StateName}");
        }
        
        private bool CheckTransitionCondition(StateConfig.StateTransition transition)
        {
            // 实现条件检查逻辑
            return false;
        }
    }
}

4. 可视化状态机编辑器扩展

4.1 自定义编辑器窗口

csharp

#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;

public class StateMachineEditor : EditorWindow
{
    private StateMachine targetMachine;
    private Vector2 scrollPosition;
    private GUIStyle stateStyle;
    private GUIStyle transitionStyle;
    
    [MenuItem("Tools/State Machine Editor")]
    public static void ShowWindow()
    {
        GetWindow<StateMachineEditor>("State Machine Editor");
    }
    
    private void OnEnable()
    {
        stateStyle = new GUIStyle(GUI.skin.box)
        {
            alignment = TextAnchor.MiddleCenter,
            fontStyle = FontStyle.Bold
        };
        
        transitionStyle = new GUIStyle(GUI.skin.label)
        {
            alignment = TextAnchor.MiddleCenter
        };
    }
    
    private void OnGUI()
    {
        EditorGUILayout.BeginHorizontal();
        
        // 选择目标状态机
        targetMachine = EditorGUILayout.ObjectField("Target State Machine", 
            targetMachine, typeof(StateMachine), true) as StateMachine;
        
        EditorGUILayout.EndHorizontal();
        
        if (targetMachine == null)
        {
            EditorGUILayout.HelpBox("Please assign a State Machine component", MessageType.Info);
            return;
        }
        
        scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
        
        DrawStateMachineGraph();
        
        EditorGUILayout.EndScrollView();
        
        if (GUILayout.Button("Refresh"))
        {
            Repaint();
        }
    }
    
    private void DrawStateMachineGraph()
    {
        // 绘制状态节点
        foreach (var state in GetStates())
        {
            DrawStateNode(state);
        }
        
        // 绘制状态转换
        foreach (var transition in GetTransitions())
        {
            DrawTransitionArrow(transition);
        }
    }
    
    private void DrawStateNode(IState state)
    {
        Rect rect = new Rect(50, 50, 150, 50);
        GUI.Box(rect, state.GetType().Name, stateStyle);
        
        // 如果是当前状态,高亮显示
        if (IsCurrentState(state))
        {
            EditorGUI.DrawRect(rect, new Color(0, 1, 0, 0.2f));
        }
    }
    
    private void DrawTransitionArrow(StateTransition transition)
    {
        // 绘制箭头和标签
        Handles.color = Color.white;
        Handles.DrawLine(transition.FromPosition, transition.ToPosition);
        
        Vector2 labelPosition = (transition.FromPosition + transition.ToPosition) / 2;
        GUI.Label(new Rect(labelPosition.x, labelPosition.y, 100, 20), 
            transition.Condition, transitionStyle);
    }
    
    private bool IsCurrentState(IState state)
    {
        return targetMachine.CurrentState == state;
    }
    
    private List<IState> GetStates()
    {
        // 通过反射获取所有状态
        return new List<IState>();
    }
    
    private List<StateTransition> GetTransitions()
    {
        // 获取状态转换信息
        return new List<StateTransition>();
    }
    
    private class StateTransition
    {
        public Vector2 FromPosition;
        public Vector2 ToPosition;
        public string Condition;
    }
}
#endif

5. 异步状态支持

5.1 协程状态

csharp

// 支持协程的状态接口
public interface ICoroutineState : IState
{
    IEnumerator EnterCoroutine();
    IEnumerator ExitCoroutine();
    bool IsTransitioning { get; }
}

// 协程状态机
public class CoroutineStateMachine : MonoBehaviour
{
    private ICoroutineState currentState;
    private Coroutine enterCoroutine;
    private Coroutine exitCoroutine;
    
    public async void ChangeState(ICoroutineState newState)
    {
        if (currentState != null && currentState.IsTransitioning)
        {
            Debug.LogWarning("State is transitioning, cannot change now");
            return;
        }
        
        // 退出当前状态
        if (currentState != null)
        {
            currentState.Exit();
            if (exitCoroutine != null)
                StopCoroutine(exitCoroutine);
            
            exitCoroutine = StartCoroutine(currentState.ExitCoroutine());
            await WaitForCoroutine(exitCoroutine);
        }
        
        // 进入新状态
        currentState = newState;
        currentState.Enter();
        
        if (enterCoroutine != null)
            StopCoroutine(enterCoroutine);
        
        enterCoroutine = StartCoroutine(currentState.EnterCoroutine());
        await WaitForCoroutine(enterCoroutine);
    }
    
    private async System.Threading.Tasks.Task WaitForCoroutine(Coroutine coroutine)
    {
        while (coroutine != null)
        {
            await System.Threading.Tasks.Task.Yield();
        }
    }
    
    private void Update()
    {
        if (currentState != null && !currentState.IsTransitioning)
        {
            currentState.Update();
        }
    }
}

// 示例:加载场景状态
public class LoadSceneState : ICoroutineState
{
    private string sceneName;
    private AsyncOperation loadOperation;
    
    public bool IsTransitioning => loadOperation != null && !loadOperation.isDone;
    
    public LoadSceneState(string sceneName)
    {
        this.sceneName = sceneName;
    }
    
    public void Enter()
    {
        Debug.Log($"Starting to load scene: {sceneName}");
    }
    
    public IEnumerator EnterCoroutine()
    {
        loadOperation = UnityEngine.SceneManagement.SceneManager.LoadSceneAsync(sceneName);
        loadOperation.allowSceneActivation = false;
        
        while (!loadOperation.isDone)
        {
            float progress = Mathf.Clamp01(loadOperation.progress / 0.9f);
            Debug.Log($"Loading progress: {progress * 100}%");
            
            if (progress >= 0.9f)
            {
                // 等待输入或延迟后激活场景
                yield return new WaitForSeconds(1f);
                loadOperation.allowSceneActivation = true;
            }
            
            yield return null;
        }
    }
    
    public void Update()
    {
        // 更新加载界面等
    }
    
    public void Exit()
    {
        Debug.Log("Exiting load scene state");
    }
    
    public IEnumerator ExitCoroutine()
    {
        yield return null;
    }
}

6. 状态模式与UniRx结合

6.1 响应式状态机

csharp

using UniRx;
using UniRx.Triggers;

public class ReactiveStateMachine : MonoBehaviour
{
    private ReactiveProperty<IState> currentState = new ReactiveProperty<IState>();
    private CompositeDisposable disposables = new CompositeDisposable();
    
    public IReadOnlyReactiveProperty<IState> CurrentState => currentState;
    
    public void Initialize(IState initialState)
    {
        ChangeState(initialState);
        
        // 每帧更新当前状态
        this.UpdateAsObservable()
            .Where(_ => currentState.Value != null)
            .Subscribe(_ => currentState.Value.Update())
            .AddTo(disposables);
        
        // 状态变化时的回调
        currentState
            .Skip(1) // 跳过初始值
            .Pairwise()
            .Subscribe(pair => 
            {
                Debug.Log($"State changed from {pair.Previous?.GetType().Name} to {pair.Current?.GetType().Name}");
            })
            .AddTo(disposables);
    }
    
    public void ChangeState(IState newState)
    {
        if (currentState.Value != null)
            currentState.Value.Exit();
        
        currentState.Value = newState;
        newState.Enter();
    }
    
    private void OnDestroy()
    {
        disposables.Dispose();
        currentState.Value?.Exit();
    }
}

// 响应式状态示例
public class ReactiveIdleState : IState
{
    private Subject<Unit> onEnter = new Subject<Unit>();
    private Subject<Unit> onExit = new Subject<Unit>();
    private Subject<float> onUpdate = new Subject<float>();
    
    public IObservable<Unit> OnEnter => onEnter;
    public IObservable<Unit> OnExit => onExit;
    public IObservable<float> OnUpdate => onUpdate;
    
    public void Enter()
    {
        onEnter.OnNext(Unit.Default);
    }
    
    public void Update()
    {
        onUpdate.OnNext(Time.deltaTime);
    }
    
    public void Exit()
    {
        onExit.OnNext(Unit.Default);
    }
}

7. 状态模式的单元测试

7.1 可测试的状态机

csharp

// 可测试的状态接口
public interface ITestableState : IState
{
    int EnterCount { get; }
    int UpdateCount { get; }
    int ExitCount { get; }
    object LastEnterParam { get; }
}

// 单元测试辅助类
public class StateMachineTester : MonoBehaviour
{
    private StateMachine stateMachine;
    private Dictionary<Type, ITestableState> testStates = new Dictionary<Type, ITestableState>();
    
    public void SetupTest()
    {
        stateMachine = GetComponent<StateMachine>();
        
        // 替换为测试状态
        ReplaceStatesWithTestVersions();
    }
    
    private void ReplaceStatesWithTestVersions()
    {
        // 通过反射获取所有状态并替换为测试版本
        var fieldInfo = typeof(StateMachine).GetField("states", 
            System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
        
        if (fieldInfo != null)
        {
            var states = fieldInfo.GetValue(stateMachine) as Dictionary<System.Type, IState>;
            
            foreach (var kvp in states)
            {
                if (kvp.Value is ITestableState testableState)
                {
                    testStates[kvp.Key] = testableState;
                }
            }
        }
    }
    
    public void AssertStateEntered<TState>(int expectedCount = 1) where TState : ITestableState
    {
        Type type = typeof(TState);
        if (testStates.TryGetValue(type, out ITestableState state))
        {
            Assert.AreEqual(expectedCount, state.EnterCount, 
                $"State {type.Name} should have been entered {expectedCount} times");
        }
    }
    
    public void AssertStateExited<TState>(int expectedCount = 1) where TState : ITestableState
    {
        Type type = typeof(TState);
        if (testStates.TryGetValue(type, out ITestableState state))
        {
            Assert.AreEqual(expectedCount, state.ExitCount, 
                $"State {type.Name} should have been exited {expectedCount} times");
        }
    }
    
    public void SimulateStateTransition<TFrom, TTo>() where TFrom : ITestableState where TTo : ITestableState
    {
        // 模拟状态转换条件
        // ...
    }
}

// 测试用状态
public class TestIdleState : IdleState, ITestableState
{
    public int EnterCount { get; private set; }
    public int UpdateCount { get; private set; }
    public int ExitCount { get; private set; }
    public object LastEnterParam { get; private set; }
    
    public TestIdleState(PlayerController player) : base(player) { }
    
    public override void Enter(object param = null)
    {
        EnterCount++;
        LastEnterParam = param;
        base.Enter(param);
    }
    
    public override void Update()
    {
        UpdateCount++;
        base.Update();
    }
    
    public override void Exit()
    {
        ExitCount++;
        base.Exit();
    }
}

8. 性能监控与调试

8.1 状态机性能监控

csharp

public class StateMachineProfiler : MonoBehaviour
{
    [System.Serializable]
    public class StateProfile
    {
        public string StateName;
        public float TotalTime;
        public int TransitionCount;
        public float AverageDuration;
    }
    
    private Dictionary<string, StateProfile> profiles = new Dictionary<string, StateProfile>();
    private string currentStateName;
    private float stateStartTime;
    
    public void OnStateChanged(string previousState, string newState)
    {
        // 记录上一个状态的时间
        if (!string.IsNullOrEmpty(previousState))
        {
            if (profiles.TryGetValue(previousState, out StateProfile profile))
            {
                profile.TotalTime += Time.time - stateStartTime;
                profile.TransitionCount++;
                profile.AverageDuration = profile.TotalTime / profile.TransitionCount;
            }
        }
        
        // 开始记录新状态
        currentStateName = newState;
        stateStartTime = Time.time;
        
        if (!profiles.ContainsKey(newState))
        {
            profiles[newState] = new StateProfile { StateName = newState };
        }
    }
    
    public void LogProfiles()
    {
        Debug.Log("=== State Machine Profile ===");
        foreach (var profile in profiles.Values)
        {
            Debug.Log($"{profile.StateName}: " +
                     $"Total={profile.TotalTime:F2}s, " +
                     $"Transitions={profile.TransitionCount}, " +
                     $"Avg={profile.AverageDuration:F2}s");
        }
    }
    
    // 在编辑器窗口中显示
    #if UNITY_EDITOR
    private void OnDrawGizmos()
    {
        GUIStyle style = new GUIStyle();
        style.normal.textColor = Color.yellow;
        
        Vector3 position = transform.position + Vector3.up * 2f;
        UnityEditor.Handles.Label(position, $"Current: {currentStateName}", style);
    }
    #endif
}

9. 总结与最佳实践

9.1 选择合适的状态模式变体

使用场景 推荐实现 优点
简单状态(<5个) 枚举 + switch 简单直接,性能好
中等复杂度 经典状态模式 结构清晰,易维护
复杂AI/角色 分层状态机 支持状态嵌套,减少重复
需要配置 ScriptableObject状态 非程序员可配置,数据驱动
异步操作 协程状态机 支持长时间运行操作

9.2 性能优化建议

  1. 状态缓存:复用状态对象,避免频繁GC

  2. 条件检查优化:使用事件驱动代替每帧检查

  3. 状态预加载:提前初始化可能用到的状态

  4. 避免深度嵌套:过深的子状态机影响性能

  5. 使用值类型:状态参数尽量使用值类型

9.3 调试技巧

  1. 状态历史:记录状态变化历史便于调试

  2. 可视化工具:开发编辑器扩展监控状态

  3. 断点条件:在特定状态转换时触发断点

  4. 日志分级:不同详细程度的调试日志

  5. 性能统计:监控每个状态的执行时间和频率

通过以上补充和优化,状态模式可以更好地适应Unity项目中的各种复杂需求,提供高性能、易维护且功能强大的状态管理解决方案。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐