feat: Implement DownDash ability and related systems
- Added DownDash ability with cooldown and speed configuration. - Introduced DownDashState to handle down dashing mechanics, including gravity manipulation and animation playback. - Updated PlayerMovement to support DownDash functionality. - Enhanced PlayerStats to manage spring charge consumption and healing. - Modified PlayerCombat and WeaponHitBoxInstance to support new hit confirmation events. - Updated AbilityType to include new form types for character abilities. - Improved Gizmos for better visualization of enemy detection and attack ranges. - Added feedback systems for form switching in PlayerFeedback and IFeedbackPlayer. - Refactored combat and movement states to accommodate new abilities and ensure smooth transitions.
This commit is contained in:
@@ -112,7 +112,10 @@ namespace BaseGames.Player.States
|
||||
var animState = Anim.Play(step.clip);
|
||||
animState.Speed *= spd; // 在 ClipTransition 自身速度基础上叠加攻速
|
||||
|
||||
// 每次重播同一 ClipTransition 会复用同一 AnimancerState,
|
||||
// 必须先清除旧事件再注册新事件,否则回调会累积叠加。
|
||||
var events = animState.Events(this);
|
||||
events.Clear();
|
||||
events.OnEnd = OnClipEnd;
|
||||
|
||||
// HitBox 时间窗口(capture step by value for closure safety)
|
||||
@@ -121,11 +124,24 @@ namespace BaseGames.Player.States
|
||||
Owner.Combat?.EnableWeaponHitBox(AttackDirection.Ground,
|
||||
capturedStep.hitBoxId, capturedStep.damageSource));
|
||||
events.Add(capturedStep.hitBoxExit, () => Owner.Combat?.DisableAllWeaponHitBoxes());
|
||||
|
||||
// 连击输入窗口
|
||||
if (capturedStep.comboInputOpen > 0f)
|
||||
events.Add(capturedStep.comboInputOpen, () => _comboWindowOpen = true);
|
||||
{
|
||||
events.Add(capturedStep.comboInputOpen, () =>
|
||||
{
|
||||
_comboWindowOpen = true;
|
||||
// 窗口刚开时,补检查 InputBuffer——玩家可能在窗口前就提前按键
|
||||
if (!_comboInputPending && Buffer.ConsumeAttack())
|
||||
_comboInputPending = true;
|
||||
});
|
||||
}
|
||||
else
|
||||
_comboWindowOpen = true; // 0 = 立即开放
|
||||
{
|
||||
_comboWindowOpen = true;
|
||||
if (!_comboInputPending && Buffer.ConsumeAttack())
|
||||
_comboInputPending = true;
|
||||
}
|
||||
|
||||
if (capturedStep.comboInputClose > 0f)
|
||||
events.Add(capturedStep.comboInputClose, () => _comboWindowOpen = false);
|
||||
@@ -141,11 +157,18 @@ namespace BaseGames.Player.States
|
||||
_comboWindowOpen = false;
|
||||
Move.SetCancelWindowOpen(false);
|
||||
|
||||
// 如果已有缓存输入,直接推进(零延迟连击)
|
||||
// 有缓存连击输入且还不是最后一段 → 零延迟推进到下一段
|
||||
if (_comboInputPending)
|
||||
{
|
||||
AdvanceCombo();
|
||||
return;
|
||||
_comboInputPending = false;
|
||||
int maxCombo = Owner.Weapon?.ActiveWeapon?.GroundComboCount ?? 1;
|
||||
if (_comboIndex < maxCombo - 1)
|
||||
{
|
||||
_comboIndex++;
|
||||
PlayAttackClip();
|
||||
return; // 新动画已开始,不进入等待阶段
|
||||
}
|
||||
// 已是最后一段:消耗掉多余输入,继续进入等待阶段(不 return)
|
||||
}
|
||||
|
||||
// 进入动画后等待阶段
|
||||
@@ -169,7 +192,7 @@ namespace BaseGames.Player.States
|
||||
}
|
||||
else
|
||||
{
|
||||
// 已是最后一段,忽略多余输入,等待超时
|
||||
// 已是最后一段,忽略多余输入,等待超时回 Idle
|
||||
_comboInputPending = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,9 @@ namespace BaseGames.Player.States
|
||||
/// <summary>重置冲刺次数(落地或 Pogo 时由 IdleState/RunState/DownAttackState 调用)。</summary>
|
||||
public void ResetDashCharge() => _dashChargeUsed = false;
|
||||
|
||||
/// <summary>消耗空中冲刺次数(DownDashState 进入时调用,与普通空中冲刺共享次数上限)。</summary>
|
||||
public void ConsumeAirDashCharge() => _dashChargeUsed = true;
|
||||
|
||||
/// <summary>
|
||||
/// 无敌帧是否已冷却,即本次冲刺可以获得无敌。
|
||||
/// </summary>
|
||||
|
||||
62
Assets/_Game/Scripts/Player/States/DownDashState.cs
Normal file
62
Assets/_Game/Scripts/Player/States/DownDashState.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Player.States
|
||||
{
|
||||
/// <summary>
|
||||
/// 向下冲刺状态(空中下 + 冲刺触发)。
|
||||
/// - 消耗空中冲刺次数(与普通冲刺共享,本次离地仅可用一次)。
|
||||
/// - 关闭重力,施加向下速度,持续 DownDashDuration 秒。
|
||||
/// - 提前着地或计时结束时退出。
|
||||
/// - 需解锁 AbilityType.DownDash 才能进入(FallState / JumpState 负责条件检查)。
|
||||
/// </summary>
|
||||
public class DownDashState : PlayerStateBase
|
||||
{
|
||||
private float _timer;
|
||||
|
||||
public DownDashState(PlayerController owner) : base(owner) { }
|
||||
|
||||
public override void OnStateEnter()
|
||||
{
|
||||
// 消耗空中冲刺次数(与普通空中冲刺互斥)
|
||||
Owner.GetState<DashState>()?.ConsumeAirDashCharge();
|
||||
|
||||
_timer = Cfg.DownDashDuration;
|
||||
|
||||
// 关闭重力,施加向下速度
|
||||
Move?.SetGravityScale(0f);
|
||||
Move?.DownDash(Cfg.DownDashSpeed);
|
||||
|
||||
// 优先播放专属动画,未配置时回退到 Fall 动画
|
||||
var clip = AnimCfg?.DownDash ?? AnimCfg?.Fall;
|
||||
if (clip != null) Anim?.Play(clip);
|
||||
}
|
||||
|
||||
public override void OnStateUpdate()
|
||||
{
|
||||
_timer -= Time.deltaTime;
|
||||
if (_timer <= 0f || Move.IsGrounded)
|
||||
EndDownDash();
|
||||
}
|
||||
|
||||
public override void OnStateFixedUpdate()
|
||||
{
|
||||
// 持续保持向下速度(防止摩擦力减速)
|
||||
if (_timer > 0f && !Move.IsGrounded)
|
||||
Move?.DownDash(Cfg.DownDashSpeed);
|
||||
}
|
||||
|
||||
public override void OnStateExit()
|
||||
{
|
||||
Move?.SetGravityScale(Cfg.DefaultGravityScale);
|
||||
}
|
||||
|
||||
private void EndDownDash()
|
||||
{
|
||||
Move?.ZeroVelocity();
|
||||
if (Move != null && Move.IsGrounded)
|
||||
Owner.TransitionTo(Owner.GetState<IdleState>());
|
||||
else
|
||||
Owner.TransitionTo(Owner.GetState<FallState>());
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/_Game/Scripts/Player/States/DownDashState.cs.meta
Normal file
11
Assets/_Game/Scripts/Player/States/DownDashState.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b5c828df0b1d49d4bb45ced1da093e7b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -6,6 +6,7 @@ namespace BaseGames.Player.States
|
||||
/// 下落状态。
|
||||
/// - 郊狼跳:CoyoteTimer > 0 时按跳跃 → 一段跳(JumpState,使用 JumpForce)。
|
||||
/// - 空中跳跃:CoyoteTimer 耗尽后按跳跃且 AirJumpsLeft > 0 → JumpState(使用 DoubleJumpForce)。
|
||||
/// - 下冲刺:HasAbility(DownDash) && 下方向 + 冲刺键 → DownDashState(优先于普通冲刺)。
|
||||
/// - 冲刺:HasAbility(Dash) && DashState.CanDashMidAir → DashState(地面与空中统一,空中限一次)。
|
||||
/// - 抓墙:贴墙时按下朝向墙壁的方向键 → WallSlideState。
|
||||
/// - 增强下落重力(FallGravityMult)确保下落快于上升,手感紧实。
|
||||
@@ -53,9 +54,20 @@ namespace BaseGames.Player.States
|
||||
return;
|
||||
}
|
||||
|
||||
// ── 下冲刺(下 + 冲刺 → 向下冲刺,优先于普通冲刺)──────────────────────
|
||||
// 按住下方向 + 冲刺键,且已解锁 DownDash 能力、空中冲刺次数未耗尽
|
||||
var dashState = Owner.GetState<DashState>();
|
||||
if (dashState != null && dashState.CanDashMidAir
|
||||
&& Input.MoveInput.y < -0.5f
|
||||
&& Stats != null && Stats.HasAbility(AbilityType.DownDash)
|
||||
&& Buffer.ConsumeDash())
|
||||
{
|
||||
_owner.TransitionTo(_owner.GetState<DownDashState>());
|
||||
return;
|
||||
}
|
||||
|
||||
// ── 冲刺(地面/空中统一使用 DashState)────────────────────────────
|
||||
// 先确认能力与冷却均满足,再消耗缓冲,避免无操作时静默吃掉输入
|
||||
var dashState = Owner.GetState<DashState>();
|
||||
if (dashState != null && dashState.CanDashMidAir
|
||||
&& Stats != null && Stats.HasAbility(AbilityType.Dash)
|
||||
&& Buffer.ConsumeDash())
|
||||
|
||||
@@ -52,9 +52,20 @@ namespace BaseGames.Player.States
|
||||
return;
|
||||
}
|
||||
|
||||
// ── 下冲刺(下 + 冲刺 → 向下冲刺,优先于普通冲刺)──────────────────────
|
||||
// 按住下方向 + 冲刺键,且已解锁 DownDash 能力、空中冲刺次数未耗尽
|
||||
var dashState = Owner.GetState<DashState>();
|
||||
if (dashState != null && dashState.CanDashMidAir
|
||||
&& Input.MoveInput.y < -0.5f
|
||||
&& Stats != null && Stats.HasAbility(AbilityType.DownDash)
|
||||
&& Buffer.ConsumeDash())
|
||||
{
|
||||
_owner.TransitionTo(_owner.GetState<DownDashState>());
|
||||
return;
|
||||
}
|
||||
|
||||
// 冲刺(地面/空中统一使用 DashState,空中限一次,优先于二段跳:冲刺可保存二段跳机会)
|
||||
// 先确认能力与冷却均满足,再消耗缓冲,避免无操作时静默吃掉输入
|
||||
var dashState = Owner.GetState<DashState>();
|
||||
if (dashState != null && dashState.CanDashMidAir
|
||||
&& Stats != null && Stats.HasAbility(AbilityType.Dash)
|
||||
&& Buffer.ConsumeDash())
|
||||
|
||||
@@ -350,6 +350,7 @@ namespace BaseGames.Player.States
|
||||
_states[typeof(FallState)] = new FallState(this);
|
||||
_states[typeof(AttackState)] = new AttackState(this);
|
||||
_states[typeof(DashState)] = new DashState(this);
|
||||
_states[typeof(DownDashState)] = new DownDashState(this);
|
||||
_states[typeof(WallSlideState)] = new WallSlideState(this);
|
||||
_states[typeof(WallJumpState)] = new WallJumpState(this);
|
||||
_states[typeof(AirAttackState)] = new AirAttackState(this);
|
||||
|
||||
@@ -14,11 +14,11 @@ namespace BaseGames.Player.States
|
||||
|
||||
public override void OnStateEnter()
|
||||
{
|
||||
// 消耗灵泉充能并治疗(PlayerStats.UseSpring 内部回复 HP)
|
||||
bool used = Stats?.UseSpring() ?? false;
|
||||
// 前摇开始时只扣除充能,不立即回血;回血在前摇结束后的 OnSpringEnd 中执行。
|
||||
// 若前摇被打断(受伤 → HurtState),OnStateExit 被调用,充能已扣除但 OnSpringEnd 不会执行,回血失败。
|
||||
bool used = Stats?.ConsumeSpringCharge() ?? false;
|
||||
if (!used)
|
||||
{
|
||||
// 无充能时立即退出
|
||||
Owner.TransitionTo(Owner.GetState<IdleState>());
|
||||
return;
|
||||
}
|
||||
@@ -37,7 +37,7 @@ namespace BaseGames.Player.States
|
||||
}
|
||||
}
|
||||
|
||||
// 无动画则直接结束
|
||||
// 无动画配置则直接结束(视为前摇瞬间完成)
|
||||
OnSpringEnd();
|
||||
}
|
||||
|
||||
@@ -49,6 +49,8 @@ namespace BaseGames.Player.States
|
||||
|
||||
private void OnSpringEnd()
|
||||
{
|
||||
// 前摇正常结束 → 执行回血
|
||||
Stats?.ApplySpringHeal();
|
||||
Owner.TransitionTo(Owner.GetState<IdleState>());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user