状态模式是一种行为设计模式,允许对象在内部状态改变时改变其行为,使对象看起来像是修改了其类。
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. 状态模式的优缺点
优点:
单一职责原则:每个状态都有自己的类,代码更清晰
开闭原则:易于添加新状态而不修改现有代码
消除条件语句:减少了大量的if-else或switch语句
状态转换逻辑明确:状态转换集中管理
缺点:
类数量增加:每个状态都需要一个类
过度设计:对于简单状态机可能过度复杂
状态间依赖:状态之间可能有复杂的依赖关系
7. 实际应用建议
何时使用状态模式:
对象有多个状态,且状态间频繁转换
状态相关行为复杂,且有大量条件语句
需要清晰的状态管理结构
Unity特定优化:
结合Unity的Animator Controller
使用ScriptableObject创建可配置的状态
利用Unity的协程处理状态中的时序逻辑
性能考虑:
避免频繁创建状态对象(使用对象池或缓存)
对于简单状态机,考虑使用枚举+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; } } #endif5. 异步状态支持
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 性能优化建议
状态缓存:复用状态对象,避免频繁GC
条件检查优化:使用事件驱动代替每帧检查
状态预加载:提前初始化可能用到的状态
避免深度嵌套:过深的子状态机影响性能
使用值类型:状态参数尽量使用值类型
9.3 调试技巧
状态历史:记录状态变化历史便于调试
可视化工具:开发编辑器扩展监控状态
断点条件:在特定状态转换时触发断点
日志分级:不同详细程度的调试日志
性能统计:监控每个状态的执行时间和频率
通过以上补充和优化,状态模式可以更好地适应Unity项目中的各种复杂需求,提供高性能、易维护且功能强大的状态管理解决方案。