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:
@@ -180,13 +180,26 @@ namespace BaseGames.Player
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 蹬墙跳:对墙方向施加相反水平力 + 向上力。
|
||||
/// wallDir = +1 (右墙) 或 -1 (左墙),跳跃方向与之相反。
|
||||
/// 蹬墙跳:根据跳跃类型施加不同速度。
|
||||
/// wallDir = +1 (右墙) 或 -1 (左墙)。
|
||||
/// jumpAway = true:背墙跳(朝远离墙壁方向),施加 WallJumpAwayForceX/Y;
|
||||
/// jumpAway = false:对墙跳(朝墙壁方向),施加 WallJumpBackForceX + WallJumpForceY。
|
||||
/// </summary>
|
||||
public void WallJump(int wallDir)
|
||||
public void WallJump(int wallDir, bool jumpAway)
|
||||
{
|
||||
float forceX = -wallDir * _config.WallJumpForceX;
|
||||
float forceY = _config.WallJumpForceY;
|
||||
float forceX, forceY;
|
||||
if (jumpAway)
|
||||
{
|
||||
// 背墙跳:远离墙壁方向弹出
|
||||
forceX = -wallDir * _config.WallJumpAwayForceX;
|
||||
forceY = _config.WallJumpAwayForceY;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 对墙跳:偏向垂直向上,水平分量朝向墙壁
|
||||
forceX = wallDir * _config.WallJumpBackForceX;
|
||||
forceY = _config.WallJumpForceY;
|
||||
}
|
||||
_rb.velocity = new Vector2(forceX, forceY);
|
||||
_coyoteTimer = 0f;
|
||||
}
|
||||
|
||||
@@ -64,6 +64,20 @@ namespace BaseGames.Player.States
|
||||
return;
|
||||
}
|
||||
|
||||
// ── 抓墙:贴墙 + 朝向墙壁按键,或蹬墙跳后的自动抓墙──────────────
|
||||
var wd = Owner.WallDetector;
|
||||
if (wd != null && wd.IsTouchingWall)
|
||||
{
|
||||
int wallDir = wd.WallDirection;
|
||||
bool pressingTowardWall = Mathf.Abs(Input.MoveInput.x) > 0.01f
|
||||
&& (int)Mathf.Sign(Input.MoveInput.x) == wallDir;
|
||||
if (pressingTowardWall || Owner.IsPostWallJump)
|
||||
{
|
||||
_owner.TransitionTo(_owner.GetState<WallSlideState>());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override void OnStateFixedUpdate()
|
||||
|
||||
@@ -12,9 +12,11 @@ namespace BaseGames.Player.States
|
||||
if (AnimCfg?.Idle != null)
|
||||
Anim.Play(AnimCfg.Idle);
|
||||
Move?.ZeroHorizontalVelocity();
|
||||
// 落地时重置空中能力计数器
|
||||
// 落地时重置空中能力计数器及抓墙记录
|
||||
Owner.GetState<AerialDashState>()?.ResetAerialDashes();
|
||||
Owner.ResetAirJumps();
|
||||
Owner.ResetWallGrab();
|
||||
Owner.SetPostWallJump(false);
|
||||
}
|
||||
|
||||
public override void OnStateUpdate()
|
||||
|
||||
@@ -68,6 +68,20 @@ namespace BaseGames.Player.States
|
||||
if (AnimCfg?.Jump != null) Anim?.Play(AnimCfg.Jump);
|
||||
return;
|
||||
}
|
||||
|
||||
// ── 抓墙:贴墙 + 朝向墙壁按键,或蹬墙跳后的自动抓墙──────────────
|
||||
var wd = Owner.WallDetector;
|
||||
if (wd != null && wd.IsTouchingWall && !Move.IsGrounded)
|
||||
{
|
||||
int wallDir = wd.WallDirection;
|
||||
bool pressingTowardWall = Mathf.Abs(Input.MoveInput.x) > 0.01f
|
||||
&& (int)Mathf.Sign(Input.MoveInput.x) == wallDir;
|
||||
if (pressingTowardWall || Owner.IsPostWallJump)
|
||||
{
|
||||
_owner.TransitionTo(_owner.GetState<WallSlideState>());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnStateFixedUpdate()
|
||||
|
||||
@@ -144,6 +144,47 @@ namespace BaseGames.Player.States
|
||||
public void ResetAirJumps() =>
|
||||
_airJumpsLeft = _stats != null && _stats.HasAbility(AbilityType.DoubleJump) ? 1 : 0;
|
||||
|
||||
// ── 抓墙高度记忆 API ──────────────────────────────────────────────────
|
||||
// wallGrabY:首次抓住某面墙壁时记录的 Y 坐标;用于防止单面墙无限向上爬。
|
||||
// wallGrabDir:当前记录所属墙壁方向(+1=右墙,-1=左墙,0=无)。
|
||||
// isPostWallJump:蹬墙跳后未落地标记,允许靠近墙壁时自动抓墙。
|
||||
private float _wallGrabY = float.NegativeInfinity;
|
||||
private int _wallGrabDir = 0;
|
||||
private bool _isPostWallJump;
|
||||
|
||||
public float WallGrabY => _wallGrabY;
|
||||
public int WallGrabDir => _wallGrabDir;
|
||||
public bool IsPostWallJump => _isPostWallJump;
|
||||
|
||||
/// <summary>
|
||||
/// 进入 WallSlideState 时调用。
|
||||
/// 不同墙壁(dir != _wallGrabDir)→ 重置并记录新高度;
|
||||
/// 同一面墙壁 → 保留原记录(防止攀爬重置)。
|
||||
/// </summary>
|
||||
public void RecordWallGrab(int dir, float y)
|
||||
{
|
||||
if (dir != _wallGrabDir)
|
||||
{
|
||||
_wallGrabDir = dir;
|
||||
_wallGrabY = y;
|
||||
}
|
||||
// 同一面墙:保留 _wallGrabY,不更新
|
||||
}
|
||||
|
||||
/// <summary>落地时重置抓墙记录(由 IdleState/RunState.OnStateEnter 调用)。</summary>
|
||||
public void ResetWallGrab()
|
||||
{
|
||||
_wallGrabY = float.NegativeInfinity;
|
||||
_wallGrabDir = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置蹬墙跳后自动抓墙标记。
|
||||
/// true:由 WallJumpState.OnStateEnter 设置;
|
||||
/// false:由 IdleState/RunState.OnStateEnter(落地)或 WallSlideState.OnStateEnter(已消耗)清除。
|
||||
/// </summary>
|
||||
public void SetPostWallJump(bool value) => _isPostWallJump = value;
|
||||
|
||||
// ── Overlay Layer API(供 SpringState / SoulSkill 等叠加动画使用)─────
|
||||
/// <summary>
|
||||
/// 在 Overlay Layer(Layer 1)播放动画,叠加于当前 Base Layer 动画之上。
|
||||
|
||||
@@ -11,9 +11,11 @@ namespace BaseGames.Player.States
|
||||
{
|
||||
if (AnimCfg?.Run != null)
|
||||
Anim.Play(AnimCfg.Run);
|
||||
// 落地时重置空中能力计数器(绝大多数情况被 IdleState 覆盖,但水平落地直接进入 RunState 时也需要)
|
||||
// 落地时重置空中能力计数器及抓墙记录(水平落地直接进入 RunState 时)
|
||||
Owner.GetState<AerialDashState>()?.ResetAerialDashes();
|
||||
Owner.ResetAirJumps();
|
||||
Owner.ResetWallGrab();
|
||||
Owner.SetPostWallJump(false);
|
||||
}
|
||||
|
||||
public override void OnStateUpdate()
|
||||
|
||||
@@ -4,31 +4,45 @@ namespace BaseGames.Player.States
|
||||
{
|
||||
/// <summary>
|
||||
/// 蹬墙跳状态(架构 05_PlayerModule §2)。
|
||||
/// 从 WallSlideState 进入;施加背墙方向的水平 + 垂直速度;
|
||||
/// 短暂锁定水平输入后转为 FallState。
|
||||
/// 从 WallSlideState 进入;根据输入方向区分背墙跳与对墙跳;
|
||||
/// 蹬墙后标记 PostWallJump,允许空中靠近墙壁时自动抓墙(无需方向键);
|
||||
/// 输入锁结束后如贴墙则自动进入 WallSlideState;否则上升结束后转为 FallState。
|
||||
/// </summary>
|
||||
public class WallJumpState : PlayerStateBase
|
||||
{
|
||||
private float _inputLockTimer;
|
||||
private int _wallDir;
|
||||
private bool _jumpAway; // true = 背墙跳,false = 对墙跳
|
||||
|
||||
public WallJumpState(PlayerController owner) : base(owner) { }
|
||||
|
||||
public override void OnStateEnter()
|
||||
{
|
||||
// 记录墙壁方向(跳跃反向)
|
||||
// 记录墙壁方向
|
||||
_wallDir = Owner.WallDetector != null ? Owner.WallDetector.WallDirection : 0;
|
||||
if (_wallDir == 0) _wallDir = -Owner.FacingDirection;
|
||||
|
||||
// 施加蹬墙跳速度
|
||||
Move?.WallJump(_wallDir);
|
||||
// 根据输入方向判断跳跃类型:
|
||||
// 无输入或背离墙壁方向 → 背墙跳
|
||||
// 朝向墙壁方向 → 对墙跳
|
||||
float moveX = Input.MoveInput.x;
|
||||
bool pressingTowardWall = Mathf.Abs(moveX) > 0.01f
|
||||
&& (int)Mathf.Sign(moveX) == _wallDir;
|
||||
_jumpAway = !pressingTowardWall;
|
||||
|
||||
// 锁定水平输入
|
||||
// 施加蹬墙跳速度
|
||||
Move?.WallJump(_wallDir, _jumpAway);
|
||||
|
||||
// 锁定水平输入,防止立即覆盖跳跃速度
|
||||
_inputLockTimer = Cfg.WallJumpInputLockDuration;
|
||||
|
||||
// 播放跳跃动画(复用跳跃动画)
|
||||
if (AnimCfg?.Jump != null) Anim?.Play(AnimCfg.Jump);
|
||||
// 标记蹬墙跳后自动抓墙(在 FallState/WallJumpState 中消耗)
|
||||
Owner.SetPostWallJump(true);
|
||||
|
||||
// 蹬墙成功后立即恢复空中冲刺次数
|
||||
Owner.GetState<AerialDashState>()?.ResetAerialDashes();
|
||||
|
||||
if (AnimCfg?.Jump != null) Anim?.Play(AnimCfg.Jump);
|
||||
Input.JumpCancelledEvent += OnJumpCancelled;
|
||||
}
|
||||
|
||||
@@ -39,7 +53,18 @@ namespace BaseGames.Player.States
|
||||
|
||||
public override void OnStateUpdate()
|
||||
{
|
||||
// 上升结束 → 下落
|
||||
// 输入锁结束后检查是否贴墙:自动抓墙(优先于下落判断)
|
||||
if (_inputLockTimer <= 0f)
|
||||
{
|
||||
var wd = Owner.WallDetector;
|
||||
if (wd != null && wd.IsTouchingWall && !Move.IsGrounded)
|
||||
{
|
||||
Owner.TransitionTo(Owner.GetState<WallSlideState>());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 上升结束 → 下落(isPostWallJump 标记保留,FallState 中继续支持自动抓墙)
|
||||
if (!Move.IsRising)
|
||||
{
|
||||
Owner.TransitionTo(Owner.GetState<FallState>());
|
||||
|
||||
@@ -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>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user