PlayerMovementConfigSO.cs

新增 WallHangSpeed = 1f:正常抓墙(低于等于 wallGrabY,可蹬墙跳区间)的缓慢下滑速度
WallSlideSpeed 语义调整为:受限模式(高于 wallGrabY)的较快下滑速度
PlayerMovement.cs

ApplyWallSlide() 改为 ApplyWallSlide(float speed),由调用方传入对应速度
WallSlideState.cs

OnStateFixedUpdate:正常模式用 WallHangSpeed,受限模式用 WallSlideSpeed(两档清晰分离)
恢复反方向键脱离:脱离时同样调用 StartWallCoyote,wall coyote 窗口内仍能触发蹬墙跳
更新类头注释完整描述脱离方式和下滑速度档位Two wall slide improvements:
This commit is contained in:
2026-05-21 15:58:46 +08:00
parent 20b207ed58
commit fcd3e2dcdd
6 changed files with 106 additions and 26 deletions

View File

@@ -24,6 +24,8 @@ namespace BaseGames.Player
// ── 运行时状态 ────────────────────────────────────────────────────────
private Rigidbody2D _rb;
private float _coyoteTimer;
private float _wallCoyoteTimer;
private int _wallCoyoteDir; // 离墙时记录的墙壁方向(+1 右 / -1 左)
private bool _isGrounded;
// Update 中调用 ZeroHorizontalVelocity 后设置此标记;
// 下一个 FixedUpdate-200先于状态机 -100读取并清零
@@ -47,6 +49,7 @@ namespace BaseGames.Player
[SerializeField] private bool _dbg_IsGrounded;
[SerializeField] private bool _dbg_OnOneWayPlatform;
[SerializeField] private bool _dbg_HasCoyoteTime;
[SerializeField] private bool _dbg_HasWallCoyoteTime;
[SerializeField] private bool _dbg_IsWallLeft;
[SerializeField] private bool _dbg_IsWallRight;
[SerializeField] private bool _dbg_CancelWindowOpen;
@@ -55,6 +58,8 @@ namespace BaseGames.Player
public bool IsGrounded => _isGrounded;
public bool HasCoyoteTime => _coyoteTimer > 0f;
public bool HasWallCoyoteTime => _wallCoyoteTimer > 0f;
public int WallCoyoteDir => _wallCoyoteDir;
public bool IsWallLeft => _isWallLeft;
public bool IsWallRight => _isWallRight;
public bool OnOneWayPlatform => _onOneWayPlatform;
@@ -93,12 +98,15 @@ namespace BaseGames.Player
else
_coyoteTimer = Mathf.Max(0f, _coyoteTimer - Time.fixedDeltaTime);
_wallCoyoteTimer = Mathf.Max(0f, _wallCoyoteTimer - Time.fixedDeltaTime);
#if UNITY_EDITOR
_dbg_VelocityX = _rb.velocity.x;
_dbg_VelocityY = _rb.velocity.y;
_dbg_IsGrounded = _isGrounded;
_dbg_OnOneWayPlatform = _onOneWayPlatform;
_dbg_HasCoyoteTime = _coyoteTimer > 0f;
_dbg_HasWallCoyoteTime = _wallCoyoteTimer > 0f;
_dbg_IsWallLeft = _isWallLeft;
_dbg_IsWallRight = _isWallRight;
_dbg_CancelWindowOpen = _cancelWindowOpen;
@@ -198,6 +206,21 @@ namespace BaseGames.Player
// ── 取消窗口 ──────────────────────────────────────────────────────────
public void SetCancelWindowOpen(bool open) => _cancelWindowOpen = open;
// ── 墙壁土狼时间 ──────────────────────────────────────────────────────
/// <summary>
/// 由 WallSlideState 在切换到 FallState 时调用:启动墙壁土狼计时器。
/// 窗口内HasWallCoyoteTime == true按跳跃键仍可触发蹬墙跳。
/// wallDir+1 = 右墙 / -1 = 左墙。
/// </summary>
public void StartWallCoyote(int wallDir)
{
_wallCoyoteTimer = _config.WallCoyoteTime;
_wallCoyoteDir = wallDir;
}
/// <summary>消耗墙壁土狼时间,防止同一帧被多次触发。</summary>
public void ConsumeWallCoyote() => _wallCoyoteTimer = 0f;
// ── 冲刺 ──────────────────────────────────────────────────────────────
/// <summary>
/// 施加冲刺速度DashState/DashState 调用)。
@@ -210,12 +233,12 @@ namespace BaseGames.Player
}
/// <summary>
/// 壁滑:将垂直速度限制为 -WallSlideSpeed受限抓墙时向下缓慢滑动)。
/// WallSlideState.OnStateFixedUpdate 在受限模式下每帧调用
/// 壁滑:将垂直速度限制为 -speed每帧调用以约束最大下滑速度)。
/// WallSlideState.OnStateFixedUpdate 根据正常/受限模式传入不同速度
/// </summary>
public void ApplyWallSlide()
public void ApplyWallSlide(float speed)
{
float targetY = -_config.WallSlideSpeed;
float targetY = -speed;
if (_rb.velocity.y < targetY)
_rb.velocity = new Vector2(_rb.velocity.x, targetY);
}

View File

