角色能力,存档

This commit is contained in:
2026-05-19 11:50:21 +08:00
parent d25f237e76
commit 2dcb7a961a
136 changed files with 36035 additions and 27551 deletions

View File

@@ -5,8 +5,9 @@ namespace BaseGames.Player.States
/// <summary>
/// 下落状态。
/// - 郊狼跳CoyoteTimer > 0 时按跳跃 → 一段跳JumpState使用 JumpForce
/// - 二段跳CoyoteTimer 耗尽后按跳跃且 AirJumpsLeft > 0 → JumpState使用 DoubleJumpForce
/// - 空中冲刺HasAbility(AirDash) && HasAerialDash → AerialDashState。
/// - 空中跳跃CoyoteTimer 耗尽后按跳跃且 AirJumpsLeft > 0 → JumpState使用 DoubleJumpForce
/// - 冲刺HasAbility(Dash) &amp;&amp; DashState.CanAirDash → DashState(地面与空中统一,空中限一次)
/// - 抓墙:贴墙时按下朝向墙壁的方向键 → WallSlideState。
/// - 增强下落重力FallGravityMult确保下落快于上升手感紧实。
/// </summary>
public class FallState : PlayerStateBase
@@ -22,7 +23,8 @@ namespace BaseGames.Player.States
public override void OnStateUpdate()
{
// ── 跳跃输入(郊狼跳 / 二段跳)────────────────────────────────────
if (Buffer.ConsumeJump())
// 先确认有可用跳跃机会,再消耗缓冲,避免无操作时静默吃掉输入
if ((Move.HasCoyoteTime || Owner.AirJumpsLeft > 0) && Buffer.ConsumeJump())
{
if (Move.HasCoyoteTime)
{
@@ -30,30 +32,40 @@ namespace BaseGames.Player.States
_owner.TransitionTo(_owner.GetState<JumpState>());
return;
}
if (Owner.AirJumpsLeft > 0)
{
// 二段跳:通过 SetDoubleJump 标记 JumpState 使用 DoubleJumpForce
Owner.UseAirJump();
Owner.GetState<JumpState>()?.SetDoubleJump(true);
_owner.TransitionTo(_owner.GetState<JumpState>());
return;
}
// 无跳跃机会:输入已消耗,静默忽略(无可用跳跃机会时静默消耗输入缓冲)
// 二段跳:通过 SetDoubleJump 标记 JumpState 使用 DoubleJumpForce
Owner.UseAirJump();
Owner.GetState<JumpState>()?.SetDoubleJump(true);
_owner.TransitionTo(_owner.GetState<JumpState>());
return;
}
// ── 空中冲刺────────────────────────────────────────────────────────
if (Buffer.ConsumeDash()
&& Stats != null && Stats.HasAbility(AbilityType.AirDash))
// ── 冲刺(地面/空中统一使用 DashState────────────────────────────
// 先确认能力与冷却均满足,再消耗缓冲,避免无操作时静默吃掉输入
var dashState = Owner.GetState<DashState>();
if (dashState != null && dashState.CanAirDash
&& Stats != null && Stats.HasAbility(AbilityType.Dash)
&& Buffer.ConsumeDash())
{
var aerialDash = Owner.GetState<AerialDashState>();
if (aerialDash != null && aerialDash.HasAerialDash)
{
_owner.TransitionTo(aerialDash);
return;
}
_owner.TransitionTo(dashState);
return;
}
// ── 着地──────────────────────────────────────────────────────────
// ── 空中攻击Move Y > 0 → 上劈Y < -0.5 且解锁下劈 → 下劈;其余 → 空中攻击 ──
if (Buffer.ConsumeAttack())
{
if (Input.MoveInput.y > 0.5f)
_owner.TransitionTo(_owner.GetState<UpAttackState>());
else if (Input.MoveInput.y < -0.5f
&& Stats != null && Stats.HasAbility(AbilityType.DownSlash))
_owner.TransitionTo(_owner.GetState<DownAttackState>());
else
_owner.TransitionTo(_owner.GetState<AirAttackState>());
return;
}
// ── 着地回退(主逻辑已移至 OnStateFixedUpdate此处仅作极端情况保险────────────
// 正常情况下状态在 FixedUpdate 中已转换Update 执行时 _currentState 已是 IdleState/RunState
// 此段不会被执行。仅在初始帧等 FixedUpdate 尚未运行时作补充保障。
if (Move.IsGrounded)
{
Move.ZeroVelocity();
@@ -68,13 +80,55 @@ namespace BaseGames.Player.States
public override void OnStateFixedUpdate()
{
// 空中水平移动:有输入时立即覆盖至目标速度;无输入时施加空气阻力保留动量
// ── 着地检测(在 FixedUpdate 内与 CheckGrounded 同帧执行)────────────────────
// PlayerMovement.FixedUpdate(-200) 先于 PlayerController.FixedUpdate(-100) 执行,
// 此处读到的 IsGrounded 已是本物理帧最新结果,不存在跨帧读到过期值的问题。
// 落地检测放在 OnStateUpdateUpdate 阶段)时,若低帧率导致同帧内多次 FixedUpdate
// 最后一次 FixedUpdate 的 depenetration 弹回可能使 _isGrounded 在 Update 时为 false
// 进而导致无法着地。
if (Move.IsGrounded)
{
Move.ZeroVelocity();
if (Mathf.Abs(Input.MoveInput.x) > 0.1f)
_owner.TransitionTo(_owner.GetState<RunState>());
else
_owner.TransitionTo(_owner.GetState<IdleState>());
return;
}
// 空中水平移动:有输入时立即覆盖至目标速度;无输入时水平速度立即归零。
// 朝向墙壁时停止施力,防止物理摩擦使角色在空中贴墙悬停。
if (Mathf.Abs(Input.MoveInput.x) > 0.01f)
Move.Move(Input.MoveInput.x * Cfg.RunSpeed);
{
int inputDir = Input.MoveInput.x > 0 ? 1 : -1;
var wd = Owner.WallDetector;
if (wd != null && wd.IsTouchingWall && wd.WallDirection == inputDir)
{
// 按下朝墙方向键 + 在墙上(且未著地)→ 进入抓墙状态
var wss = Owner.GetState<WallSlideState>();
if (wss != null && !Move.IsGrounded)
{
wss.PrepareEnter(inputDir);
Owner.TransitionTo(wss);
return;
}
Move.ZeroHorizontalVelocity();
}
else if (wd != null && wd.HasPartialContact(inputDir))
{
// 仅部分射线命中(如检测点高于矮墙顶部),停止施加朝墙方向的水平速度,
// 防止角色边角被卡在墙顶而无法继续下落。
Move.ZeroHorizontalVelocity();
}
else
Move.Move(Input.MoveInput.x * Cfg.RunSpeed);
}
else
Move.ApplyAirDrag(Cfg.AirDragFactor);
Move.ZeroHorizontalVelocity();
// 增强下落重力FallGravityMult下落比上升更快手感更紧实
// 着地时已由上方提前 return此处无需额外判断 IsGrounded
// 避免持续下压速度与 depenetration 形成振荡导致 IsGrounded 持续为 false
if (Move.Rb.velocity.y < 0f)
{
float extraGrav = Physics2D.gravity.y * (Cfg.FallGravityMult - 1f) * Time.fixedDeltaTime;