feat: 实现抓墙高度记忆、背墙跳/对墙跳、蹬墙后自动抓墙

- PlayerController: 添加 wallGrabY/wallGrabDir/isPostWallJump 字段及 API
- WallSlideState: 高度限制(超限强制下滑不可蹬跳)+ 静止悬挂 + 反向键释放
- WallJumpState: 区分背墙跳(WallJumpAwayForce)/对墙跳(WallJumpBackForce)
  蹬墙后标记 PostWallJump 允许空中自动抓墙;恢复空中冲刺次数
- FallState/JumpState: 新增抓墙入口(朝向按键 OR PostWallJump 自动抓墙)
- PlayerMovement.WallJump: 增加 jumpAway 参数区分两种跳跃力
- IdleState/RunState: 落地时重置抓墙记录和蹬墙跳标记

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-05-19 12:12:24 +08:00
parent d25f237e76
commit 2a6a0b1861
8 changed files with 172 additions and 22 deletions

View File

@@ -3,12 +3,17 @@ using UnityEngine;
namespace BaseGames.Player.States
{
/// <summary>
/// 壁滑状态(架构 05_PlayerModule §2
/// 需有 PlayerWallDetector.IsTouchingWall == true 才能进入;
/// 限制下落速度为 WallSlideSpeed按跳跃则切换到 WallJumpState。
/// 抓墙状态(架构 05_PlayerModule §2
/// 进入条件:空中贴墙时,按下朝向墙壁的方向键(或蹬墙跳后的自动抓墙)。
/// 高度限制:若当前 Y > wallGrabY首次抓该墙的高度强制下滑且不可蹬墙跳
/// 若当前 Y ≤ wallGrabY静止悬挂可蹬墙跳。
/// 释放条件:主动按下反方向键或落地。
/// </summary>
public class WallSlideState : PlayerStateBase
{
private bool _canWallJump;
private int _wallDir;
public WallSlideState(PlayerController owner) : base(owner) { }
public override void OnStateEnter()
@@ -16,6 +21,16 @@ namespace BaseGames.Player.States
if (AnimCfg?.WallSlide != null)
Anim?.Play(AnimCfg.WallSlide);
// 记录当前墙壁方向
_wallDir = Owner.WallDetector != null ? Owner.WallDetector.WallDirection : -Owner.FacingDirection;
if (_wallDir == 0) _wallDir = -Owner.FacingDirection;
// 记录首次抓墙高度(不同墙壁才重置)
Owner.RecordWallGrab(_wallDir, Owner.transform.position.y);
// 消耗蹬墙跳后的自动抓墙标记
Owner.SetPostWallJump(false);
Input.JumpStartedEvent += OnJumpPressed;
}
@@ -39,17 +54,41 @@ namespace BaseGames.Player.States
Owner.TransitionTo(Owner.GetState<IdleState>());
return;
}
// 主动按下反方向键 → 松开墙壁下落
float moveX = Input.MoveInput.x;
if (Mathf.Abs(moveX) > 0.01f && (int)Mathf.Sign(moveX) == -_wallDir)
{
Owner.TransitionTo(Owner.GetState<FallState>());
return;
}
}
public override void OnStateFixedUpdate()
{
// 限制下落速度(壁滑缓慢下落
Move?.ApplyWallSlide();
// 每帧重新判断是否允许蹬墙跳(随下滑高度动态变化
float currentY = Owner.transform.position.y;
_canWallJump = currentY <= Owner.WallGrabY + Cfg.WallGrabMaxHeightGain;
if (_canWallJump)
{
// 高度合法:静止悬挂(冻结垂直速度)
var vel = Move.Rb.velocity;
if (vel.y < 0f)
Move.Rb.velocity = new Vector2(vel.x, 0f);
}
else
{
// 高度超限:强制下滑
Move?.ApplyWallSlide();
}
}
private void OnJumpPressed()
{
Owner.TransitionTo(Owner.GetState<WallJumpState>());
// 仅高度合法时才允许蹬墙跳
if (_canWallJump)
Owner.TransitionTo(Owner.GetState<WallJumpState>());
}
}
}