using System.Collections; using UnityEngine; using BaseGames.Combat; namespace BaseGames.Enemies.Abilities { /// /// 跳跃俯冲能力:起跳后以抛物线轨迹扑向目标,落地触发 AoE。 /// 流程:起跳预备 → 抛物线移动到目标 → 落地 AoE。 /// 使用 Rigidbody2D 直接物理推动;NavAgent 在执行期间被禁用以避免冲突。 /// [RequireComponent(typeof(Rigidbody2D))] public sealed class LeapAttackAbility : EnemyAbilityBase { [Header("跳跃参数")] [SerializeField] private float _jumpHeight = 4f; [SerializeField] private float _maxRange = 8f; [SerializeField] private float _windupTime = 0.35f; [SerializeField] private float _recoveryTime = 0.4f; [SerializeField] private LayerMask _groundMask; [Header("落地 AoE")] [SerializeField] private HitBox _landingHitBox; [SerializeField] private float _hitBoxActiveTime = 0.15f; private Rigidbody2D _rb; private float _origGravity; protected override void Awake() { base.Awake(); _rb = GetComponentInParent(); if (_rb != null) _origGravity = _rb.gravityScale; } protected override IEnumerator ExecuteCoroutine() { if (_rb == null || _enemy == null || _enemy.PlayerTransform == null) yield break; var atk = _config.attackSequence != null && _config.attackSequence.Length > 0 ? _config.attackSequence[0] : null; FaceTarget(_enemy.PlayerTransform); // Windup(可选动画) if (atk != null && atk.clip != null && _animancer != null) _animancer.Play(atk.clip); yield return EnemyAbilityWaits.Get(_windupTime); // 计算抛物线初速度 Vector2 from = _rb.position; Vector2 to = _enemy.PlayerTransform.position; float dx = Mathf.Clamp(to.x - from.x, -_maxRange, _maxRange); float g = Mathf.Abs(Physics2D.gravity.y) * _origGravity; float vy = Mathf.Sqrt(2f * g * Mathf.Max(0.1f, _jumpHeight)); float tUp = vy / g; float dyDown = (from.y + _jumpHeight) - to.y; float tDown = Mathf.Sqrt(2f * Mathf.Max(0.01f, dyDown) / g); float total = tUp + tDown; float vx = dx / Mathf.Max(0.1f, total); Phase = AbilityRunState.Active; _rb.velocity = new Vector2(vx, vy); // 空中飞行直到接触地面 yield return null; float airTimer = 0f; while (airTimer < total + 0.5f) { airTimer += Time.fixedDeltaTime; yield return new WaitForFixedUpdate(); if (airTimer > 0.1f && IsGrounded()) break; } _rb.velocity = new Vector2(0f, _rb.velocity.y); // 落地 AoE if (_landingHitBox != null) { var src = atk != null ? atk.damageSource : null; _landingHitBox.Activate(src, _transform); yield return EnemyAbilityWaits.Get(_hitBoxActiveTime); _landingHitBox.Deactivate(); } yield return EnemyAbilityWaits.Get(_recoveryTime); } private bool IsGrounded() { var hit = Physics2D.Raycast(_rb.position, Vector2.down, 0.6f, _groundMask); return hit.collider != null; } protected override void OnInterrupted(InterruptReason reason) { if (_landingHitBox != null && _landingHitBox.IsActive) _landingHitBox.Deactivate(); } } }