在实际游戏开发或技能设计项目中我们经常需要为一个复杂的技能或动作设计一套连贯的、有节奏感的执行逻辑。一个典型的例子就是“后羿射日”这类多段式技能它要求从起手、蓄力、释放到收尾每个阶段都有明确的视觉表现、时间控制和伤害判定。如果代码写得松散就容易出现动作卡顿、伤害计算时机错乱、特效与逻辑不同步等问题导致玩家体验极差。本文将以“后羿射日”这个四段式技能为案例拆解如何实现一套“秒级”响应的技能系统。这里的“秒级”并非指技能持续一秒而是强调代码的执行效率和时序控制的精确性确保从“眼神锁定”瞄准到“箭矢爆炸”伤害生效的整个过程逻辑清晰、性能可控、且易于调试和扩展。我们将从技能状态机设计、时间轴管理、事件驱动和伤害结算四个核心层面构建一个可用于实战的轻量级技能框架。无论你是在开发MOBA、ARPG还是其他需要复杂技能逻辑的游戏这套思路都能提供直接的参考。1. 理解多段技能的核心状态与时序在动手写代码之前必须厘清多段技能的本质。它不是一个简单的函数调用而是一个随时间推进、拥有多个明确阶段的状态机。每个阶段或称为“段”都有其专属的生命周期开始、持续、结束并可能触发不同的事件。1.1 技能阶段拆解以后羿射日为例以“后羿射日”为例我们可以将其抽象为四个阶段起手/瞄准阶段 (Windup)角色做出瞄准动作播放动画可能有一个准星特效。此阶段通常可被移动或受击打断。蓄力/拉弓阶段 (Channeling)角色持续拉弓能量或特效不断聚集。此阶段可能持续数秒期间玩家可以调整方向但移动会中断。释放/出箭阶段 (Casting)箭矢射出飞行物被创建并朝目标飞行。这是伤害判定的核心准备阶段。收尾/爆炸阶段 (Follow-through)箭矢命中目标或到达终点触发爆炸特效、范围伤害和击退等效果。技能效果真正生效。这四段必须严格按顺序执行且每段都有其特定的持续时间、可中断性和触发事件。1.2 为什么需要“状态机”和“时间轴”如果只用一堆布尔标志和if-else来控制代码会迅速变得难以维护// 反面示例难以维护的硬编码控制 bool isAiming false; bool isCharging false; bool isFlying false; float timer 0f; void Update() { if (isAiming) { timer Time.deltaTime; if (timer 0.5f) { isAiming false; isCharging true; timer 0; } } else if (isCharging) { // ... 更多混乱的 if-else } }问题状态切换逻辑散落各处难以增加新阶段或修改时序且Update函数会越来越臃肿。解决方案是引入一个轻量级状态机来管理阶段并配合一个时间轴Timeline或计时器系统来驱动状态转换和事件触发。这样技能的逻辑就变成了对“状态”和“时间点”的定义与响应。2. 环境准备与项目结构我们将使用 C# 和 Unity 引擎作为演示环境因为其组件化和帧更新机制非常适合展示游戏逻辑。但核心设计模式状态机、事件、时间轴是语言和引擎无关的你可以轻松移植到 Cocos、Unreal 或自研引擎中。2.1 基础环境要求Unity 版本: 2020.3 LTS 或更高版本确保稳定的 MonoBehaviour 生命周期。脚本运行时版本: .NET 4.x 或更高以使用更丰富的 API。核心概念理解: 了解 Unity 的GameObject、Component、Update循环、协程 (Coroutine) 和事件 (Action/delegate) 的基本用法。2.2 创建技能系统的核心目录结构在 Unity 项目中的Assets/Scripts下创建如下结构Assets/ └── Scripts/ ├── Skills/ │ ├── Interfaces/ # 接口定义 │ ├── Core/ # 核心状态机、计时器 │ ├── Components/ # 挂载在GameObject上的组件 │ └── HouYi/ # 后羿射日技能具体实现 └── Demo/ # 测试场景和控制器这个结构确保了核心框架与具体技能实现的分离。2.3 定义核心接口ISkillPhase首先为技能的每一个“阶段”定义一个契约。在Interfaces/ISkillPhase.cs中// Assets/Scripts/Skills/Interfaces/ISkillPhase.cs using UnityEngine; namespace Skills.Interfaces { /// summary /// 技能阶段接口。每一个具体的阶段如瞄准、蓄力都应实现此接口。 /// /summary public interface ISkillPhase { /// summary /// 阶段名称用于调试和日志。 /// /summary string PhaseName { get; } /// summary /// 进入该阶段时调用。 /// /summary /// param namecontext技能上下文包含施法者、目标等信息。/param void OnEnter(SkillContext context); /// summary /// 每帧更新时调用如果该阶段需要持续帧更新逻辑。 /// /summary /// param namedeltaTime帧间隔时间。/param void OnUpdate(float deltaTime); /// summary /// 离开该阶段时调用。 /// /summary void OnExit(); /// summary /// 检查该阶段是否已经完成例如计时结束、条件满足。 /// /summary /// returnstrue 表示阶段完成可以切换到下一阶段。/returns bool IsFinished(); } }这个接口强制每个阶段都拥有标准的生命周期方法这是实现状态机切换的基础。3. 构建技能状态机与时间轴驱动器有了阶段定义我们需要一个管理器来按顺序执行这些阶段并控制切换时机。3.1 实现技能状态机 (SkillStateMachine)在Core/SkillStateMachine.cs中// Assets/Scripts/Skills/Core/SkillStateMachine.cs using System.Collections.Generic; using Skills.Interfaces; using UnityEngine; namespace Skills.Core { /// summary /// 技能状态机。负责按顺序管理和执行技能阶段。 /// /summary public class SkillStateMachine { private ListISkillPhase _phases; private int _currentPhaseIndex -1; private ISkillPhase _currentPhase (_currentPhaseIndex 0 _currentPhaseIndex _phases.Count) ? _phases[_currentPhaseIndex] : null; private SkillContext _context; public SkillStateMachine(SkillContext context) { _phases new ListISkillPhase(); _context context; } /// summary /// 添加一个技能阶段到执行序列。 /// /summary public void AddPhase(ISkillPhase phase) { _phases.Add(phase); } /// summary /// 启动状态机从第一个阶段开始。 /// /summary public void Start() { if (_phases.Count 0) { Debug.LogWarning(SkillStateMachine has no phases to execute.); return; } _currentPhaseIndex 0; _currentPhase?.OnEnter(_context); } /// summary /// 每帧更新驱动当前阶段逻辑并检查是否切换。 /// /summary public void Update(float deltaTime) { if (_currentPhase null) return; // 更新当前阶段 _currentPhase.OnUpdate(deltaTime); // 检查当前阶段是否完成 if (_currentPhase.IsFinished()) { // 退出当前阶段 _currentPhase.OnExit(); // 切换到下一个阶段 _currentPhaseIndex; if (_currentPhaseIndex _phases.Count) { _currentPhase.OnEnter(_context); } else { // 所有阶段执行完毕 Debug.Log(Skill execution completed.); // 这里可以触发技能完成事件 } } } /// summary /// 强制中断技能。 /// /summary public void Interrupt() { _currentPhase?.OnExit(); _currentPhaseIndex _phases.Count; // 跳到末尾结束状态机 Debug.Log(Skill interrupted.); } } }这个状态机维护了一个阶段列表并在Update中驱动当前阶段。当IsFinished()返回true时自动切换到下一阶段。3.2 定义技能上下文 (SkillContext)上下文对象用于在各个阶段间传递共享数据。在Core/SkillContext.cs中// Assets/Scripts/Skills/Core/SkillContext.cs using UnityEngine; namespace Skills.Core { /// summary /// 技能执行上下文封装了技能释放所需的所有环境数据。 /// /summary public class SkillContext { public GameObject Caster { get; set; } // 施法者 public Vector3 TargetPosition { get; set; } // 目标位置地面点 public GameObject TargetUnit { get; set; } // 目标单位可选 public Transform CastOrigin { get; set; } // 技能释放原点如手部 // 可以扩展其他数据如技能等级、威力加成等 public float PowerMultiplier { get; set; } 1.0f; } }3.3 创建基于时间的阶段基类 (TimedPhase)很多技能阶段都是基于时间的例如蓄力2秒。我们创建一个通用的可计时阶段基类。在Core/Phases/TimedPhase.cs中// Assets/Scripts/Skills/Core/Phases/TimedPhase.cs using Skills.Interfaces; using UnityEngine; namespace Skills.Core.Phases { /// summary /// 一个具有固定持续时间的技能阶段基类。 /// /summary public abstract class TimedPhase : ISkillPhase { public abstract string PhaseName { get; } protected SkillContext Context { get; private set; } protected float Duration { get; set; } protected float ElapsedTime { get; private set; } protected TimedPhase(float duration) { Duration duration; } public virtual void OnEnter(SkillContext context) { Context context; ElapsedTime 0f; Debug.Log($Entering phase: {PhaseName}, Duration: {Duration}s); // 子类可以在这里播放动画、生成特效等 } public virtual void OnUpdate(float deltaTime) { ElapsedTime deltaTime; // 子类可以在这里更新特效、检查输入等 } public virtual void OnExit() { Debug.Log($Exiting phase: {PhaseName}); // 子类可以在这里清理资源 } public virtual bool IsFinished() { // 默认根据时间判断是否完成 return ElapsedTime Duration; } } }这个基类处理了通用的计时逻辑具体阶段只需继承它并重写OnEnter,OnUpdate来添加特定行为。4. 实现“后羿射日”四段技能现在我们用上面构建的框架来实现具体的四个阶段。4.1 第一阶段瞄准 (AimPhase)在HouYi/Phases/AimPhase.cs中// Assets/Scripts/Skills/HouYi/Phases/AimPhase.cs using Skills.Core.Phases; using UnityEngine; namespace Skills.HouYi.Phases { public class AimPhase : TimedPhase { public override string PhaseName Aim; private GameObject _aimingReticle; // 瞄准准星特效 public AimPhase(float duration) : base(duration) { } public override void OnEnter(SkillContext context) { base.OnEnter(context); // 1. 播放瞄准动画 var animator context.Caster.GetComponentAnimator(); animator?.SetTrigger(Aim); // 2. 在目标位置生成一个准星特效假设有预制体 // _aimingReticle GameObject.Instantiate(aimReticlePrefab, context.TargetPosition, Quaternion.identity); Debug.Log($Aiming at position: {context.TargetPosition}); } public override void OnUpdate(float deltaTime) { base.OnUpdate(deltaTime); // 可以在这里让准星跟随鼠标或摇杆微调目标位置 // if (Input.GetMouseButton(0)) { ... } } public override void OnExit() { base.OnExit(); // 销毁准星特效 // if (_aimingReticle ! null) GameObject.Destroy(_aimingReticle); } public override bool IsFinished() { // 除了时间到也可以被外部事件中断如移动 return base.IsFinished() || IsInterruptedByMovement(); } private bool IsInterruptedByMovement() { // 简单示例如果施法者移动了则中断瞄准 // 实际项目中需要更精细的判断如速度阈值 return false; // 此处简化 } } }4.2 第二阶段蓄力 (ChargePhase)在HouYi/Phases/ChargePhase.cs中// Assets/Scripts/Skills/HouYi/Phases/ChargePhase.cs using Skills.Core.Phases; using UnityEngine; namespace Skills.HouYi.Phases { public class ChargePhase : TimedPhase { public override string PhaseName Charge; private ParticleSystem _chargeEffect; // 蓄力特效 private float _chargePower; // 蓄力强度可能随时间增长 public ChargePhase(float duration) : base(duration) { } public override void OnEnter(SkillContext context) { base.OnEnter(context); _chargePower 0f; // 播放蓄力动画 var animator context.Caster.GetComponentAnimator(); animator?.SetBool(IsCharging, true); // 开始播放蓄力粒子特效 // _chargeEffect GameObject.Instantiate(chargeEffectPrefab, context.CastOrigin).GetComponentParticleSystem(); // _chargeEffect.Play(); Debug.Log(Start charging...); } public override void OnUpdate(float deltaTime) { base.OnUpdate(deltaTime); // 蓄力强度随时间的增长线性或曲线 _chargePower Mathf.Clamp01(ElapsedTime / Duration); // 可以根据_chargePower调整特效大小、颜色等 // if (_chargeEffect ! null) _chargeEffect.main.startSize _chargePower * 2f; // 检查是否提前释放例如玩家松开按键 if (Input.GetMouseButtonUp(0)) // 假设鼠标左键控制蓄力 { // 可以设置一个标志让IsFinished提前返回true // 这里简化处理直接修改计时器来“完成”蓄力 ElapsedTime Duration; } } public override void OnExit() { base.OnExit(); // 停止蓄力动画和特效 var animator Context.Caster.GetComponentAnimator(); animator?.SetBool(IsCharging, false); // if (_chargeEffect ! null) _chargeEffect.Stop(); Debug.Log($Charge completed with power: {_chargePower}); // 将蓄力强度存入上下文供后续阶段使用 Context.PowerMultiplier 1.0f _chargePower; // 例如基础1倍蓄满2倍 } } }4.3 第三阶段释放/箭矢飞行 (CastPhase)这个阶段通常不是纯计时而是创建一个飞行物箭矢并监听其命中事件。我们创建一个非TimedPhase的普通阶段。在HouYi/Phases/CastPhase.cs中// Assets/Scripts/Skills/HouYi/Phases/CastPhase.cs using Skills.Interfaces; using UnityEngine; namespace Skills.HouYi.Phases { public class CastPhase : ISkillPhase { public string PhaseName Cast; private SkillContext _context; private GameObject _arrowInstance; private bool _hasHit false; public void OnEnter(SkillContext context) { _context context; _hasHit false; // 1. 播放射箭动画 var animator context.Caster.GetComponentAnimator(); animator?.SetTrigger(Shoot); // 2. 实例化箭矢飞行物 // _arrowInstance GameObject.Instantiate(arrowPrefab, context.CastOrigin.position, Quaternion.identity); // var arrowScript _arrowInstance.GetComponentArrowProjectile(); // arrowScript.Initialize(context.TargetPosition, OnArrowHit); Debug.Log(Arrow released!); } public void OnUpdate(float deltaTime) { // 此阶段的主要逻辑由飞行物脚本驱动这里可以检查超时 // 例如如果箭矢飞行超过5秒仍未命中强制结束 } public void OnExit() { // 如果箭矢还未销毁例如未命中任何目标而飞远在此清理 // if (_arrowInstance ! null) GameObject.Destroy(_arrowInstance); Debug.Log(Cast phase exit.); } public bool IsFinished() { // 当箭矢命中目标触发OnArrowHit或超时时此阶段结束 return _hasHit; } // 由箭矢飞行物脚本回调 private void OnArrowHit(GameObject hitObject, Vector3 hitPoint) { _hasHit true; // 将命中的目标和点信息存储供下一阶段爆炸使用 _context.TargetUnit hitObject; _context.TargetPosition hitPoint; Debug.Log($Arrow hit: {hitObject.name} at {hitPoint}); } } }这里的关键是CastPhase的完成不依赖于固定时间而是依赖于一个异步事件箭矢命中。这体现了事件驱动设计的灵活性。4.4 第四阶段爆炸/伤害结算 (ExplosionPhase)在HouYi/Phases/ExplosionPhase.cs中// Assets/Scripts/Skills/HouYi/Phases/ExplosionPhase.cs using Skills.Core.Phases; using UnityEngine; namespace Skills.HouYi.Phases { public class ExplosionPhase : TimedPhase { public override string PhaseName Explosion; private GameObject _explosionEffect; public ExplosionPhase(float duration) : base(duration) { } public override void OnEnter(SkillContext context) { base.OnEnter(context); // 1. 播放爆炸特效 // _explosionEffect GameObject.Instantiate(explosionPrefab, context.TargetPosition, Quaternion.identity); // 2. 计算并施加伤害 ApplyDamage(context); // 3. 可能附加击退、燃烧等效果 ApplyKnockback(context); Debug.Log($Explosion at {context.TargetPosition} with power {context.PowerMultiplier}); } private void ApplyDamage(SkillContext context) { // 基础伤害 * 蓄力倍数 float baseDamage 100f; float finalDamage baseDamage * context.PowerMultiplier; // 查找爆炸范围内的所有敌人 Collider[] hitColliders Physics.OverlapSphere(context.TargetPosition, 5f); // 5米半径 foreach (var collider in hitColliders) { var enemy collider.GetComponentEnemyHealth(); // 假设有一个 EnemyHealth 组件 if (enemy ! null) { enemy.TakeDamage(finalDamage); Debug.Log($Dealt {finalDamage} damage to {enemy.name}); } } } private void ApplyKnockback(SkillContext context) { // 击退逻辑... } public override void OnExit() { base.OnExit(); // 可选特效播放完毕后销毁 // if (_explosionEffect ! null) GameObject.Destroy(_explosionEffect, 1.0f); } } }4.5 组装完整的后羿射日技能创建一个HouYiSkill组件将其挂载到英雄角色上用于启动技能。在HouYi/HouYiSkill.cs中// Assets/Scripts/Skills/HouYi/HouYiSkill.cs using Skills.Core; using Skills.HouYi.Phases; using UnityEngine; namespace Skills.HouYi { public class HouYiSkill : MonoBehaviour { private SkillStateMachine _stateMachine; void Update() { // 每帧驱动状态机 _stateMachine?.Update(Time.deltaTime); // 测试按下空格键释放技能 if (Input.GetKeyDown(KeyCode.Space)) { ExecuteSkill(); } } public void ExecuteSkill() { // 1. 创建技能上下文 var context new SkillContext { Caster this.gameObject, TargetPosition GetMouseWorldPosition(), // 假设一个获取鼠标位置的方法 CastOrigin transform.Find(HandPos) // 手部变换节点 }; // 2. 创建状态机并添加四个阶段 _stateMachine new SkillStateMachine(context); _stateMachine.AddPhase(new AimPhase(0.8f)); // 瞄准0.8秒 _stateMachine.AddPhase(new ChargePhase(2.0f)); // 蓄力最多2秒 _stateMachine.AddPhase(new CastPhase()); // 释放箭矢事件驱动 _stateMachine.AddPhase(new ExplosionPhase(0.5f));// 爆炸持续0.5秒 // 3. 启动状态机 _stateMachine.Start(); Debug.Log(Hou Yi Skill: Sun Piercer started!); } private Vector3 GetMouseWorldPosition() { // 简化的鼠标位置获取实际项目可能需要射线检测地面 Ray ray Camera.main.ScreenPointToRay(Input.mousePosition); if (Physics.Raycast(ray, out RaycastHit hit, 100f)) { return hit.point; } return ray.origin ray.direction * 10f; } } }5. 运行验证与调试将HouYiSkill脚本挂载到你的英雄角色GameObject上。运行游戏将鼠标指向某个位置按下空格键。在 Unity 的 Console 窗口中你应该能看到按顺序输出的日志Hou Yi Skill: Sun Piercer started! Entering phase: Aim, Duration: 0.8s Aiming at position: (x, y, z) Exiting phase: Aim Entering phase: Charge, Duration: 2s Start charging... Charge completed with power: 1 Exiting phase: Charge Entering phase: Cast Arrow released! Arrow hit: Enemy (name) at (x, y, z) Exiting phase: Cast Entering phase: Explosion, Duration: 0.5s Explosion at (x, y, z) with power 2 Dealt 200 damage to Enemy (name) Exiting phase: Explosion Skill execution completed.这验证了技能四段逻辑被状态机严格按顺序驱动。你可以通过调整每个TimedPhase的Duration参数来改变技能节奏。6. 关键优化与生产环境考量上述代码是一个清晰的教学示例但在真实项目中还需要考虑更多工程化问题。6.1 性能优化对象池与事件解耦对象池频繁实例化/销毁特效GameObject.Instantiate/Destroy会产生GC垃圾回收压力。对于箭矢、爆炸特效等应使用对象池进行复用。// 伪代码示例 public class EffectPool { private QueueGameObject _pool new QueueGameObject(); public GameObject GetEffect(GameObject prefab) { if (_pool.Count 0) return _pool.Dequeue(); return Instantiate(prefab); } public void ReturnEffect(GameObject effect) { _pool.Enqueue(effect); } }事件解耦CastPhase中箭矢命中回调是紧耦合的。更好的做法是使用一个全局或技能系统内的事件总线Event Bus让ArrowProjectile发布一个OnProjectileHit事件CastPhase订阅该事件。这样箭矢逻辑与技能阶段完全解耦。6.2 配置数据外置将技能各阶段的持续时间、伤害值、特效预制体路径等硬编码数据提取到ScriptableObject或 JSON/XML 配置文件中。这允许策划人员在不修改代码的情况下调整技能平衡。// Assets/Scripts/Skills/HouYi/HouYiSkillConfig.asset (ScriptableObject) [CreateAssetMenu] public class HouYiSkillConfig : ScriptableObject { public float aimDuration 0.8f; public float maxChargeDuration 2.0f; public float explosionDuration 0.5f; public float baseDamage 100f; public float explosionRadius 5f; // 引用特效预制体 public GameObject aimingReticlePrefab; public GameObject chargeEffectPrefab; public GameObject arrowPrefab; public GameObject explosionPrefab; }然后在HouYiSkill中引用这个配置资产并在创建阶段时传入对应的数值。6.3 网络同步对于多人游戏在多人对战游戏中技能释放必须同步。上述状态机模型非常适合做网络同步因为其状态明确、阶段清晰。权威服务器在服务器上运行完整的状态机。客户端预测客户端本地也运行状态机以提供即时反馈但需与服务器状态进行校正。同步关键事件只需要同步“技能开始”、“阶段切换”、“命中目标”等关键事件和时序而非每一帧的状态。可以使用帧编号或服务器时间进行同步。6.4 可中断性与资源清理我们的SkillStateMachine提供了Interrupt()方法。在实际项目中当角色被控制眩晕、沉默、死亡或主动取消时必须调用此方法。每个Phase的OnExit必须确保清理所有临时创建的对象特效、飞行物、重置动画状态和取消未完成的协程防止资源泄漏和状态残留。7. 常见问题排查清单在实现和调试此类技能系统时你可能会遇到以下典型问题问题现象可能原因检查与解决思路技能释放后没有任何反应日志也无输出。1.HouYiSkill组件未正确挂载或启用。2.ExecuteSkill()方法未被触发按键检测失败。3. 状态机Start()未被调用。1. 检查 Inspector 面板确认组件存在且 enabled 为 true。2. 在ExecuteSkill()第一行加Debug.Log确认方法被调用。3. 检查Update中驱动状态机的代码是否执行。技能卡在某个阶段无法进入下一阶段。1. 该阶段的IsFinished()条件始终不满足。2. 该阶段的OnUpdate或事件回调未正确设置完成标志。1. 在IsFinished()内加日志查看返回值。2. 对于事件驱动的阶段如CastPhase检查回调函数是否被正确触发和调用。特效残留或对象未销毁。1. 阶段OnExit()中未销毁临时创建的对象。2. 技能被中断 (Interrupt) 时OnExit可能未被调用。1. 确保每个创建了GameObject的阶段都在OnExit中清理。2. 在Interrupt方法中手动调用当前阶段的OnExit。伤害计算不正确或未生效。1.ExplosionPhase中查找敌人的逻辑有问题层过滤、半径。2. 伤害数值未从上下文 (SkillContext) 正确传递。1. 使用Physics.OverlapSphere的 layerMask 参数过滤非敌人物体。在场景中可视化爆炸半径Gizmos.DrawWireSphere。2. 调试context.PowerMultiplier在ChargePhase退出时的值确保它被正确设置。动画与逻辑不同步。1. 动画状态机 (Animator) 的参数设置时机不对。2. 动画片段长度与代码中设置的阶段时长不匹配。1. 在OnEnter中设置动画触发器在OnExit中重置布尔值。使用动画事件来驱动逻辑更精确。2. 调整代码中的Duration或使用动画的 normalizedTime 来驱动阶段切换。8. 扩展方向与最佳实践掌握了四段式技能的基础框架后你可以从以下几个方向进行深化和扩展技能编辑器可视化基于 Unity 的ScriptableObject和自定义编辑器窗口打造一个可视化的技能编辑工具让策划通过拖拽节点、连线的方式来配置技能阶段、时间和事件彻底将逻辑与数据分离。条件分支与组合技能扩展状态机使其支持阶段之间的条件跳转例如如果蓄力时间不足1秒则跳过一个强化阶段。甚至可以定义技能链一个技能结束后自动触发下一个技能。更精细的命中检测将CastPhase中的简单命中检测升级为带有弹道速度、重力下坠、穿透、弹射等复杂规则的投射物系统。Buff/Debuff 系统集成在SkillContext中引入施法者和目标的 Buff 列表在伤害计算、阶段时长、可中断性等逻辑中查询这些效果实现“减CD”、“增伤”、“免疫控制”等复杂效果。使用协程 (Coroutine) 替代部分 Update对于简单的延时等待可以使用yield return new WaitForSeconds(duration)的协程让代码更线性易读。但需注意协程在对象销毁时的停止问题以及与状态机主循环的配合。最关键的最佳实践是始终保持阶段职责单一。一个阶段只做一件事播放动画、聚集能量、发射投射物、结算伤害。这样当需要修改、调试或复用某个部分时代价最小也最符合“秒级”响应所追求的清晰与高效。