Files
zeling_v2/Assets/_Game/Scripts/Player/States/DashState.cs
Joywayer b7baf7ad6a Add WeaponFeedback component and AddressableManagerWindow meta file
- Implemented WeaponFeedback class for handling weapon-related feedbacks such as hit effects and attack sounds.
- Added meta file for AddressableManagerWindow to manage addressable assets.
- Included a new jump.data file for profiler data.
2026-05-22 22:03:32 +08:00

145 lines
6.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using UnityEngine;
using System.Collections;
namespace BaseGames.Player.States
{
/// <summary>
/// 冲刺状态(架构 05_PlayerModule §2 + §12
/// 施加水平位移 + 无敌帧;冲刺期间重力归零,结束后恢复;
/// 地面与空中统一使用此状态空中每次离地限冲刺一次_dashChargeUsed
/// 需解锁 AbilityType.Dash 才能进入PlayerController 负责条件检查)。
/// </summary>
public class DashState : PlayerStateBase
{
private float _timer;
private float _cooldownTimer;
/// <summary>
/// 无敌的独立冷却计时器。由 TickCooldown 每帧减少。
/// </summary>
private float _invincibilityCooldownTimer;
private int _facingDir;
/// <summary>本次离地后是否已消耗过冲刺次数。落地或下劈命中Pogo时重置。</summary>
private bool _dashChargeUsed;
public bool CanDash => _cooldownTimer <= 0f;
/// <summary>空中冲刺可用条件:冷却就绪 且 本次离地内尚未冲刺过。</summary>
public bool CanDashMidAir => _cooldownTimer <= 0f && !_dashChargeUsed;
/// <summary>重置冲刺次数(落地或 Pogo 时由 IdleState/RunState/DownAttackState 调用)。</summary>
public void ResetDashCharge() => _dashChargeUsed = false;
/// <summary>消耗空中冲刺次数DownDashState 进入时调用,与普通空中冲刺共享次数上限)。</summary>
public void ConsumeAirDashCharge() => _dashChargeUsed = true;
/// <summary>
/// 无敌帧是否已冷却,即本次冲刺可以获得无敌。
/// </summary>
public bool CanGrantInvincibility => _invincibilityCooldownTimer <= 0f;
/// <summary>重置无敌冷却(冲刺使用无敌后调用)。</summary>
public void ResetInvincibilityCooldown(float cd) => _invincibilityCooldownTimer = cd;
// ── IsInvincible 不再在状态层硬编码,改为全面依赖 Stats._invincibleTimer ────────────
// 冲刺无敌窗口 = BeginInvincibility(DashInvincibilityDuration) 设定的时间限 Stats._invincibleTimer
// PlayerController.TakeDamage 已改为将 Stats.IsInvincible 纳入硬直判断,居间无敌窗口自然失效。
// 不再需要 override IsInvincible。
public DashState(PlayerController owner) : base(owner) { }
public override void OnStateEnter()
{
Debug.Assert(Cfg != null, "[DashState] MovementConfig 未配置,请在 PlayerController Inspector 中绑定 MovementConfig SO。", _owner);
_facingDir = Owner.FacingDirection;
_timer = Cfg.DashDuration;
// 空中冲刺:记录本次离地已使用冲刺(地面冲刺不消耗,仅空中限制一次)
if (!Move.IsGrounded)
_dashChargeUsed = true;
// 无敌帧:
// 条件 1已解锁 InvincibleDash
// 条件 2无敌冷却已就绪防止 spam 冲刺连序无敌)
// 窗口时长 = DashInvincibilityDuration < DashDuration冲刺后段无保护
// 在设置冷却计时器前捕获,供后续动画选择使用
bool isInvincibleDash = Stats != null
&& Stats.HasAbility(AbilityType.InvincibleDash)
&& CanGrantInvincibility;
if (isInvincibleDash)
{
Stats.BeginInvincibility(Cfg.DashInvincibilityDuration);
_invincibilityCooldownTimer = Cfg.DashInvincibilityCooldown;
}
// 关闭重力,施加冲刺速度
Move?.SetGravityScale(0f);
Move?.Dash(new Vector2(_facingDir, 0f), Cfg.DashSpeed);
// 播放冲刺动画:无敌冲刺使用专属 Clip留空时回退到普通冲刺 Clip
var dashClip = (isInvincibleDash && AnimCfg?.DashInvincible != null)
? AnimCfg.DashInvincible
: AnimCfg?.Dash;
if (dashClip != null) Anim?.Play(dashClip);
Feedback.TriggerPreset("dash");
}
public override void OnStateUpdate()
{
_timer -= Time.deltaTime;
if (_timer <= 0f)
{
EndDash();
return;
}
// 跳跃可取消冲刺:冲刺期间按跳跃立即中断并起跳。
// 空中冲刺时若有剩余空中跳跃次数,消耗一次并使用二段跳力度。
if (Buffer.ConsumeJump())
{
if (!Move.IsGrounded && Owner.AirJumpsLeft > 0)
{
Owner.UseAirJump();
Owner.GetState<JumpState>()?.SetDoubleJump(true);
}
Owner.TransitionTo(Owner.GetState<JumpState>());
return;
}
// 注:碰墙时不中止冲刺,完成完整冲刺时长(物理阻止位移,但计时继续)
}
public override void OnStateExit()
{
// 恢复默认重力
Move?.SetGravityScale(Cfg.DefaultGravityScale);
_cooldownTimer = Cfg.DashCooldown;
}
public override void OnStateFixedUpdate()
{
// 冲刺期间保持冲刺速度(防止摩擦力减速)
if (_timer > 0f)
Move?.Dash(new Vector2(_facingDir, 0f), Cfg.DashSpeed);
}
private void EndDash()
{
// 双轴速度归零,防止冲刺结束时角色带着 DashSpeed 冲出平台边缘后继续向前飞行。
Move?.ZeroVelocity();
if (Move != null && Move.IsGrounded)
Owner.TransitionTo(Owner.GetState<IdleState>());
else
Owner.TransitionTo(Owner.GetState<FallState>());
}
/// <summary>每帧减少冒小帮冷却(由 PlayerController.Update 调用)。</summary>
public void TickCooldown(float dt)
{
if (_cooldownTimer > 0f) _cooldownTimer -= dt;
if (_invincibilityCooldownTimer > 0f) _invincibilityCooldownTimer -= dt;
}
}
}