diff --git a/Assets/_Game/Scripts/Player/States/FallState.cs b/Assets/_Game/Scripts/Player/States/FallState.cs index 957d449..daca388 100644 --- a/Assets/_Game/Scripts/Player/States/FallState.cs +++ b/Assets/_Game/Scripts/Player/States/FallState.cs @@ -76,6 +76,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()); + return; + } + } + } public override void OnStateFixedUpdate() diff --git a/Assets/_Game/Scripts/Player/States/IdleState.cs b/Assets/_Game/Scripts/Player/States/IdleState.cs index a401804..8adc4fe 100644 --- a/Assets/_Game/Scripts/Player/States/IdleState.cs +++ b/Assets/_Game/Scripts/Player/States/IdleState.cs @@ -12,10 +12,11 @@ namespace BaseGames.Player.States if (AnimCfg?.Idle != null) Anim.Play(AnimCfg.Idle); Move?.ZeroHorizontalVelocity(); - // 落地时重置空中能力计数器 + // 落地时重置空中能力计数器及抓墙记录 Owner.ResetAirJumps(); Owner.GetState()?.ResetAirDash(); Owner.GetState()?.ResetWallGrab(); + Owner.SetPostWallJump(false); } public override void OnStateUpdate() diff --git a/Assets/_Game/Scripts/Player/States/JumpState.cs b/Assets/_Game/Scripts/Player/States/JumpState.cs index 4fa79f9..a94091a 100644 --- a/Assets/_Game/Scripts/Player/States/JumpState.cs +++ b/Assets/_Game/Scripts/Player/States/JumpState.cs @@ -85,6 +85,20 @@ namespace BaseGames.Player.States _owner.TransitionTo(_owner.GetState()); 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()); + return; + } + } } public override void OnStateFixedUpdate() diff --git a/Assets/_Game/Scripts/Player/States/PlayerController.cs b/Assets/_Game/Scripts/Player/States/PlayerController.cs index c0d7b2d..810629f 100644 --- a/Assets/_Game/Scripts/Player/States/PlayerController.cs +++ b/Assets/_Game/Scripts/Player/States/PlayerController.cs @@ -153,6 +153,47 @@ namespace BaseGames.Player.States ? (_movementConfig != null ? _movementConfig.MaxAirJumps : 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; + + /// + /// 进入 WallSlideState 时调用。 + /// 不同墙壁(dir != _wallGrabDir)→ 重置并记录新高度; + /// 同一面墙壁 → 保留原记录(防止攀爬重置)。 + /// + public void RecordWallGrab(int dir, float y) + { + if (dir != _wallGrabDir) + { + _wallGrabDir = dir; + _wallGrabY = y; + } + // 同一面墙:保留 _wallGrabY,不更新 + } + + /// 落地时重置抓墙记录(由 IdleState/RunState.OnStateEnter 调用)。 + public void ResetWallGrab() + { + _wallGrabY = float.NegativeInfinity; + _wallGrabDir = 0; + } + + /// + /// 设置蹬墙跳后自动抓墙标记。 + /// true:由 WallJumpState.OnStateEnter 设置; + /// false:由 IdleState/RunState.OnStateEnter(落地)或 WallSlideState.OnStateEnter(已消耗)清除。 + /// + public void SetPostWallJump(bool value) => _isPostWallJump = value; + // ── Overlay Layer API(供 SpringState / SoulSkill 等叠加动画使用)───── /// /// 在 Overlay Layer(Layer 1)播放动画,叠加于当前 Base Layer 动画之上。 diff --git a/Assets/_Game/Scripts/Player/States/RunState.cs b/Assets/_Game/Scripts/Player/States/RunState.cs index c94f65e..4e19b43 100644 --- a/Assets/_Game/Scripts/Player/States/RunState.cs +++ b/Assets/_Game/Scripts/Player/States/RunState.cs @@ -11,10 +11,11 @@ namespace BaseGames.Player.States { if (AnimCfg?.Run != null) Anim.Play(AnimCfg.Run); - // 落地时重置空中能力计数器(绝大多数情况被 IdleState 覆盖,但水平落地直接进入 RunState 时也需要) + // 落地时重置空中能力计数器及抓墙记录(水平落地直接进入 RunState 时) Owner.ResetAirJumps(); Owner.GetState()?.ResetAirDash(); Owner.GetState()?.ResetWallGrab(); + Owner.SetPostWallJump(false); } public override void OnStateUpdate() diff --git a/Assets/_Game/Scripts/Player/States/WallJumpState.cs b/Assets/_Game/Scripts/Player/States/WallJumpState.cs index 9c1411b..16efa19 100644 --- a/Assets/_Game/Scripts/Player/States/WallJumpState.cs +++ b/Assets/_Game/Scripts/Player/States/WallJumpState.cs @@ -12,6 +12,7 @@ namespace BaseGames.Player.States /// /// 公共规则: /// 视为第一段跳(不消耗空中跳跃次数);支持可变高度(提前松键截断); + /// 蹬墙后标记 PostWallJump,允许空中靠近墙壁时自动抓墙; /// 上升结束后转 FallState。 /// public class WallJumpState : PlayerStateBase @@ -48,12 +49,17 @@ namespace BaseGames.Player.States _inputLockTimer = Cfg.WallJumpInputLockDuration; + // 标记蹬墙跳后自动抓墙(在 FallState/JumpState 中消耗) + Owner.SetPostWallJump(true); + + // 蹬墙成功后立即恢复空中冲刺次数 + Owner.GetState()?.ResetAerialDashes(); + // 播放蹬墙跳动画:背墙跳/对墙跳使用各自专属 Clip,留空时回退到 Jump 动画 var wallJumpClip = _isAwayJump ? (AnimCfg?.WallJumpAway ?? AnimCfg?.Jump) : (AnimCfg?.WallJumpToward ?? AnimCfg?.Jump); if (wallJumpClip != null) Anim?.Play(wallJumpClip); - Input.JumpCancelledEvent += OnJumpCancelled; } @@ -64,7 +70,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()); + return; + } + } + + // 上升结束 → 下落(isPostWallJump 标记保留,FallState 中继续支持自动抓墙) if (!Move.IsRising) { Owner.TransitionTo(Owner.GetState()); diff --git a/Assets/_Game/Scripts/Player/States/WallSlideState.cs b/Assets/_Game/Scripts/Player/States/WallSlideState.cs index 3f7a534..a5a5f65 100644 --- a/Assets/_Game/Scripts/Player/States/WallSlideState.cs +++ b/Assets/_Game/Scripts/Player/States/WallSlideState.cs @@ -58,6 +58,9 @@ namespace BaseGames.Player.States if (AnimCfg?.WallSlide != null) Anim?.Play(AnimCfg.WallSlide); + // 消耗蹬墙跳后的自动抓墙标记 + Owner.SetPostWallJump(false); + Input.JumpStartedEvent += OnJumpPressed; }