@@ -57,10 +57,16 @@ namespace BaseGames.Player
[Header("抓墙 / 壁滑")]
[Tooltip("受限抓墙时(高于 wallGrabY的下滑速度单位/秒)。推荐 2。")]
public float WallSlideSpeed = 2f;
[Tooltip("正常抓墙时(低于等于 wallGrabY可蹬墙跳区间的缓慢下滑速度单位/秒)。\n" +
"设为 0 = 完全静止悬挂;推荐 1轻微下滑更有手感。")]
public float WallHangSpeed = 1f;
public float WallRayLength = 0.55f;
public float WallRayOffsetY = 0.2f;
[Tooltip("抓墙高度容差:当前 Y 不超过 wallGrabY + 此值时视为未抬升,防止浮点抖动误判。")]
public float WallGrabHeightTolerance = 0.05f;
[Tooltip("离开墙面后仍可触发蹬墙跳的缓冲时长(秒)。" +
"类比地面土狼时间,主动按↓脱离或墙面消失后,此窗口内按跳跃仍视为有效的蹬墙跳。推荐 0.12。")]
public float WallCoyoteTime = 0.12f;
[Header("蹬墙跳 — 背墙跳Jump Away远离墙壁斜上方")]
[Tooltip("背墙跳水平速度(远离墙壁方向)。推荐 14。")]

View File

@@ -22,6 +22,20 @@ namespace BaseGames.Player.States
public override void OnStateUpdate()
{
// ── 墙壁土狼跳(优先于地面土狼跳 / 二段跳)──────────────────────────
// 离墙后 WallCoyoteTime 秒内按跳跃,视为有效蹬墙跳(背墙跳 / 对墙跳)
if (Move.HasWallCoyoteTime && Buffer.ConsumeJump())
{
var wjs = Owner.GetState<WallJumpState>();
if (wjs != null)
{
Move.ConsumeWallCoyote();
wjs.PrepareEnter(Move.WallCoyoteDir, Input.MoveInput.x);
Owner.TransitionTo(wjs);
return;
}
}
// ── 跳跃输入(郊狼跳 / 二段跳)────────────────────────────────────
// 先确认有可用跳跃机会,再消耗缓冲,避免无操作时静默吃掉输入
if ((Move.HasCoyoteTime || Owner.AirJumpsLeft > 0) && Buffer.ConsumeJump())

View File

@@ -6,7 +6,17 @@ namespace BaseGames.Player.States
/// 抓墙状态(自定义设计,架构 05_PlayerModule §2
///
/// 触发条件:空中贴墙时玩家按下朝向墙壁的方向键(由 FallState/JumpState 检测并调用 PrepareEnter
/// 维持条件:进入后无需持续按键;主动按下反方向键或落地时解除
/// 维持条件:进入后无需持续按键;按下↓键或落地时解除;跳跃键触发蹬墙跳后由 WallJumpState 接管
///
/// 脱离方式:
/// - 跳跃键 → 蹬墙跳WallJumpState由 OnJumpPressed 响应)
/// - ↓ 键 / 反方向键 → 主动脱离,进入 FallStatewall coyote 窗口内仍可触发蹬墙跳
/// - 离开墙面 → 自然下落FallState
/// - 着地 → 闲置IdleState
///
/// 下滑速度分为两档(均在 PlayerMovementConfigSO 配置):
/// - 正常模式(低于等于 wallGrabY可蹬墙跳→ WallHangSpeed缓慢下滑
/// - 受限模式(高于 wallGrabY不可蹬墙跳 → WallSlideSpeed较快下滑
///
/// 高度记忆机制(防止单面墙反复爬升):
/// - 首次抓墙(或切换到另一侧墙壁)时记录 _wallGrabY。
@@ -73,9 +83,10 @@ namespace BaseGames.Player.States
{
var wd = Owner.WallDetector;
// 离开墙壁 → 下落
// 离开墙壁 → 启动墙壁土狼时间后下落
if (wd == null || !wd.IsTouchingWall)
{
Move.StartWallCoyote(_wallDir);
Owner.TransitionTo(Owner.GetState<FallState>());
return;
}
@@ -87,30 +98,35 @@ namespace BaseGames.Player.States
return;
}
// 主动按反方向键 → 脱离(松墙下落
float mx = Input.MoveInput.x;
if (Mathf.Abs(mx) > 0.1f)
// 按下方向键 → 启动墙壁土狼时间后主动脱离,自然下落
if (Input.MoveInput.y < -0.5f)
{
int inputDir = mx > 0f ? 1 : -1;
if (inputDir != _wallDir)
{
Owner.TransitionTo(Owner.GetState<FallState>());
return;
}
Move.StartWallCoyote(_wallDir);
Owner.TransitionTo(Owner.GetState<FallState>());
return;
}
// 按反方向键 → 启动墙壁土狼时间后脱离
// wall coyote 存在时,离墙后短窗口内仍可触发蹬墙跳,不会误双跳
float mx = Input.MoveInput.x;
if (Mathf.Abs(mx) > 0.1f && (mx > 0f ? 1 : -1) != _wallDir)
{
Move.StartWallCoyote(_wallDir);
Owner.TransitionTo(Owner.GetState<FallState>());
return;
}
// 每帧刷新正常/受限状态
UpdateCanJump();
}
public override void OnStateFixedUpdate()
{
if (_canJump)
// 正常模式:静止悬挂,阻止向下速度
Move?.ZeroVerticalVelocity();
// 正常模式(低于等于 wallGrabY可蹬墙跳缓慢下滑给玩家操作窗口
Move?.ApplyWallSlide(Cfg.WallHangSpeed);
else
// 受限模式:持续下滑
Move?.ApplyWallSlide();
// 受限模式(高于 wallGrabY较快下滑不可蹬墙跳
Move?.ApplyWallSlide(Cfg.WallSlideSpeed);
}
// ── 内部 ──────────────────────────────────────────────────────────────