using UnityEngine; namespace BaseGames.Player.States { /// /// 空中冲刺状态(架构 05_PlayerModule §12)。 /// 与地面 DashState 独立,消耗 MaxAerialDashes 次数; /// 冲刺方向在进入时锁定为当前朝向(进入时锁定朝向,冲刺期间不可通过输入改变方向)。 /// public class AerialDashState : PlayerStateBase { private float _timer; private int _aerialDashesLeft; private int _facingDir; public bool HasAerialDash => _aerialDashesLeft > 0; // ── IsInvincible 不再在状态层硬编码,与 DashState 保持一致: // 实际无敌用 Stats.BeginInvincibility(DashInvincibilityDuration) 面题。 // PlayerController.TakeDamage 已将 Stats.IsInvincible 纳入硬直判断。 public AerialDashState(PlayerController owner) : base(owner) { _aerialDashesLeft = 1; } public override void OnStateEnter() { _aerialDashesLeft = Mathf.Max(0, _aerialDashesLeft - 1); _facingDir = Owner.FacingDirection; _timer = Cfg.DashDuration; // 无敌帧:与地面冲刺共享同一无敌 CD(DashState._invincibilityCooldownTimer) // 条件 1:已解锁 InvincibleDash // 条件 2:共享无敌冷却已就绪 var dashState = Owner.GetState(); if (Stats != null && Stats.HasAbility(AbilityType.InvincibleDash) && dashState != null && dashState.CanGrantInvincibility) { Stats.BeginInvincibility(Cfg.DashInvincibilityDuration); dashState.ResetInvincibilityCooldown(Cfg.DashInvincibilityCooldown); } // 关闭重力,施加冲刺速度(方向锁定为进入时朝向,不受输入影响) Move?.SetGravityScale(0f); Move?.Dash(new Vector2(_facingDir, 0f), Cfg.DashSpeed); // 播放冲刺动画(复用地面冲刺动画) if (AnimCfg?.Dash != null) Anim?.Play(AnimCfg.Dash); } public override void OnStateUpdate() { _timer -= Time.deltaTime; if (_timer <= 0f) { Move?.SetGravityScale(Cfg.DefaultGravityScale); Owner.TransitionTo(Owner.GetState()); return; } // 撞墙立即终止冲刺(碰到实体墙立即中止,避免压墙卡住) var wd = Owner.WallDetector; if (wd != null && wd.IsTouchingWall && wd.WallDirection == _facingDir) { Move?.SetGravityScale(Cfg.DefaultGravityScale); Owner.TransitionTo(Owner.GetState()); } } public override void OnStateExit() { Move?.SetGravityScale(Cfg.DefaultGravityScale); } public override void OnStateFixedUpdate() { // 冲刺期间保持锁定方向速度(与 DashState 一致,使用 _facingDir) if (_timer > 0f) Move?.Dash(new Vector2(_facingDir, 0f), Cfg.DashSpeed); } /// 着地时重置空中冲刺次数(由 PlayerController 在着地时调用)。 public void ResetAerialDashes() { _aerialDashesLeft = Cfg.MaxAerialDashes; } } }