using System.Collections; using Animancer; using UnityEngine; namespace BaseGames.Enemies.Abilities { /// /// 追击 + 体接触伤害能力:起手(Skill_Start 张口) → 循环追击(Skill_Loop 快速爬行 + 接触伤害) /// → 收招(Skill_End)。追击动画由本能力全权驱动(Approach 模式不驱动步态,避免抢动画)。 /// 何时停止由 AI 决策层负责:AI 在"脱离感知"时中断本能力(ExternalRequest),本能力播 Skill_End 收招。 /// 依据 Docs/Game/Design/小怪设计-程序开发文档-01.md(E001 状态机 Skill_Start/Loop/End)。 /// public class ContactChaseAbility : EnemyAbilityBase { [Header("动画(起手 / 循环 / 收招)")] [SerializeField] private ClipTransition _startClip; // Skill_Start:张口(一次性) [SerializeField] private ClipTransition _loopClip; // Skill_Loop:快速爬行追击(循环) [SerializeField] private ClipTransition _endClip; // Skill_End:收招(一次性) [Header("接触伤害")] [SerializeField] private BodyContactDamage _contactDamage; protected override IEnumerator ExecuteCoroutine() { Phase = AbilityRunState.Active; _enemy.SetEngaged(true); // 起手:张口(一次性)。期间静止、不伤人;锁住动画防被步态覆盖。 if (_startClip.Clip != null) { _enemy.LockAnim(_startClip.Clip.length); _animancer.Play(_startClip); yield return EnemyAbilityWaits.Get(_startClip.Clip.length); } // 循环追击:切追击速度、开接触伤害、朝玩家移动,播 Skill_Loop(Approach 模式不抢步态)。 if (_enemy.Nav != null && _enemy.Stats != null) _enemy.Nav.SetSpeed(_enemy.Stats.RunSpeed); if (_loopClip.Clip != null) _animancer.Play(_loopClip); if (_contactDamage != null) _contactDamage.enabled = true; // 追击直到玩家消失或被 AI 中断(退出追击的决策归 AI)。小怪走 nav 寻路。 _enemy.Locomotion?.Approach(_enemy.PlayerTransform); while (_enemy.PlayerTransform != null) yield return null; // 玩家对象消失(正常收尾):收招 Skill_End。 DisableContact(); yield return PlaySkillEnd(); Cleanup(); } protected override void OnInterrupted(InterruptReason reason) { DisableContact(); // 丢失目标 = AI 主动退出追击态(ExternalRequest) → 播收招 Skill_End; // 受击/死亡/硬直等硬打断不播收招。协程已停,fire-and-forget;动画锁保证 Skill_End // 在其时长内不被巡逻步态盖掉。 if (reason == InterruptReason.ExternalRequest && _endClip.Clip != null) { _enemy.LockAnim(_endClip.Clip.length); _animancer.Play(_endClip); } Cleanup(); } private IEnumerator PlaySkillEnd() { if (_endClip.Clip == null) yield break; _enemy.LockAnim(_endClip.Clip.length); _animancer.Play(_endClip); yield return EnemyAbilityWaits.Get(_endClip.Clip.length); } private void DisableContact() { if (_contactDamage != null) _contactDamage.enabled = false; } private void Cleanup() { _enemy.SetEngaged(false); _enemy.Locomotion?.Stop(); } } }