Unity C# 层次状态机HSM实战:3层状态嵌套实现角色复杂行为逻辑

Unity C# 层次状态机HSM实战:3层状态嵌套实现角色复杂行为逻辑
Unity C# 层次状态机HSM实战3层状态嵌套实现角色复杂行为逻辑在游戏开发中角色行为逻辑的复杂性往往随着游戏机制的丰富而呈指数级增长。想象一下你的游戏角色需要同时处理移动、攻击、技能释放、装备切换等多种行为每种行为又有多个子状态——比如攻击状态下又分为地面攻击、空中攻击、蓄力攻击等。传统的有限状态机(FSM)在这种场景下很快就会变得难以维护而层次状态机(HSM)则提供了一种优雅的解决方案。1. 为什么游戏开发需要层次状态机当角色行为逻辑变得复杂时传统的FSM会面临几个关键问题状态爆炸随着功能增加状态数量急剧增长导致难以管理代码重复相似状态(如地面攻击和空中攻击)需要重复编写大量相同逻辑过渡复杂状态间的转换关系变得错综复杂难以理解和维护层次状态机通过引入状态继承和层级结构解决了这些问题。让我们看一个典型游戏角色的状态层级示例角色状态 (Root) ├── 地面状态 (Grounded) │ ├── 待机 (Idle) │ ├── 移动 (Move) │ └── 地面攻击 (GroundAttack) └── 空中状态 (Airborne) ├── 跳跃 (Jump) ├── 下落 (Fall) └── 空中攻击 (AirAttack)在这个结构中子状态可以继承父状态的通用行为只需实现自己特有的逻辑。例如所有空中状态都会检查是否落地这个逻辑只需在Airborne父状态中实现一次。2. HSM核心实现3层嵌套状态机设计下面我们实现一个支持3层嵌套的HSM核心类。这个实现采用基于栈的状态管理确保状态进入/退出顺序正确。using System.Collections.Generic; using UnityEngine; public abstract class HSMState { protected HSMStateMachine stateMachine; protected HSMState parentState; protected ListHSMState subStates new ListHSMState(); protected HSMState currentSubState; public HSMState(HSMStateMachine stateMachine, HSMState parentState null) { this.stateMachine stateMachine; this.parentState parentState; } public abstract void Enter(); public abstract void Update(); public abstract void Exit(); public virtual void ChangeSubState(HSMState newState) { if (currentSubState ! null) { currentSubState.Exit(); } currentSubState newState; if (currentSubState ! null) { currentSubState.Enter(); } } public virtual void AddSubState(HSMState state) { subStates.Add(state); } } public class HSMStateMachine : MonoBehaviour { private StackHSMState stateStack new StackHSMState(); public void PushState(HSMState newState) { if (stateStack.Count 0) { stateStack.Peek().Exit(); } stateStack.Push(newState); newState.Enter(); } public void PopState() { if (stateStack.Count 0) { stateStack.Pop().Exit(); if (stateStack.Count 0) { stateStack.Peek().Enter(); } } } public void ChangeState(HSMState newState) { while (stateStack.Count 0) { PopState(); } PushState(newState); } private void Update() { if (stateStack.Count 0) { stateStack.Peek().Update(); } } }这个基础框架支持状态的嵌套和层级管理。现在让我们实现一个具体的角色状态机示例。3. 实战角色3层状态行为实现我们将实现一个包含以下层级结构的角色状态机角色状态 ├── 地面状态 │ ├── 待机 │ ├── 移动 │ └── 攻击 └── 空中状态 ├── 跳跃 ├── 下落 └── 空中攻击首先定义基础角色状态public class CharacterBaseState : HSMState { protected CharacterController character; protected Animator animator; public CharacterBaseState(HSMStateMachine stateMachine, HSMState parentState, CharacterController character) : base(stateMachine, parentState) { this.character character; this.animator character.GetComponentAnimator(); } public override void Enter() {} public override void Update() {} public override void Exit() {} protected bool IsGrounded() { return character.isGrounded; } }接着实现地面状态和它的子状态public class GroundedState : CharacterBaseState { public GroundedState(HSMStateMachine stateMachine, HSMState parentState, CharacterController character) : base(stateMachine, parentState, character) {} public override void Enter() { base.Enter(); animator.SetBool(IsGrounded, true); ChangeSubState(new IdleState(stateMachine, this, character)); } public override void Update() { base.Update(); if (!IsGrounded()) { stateMachine.PushState(new AirborneState(stateMachine, null, character)); return; } currentSubState?.Update(); } } public class IdleState : CharacterBaseState { public IdleState(HSMStateMachine stateMachine, HSMState parentState, CharacterController character) : base(stateMachine, parentState, character) {} public override void Enter() { base.Enter(); animator.SetFloat(Speed, 0); } public override void Update() { base.Update(); if (Input.GetAxisRaw(Horizontal) ! 0 || Input.GetAxisRaw(Vertical) ! 0) { parentState.ChangeSubState(new MoveState(stateMachine, parentState, character)); } else if (Input.GetButtonDown(Fire1)) { parentState.ChangeSubState(new GroundAttackState(stateMachine, parentState, character)); } } } public class MoveState : CharacterBaseState { public MoveState(HSMStateMachine stateMachine, HSMState parentState, CharacterController character) : base(stateMachine, parentState, character) {} public override void Enter() { base.Enter(); animator.SetFloat(Speed, 1); } public override void Update() { base.Update(); Vector3 moveInput new Vector3(Input.GetAxisRaw(Horizontal), 0, Input.GetAxisRaw(Vertical)); character.Move(moveInput.normalized * character.moveSpeed * Time.deltaTime); if (moveInput.magnitude 0.1f) { parentState.ChangeSubState(new IdleState(stateMachine, parentState, character)); } else if (Input.GetButtonDown(Fire1)) { parentState.ChangeSubState(new GroundAttackState(stateMachine, parentState, character)); } } } public class GroundAttackState : CharacterBaseState { private float attackTimer; public GroundAttackState(HSMStateMachine stateMachine, HSMState parentState, CharacterController character) : base(stateMachine, parentState, character) {} public override void Enter() { base.Enter(); animator.SetTrigger(Attack); attackTimer character.attackDuration; } public override void Update() { base.Update(); attackTimer - Time.deltaTime; if (attackTimer 0) { parentState.ChangeSubState(new IdleState(stateMachine, parentState, character)); } } }然后实现空中状态及其子状态public class AirborneState : CharacterBaseState { public AirborneState(HSMStateMachine stateMachine, HSMState parentState, CharacterController character) : base(stateMachine, parentState, character) {} public override void Enter() { base.Enter(); animator.SetBool(IsGrounded, false); ChangeSubState(new JumpState(stateMachine, this, character)); } public override void Update() { base.Update(); if (IsGrounded()) { stateMachine.PopState(); // 返回上一个状态(应该是GroundedState) return; } currentSubState?.Update(); } } public class JumpState : CharacterBaseState { private float jumpTimer; public JumpState(HSMStateMachine stateMachine, HSMState parentState, CharacterController character) : base(stateMachine, parentState, character) {} public override void Enter() { base.Enter(); animator.SetTrigger(Jump); character.velocity.y character.jumpForce; jumpTimer character.jumpDuration; } public override void Update() { base.Update(); jumpTimer - Time.deltaTime; if (jumpTimer 0 || character.velocity.y 0) { parentState.ChangeSubState(new FallState(stateMachine, parentState, character)); } else if (Input.GetButtonDown(Fire1)) { parentState.ChangeSubState(new AirAttackState(stateMachine, parentState, character)); } } } public class FallState : CharacterBaseState { public FallState(HSMStateMachine stateMachine, HSMState parentState, CharacterController character) : base(stateMachine, parentState, character) {} public override void Enter() { base.Enter(); animator.SetBool(IsFalling, true); } public override void Update() { base.Update(); if (Input.GetButtonDown(Fire1)) { parentState.ChangeSubState(new AirAttackState(stateMachine, parentState, character)); } } public override void Exit() { base.Exit(); animator.SetBool(IsFalling, false); } } public class AirAttackState : CharacterBaseState { private float attackTimer; public AirAttackState(HSMStateMachine stateMachine, HSMState parentState, CharacterController character) : base(stateMachine, parentState, character) {} public override void Enter() { base.Enter(); animator.SetTrigger(AirAttack); attackTimer character.airAttackDuration; } public override void Update() { base.Update(); attackTimer - Time.deltaTime; if (attackTimer 0) { if (character.velocity.y 0) { parentState.ChangeSubState(new JumpState(stateMachine, parentState, character)); } else { parentState.ChangeSubState(new FallState(stateMachine, parentState, character)); } } } }4. 状态机集成与使用最后我们需要将状态机集成到角色控制器中public class CharacterController : MonoBehaviour { public float moveSpeed 5f; public float jumpForce 10f; public float jumpDuration 0.5f; public float attackDuration 0.8f; public float airAttackDuration 0.6f; public Vector3 velocity; public bool isGrounded; private HSMStateMachine stateMachine; private Animator animator; private Rigidbody rb; private void Awake() { animator GetComponentAnimator(); rb GetComponentRigidbody(); stateMachine gameObject.AddComponentHSMStateMachine(); } private void Start() { stateMachine.PushState(new GroundedState(stateMachine, null, this)); } private void Update() { isGrounded Physics.Raycast(transform.position, Vector3.down, 0.1f); } private void FixedUpdate() { rb.velocity velocity; } public void Move(Vector3 direction) { velocity.x direction.x * moveSpeed; velocity.z direction.z * moveSpeed; } }这个实现展示了如何利用HSM管理复杂的角色行为逻辑。通过状态继承和层级结构我们避免了代码重复使逻辑更加清晰并且易于扩展新的状态和行为。