using System.Collections; using UnityEngine; using BaseGames.Combat; namespace BaseGames.Enemies.Abilities { /// /// 直线冲锋能力:朝目标方向高速直线冲锋,撞墙时进入硬直恢复。 /// 流程:朝向目标 → 蓄力 → 高速直线冲锋直到撞墙或超距 → 恢复。 /// 冲锋期间 HitBox 持续激活;可选撞墙时硬直。 /// [RequireComponent(typeof(Rigidbody2D))] public sealed class ChargeAbility : EnemyAbilityBase { [Header("冲锋参数")] [SerializeField] [Min(0.1f)] private float _chargeSpeed = 14f; [SerializeField] [Min(0.1f)] private float _maxDistance = 12f; [SerializeField] private float _windupTime = 0.4f; [SerializeField] private float _recoveryTime = 0.6f; [SerializeField] private float _wallCheckDist = 0.4f; [SerializeField] private LayerMask _wallMask; [SerializeField] private bool _stunOnWallHit = true; [SerializeField] private float _wallStunTime = 0.8f; [Header("HitBox")] [SerializeField] private HitBox _chargeHitBox; [Header("动画 Key")] [SerializeField] private string _windupAnimSlot = ""; [SerializeField] private string _chargeAnimSlot = ""; private Rigidbody2D _rb; private float _direction; protected override void Awake() { base.Awake(); _rb = GetComponentInParent(); } protected override IEnumerator ExecuteCoroutine() { if (_rb == null) yield break; FaceTarget(_enemy != null ? _enemy.PlayerTransform : null); _direction = _transform.localScale.x >= 0f ? 1f : -1f; var seq = _config != null ? _config.attackSequence : null; var windupAtk = (seq != null && seq.Length > 0) ? seq[0] : null; var chargeAtk = (seq != null && seq.Length > 1) ? seq[1] : windupAtk; // Windup if (windupAtk != null && windupAtk.clip != null && _animancer != null) _animancer.Play(windupAtk.clip); _rb.velocity = new Vector2(0f, _rb.velocity.y); yield return EnemyAbilityWaits.Get(_windupTime); // Charge Phase = AbilityRunState.Active; if (chargeAtk != null && chargeAtk.clip != null && _animancer != null) _animancer.Play(chargeAtk.clip); if (_chargeHitBox != null) _chargeHitBox.Activate(chargeAtk != null ? chargeAtk.damageSource : null, _transform); Vector2 start = _rb.position; bool wallHit = false; float maxTime = (_maxDistance / _chargeSpeed) * 3f; // 3 倍容差兜底,防止极端物理情况下死循环 float elapsed = 0f; while (true) { _rb.velocity = new Vector2(_chargeSpeed * _direction, _rb.velocity.y); yield return new WaitForFixedUpdate(); elapsed += Time.fixedDeltaTime; if (elapsed >= maxTime) break; // 超时保护 float traveled = Mathf.Abs(_rb.position.x - start.x); if (traveled >= _maxDistance) break; var hit = Physics2D.Raycast(_rb.position, new Vector2(_direction, 0f), _wallCheckDist, _wallMask); if (hit.collider != null) { wallHit = true; break; } } _rb.velocity = new Vector2(0f, _rb.velocity.y); if (_chargeHitBox != null && _chargeHitBox.IsActive) _chargeHitBox.Deactivate(); // Recovery float recover = wallHit && _stunOnWallHit ? _wallStunTime : _recoveryTime; yield return EnemyAbilityWaits.Get(recover); } protected override void OnInterrupted(InterruptReason reason) { if (_chargeHitBox != null && _chargeHitBox.IsActive) _chargeHitBox.Deactivate(); if (_rb != null) _rb.velocity = Vector2.zero; // 完整清零速度,包含 y 轴 } } }