using UnityEngine; namespace BaseGames.Player.States { /// /// 蹬墙跳状态(架构 05_PlayerModule §2,自定义蹬墙跳设计)。 /// /// 从 WallSlideState 进入(正常模式下按跳跃键触发)。 /// 分为两种子类型,由 PrepareEnter 时的水平输入决定: /// - 背墙跳(Jump Away):无输入或反方向输入 → 远离墙壁斜上方弹出(WallJumpAwayForce)。 /// - 对墙跳(Jump Toward):朝向墙壁方向输入 → 沿墙壁方向斜上方弹出(WallJumpTowardForce)。 /// /// 公共规则: /// 视为第一段跳(不消耗空中跳跃次数);支持可变高度(提前松键截断); /// 蹬墙后标记 PostWallJump,允许空中靠近墙壁时自动抓墙; /// 上升结束后转 FallState。 /// public class WallJumpState : PlayerStateBase { private float _inputLockTimer; private int _wallDir; private bool _isAwayJump; // true = 背墙跳;false = 对墙跳 public WallJumpState(PlayerController owner) : base(owner) { } /// /// 由 WallSlideState 在调用 TransitionTo 之前调用。 /// wallDir:抓墙方向(+1 右墙 / -1 左墙)。 /// moveInputX:触发跳跃时的水平输入值。 /// public void PrepareEnter(int wallDir, float moveInputX) { _wallDir = wallDir; // 无输入或输入方向与墙壁反向 → 背墙跳;输入方向与墙壁同向 → 对墙跳 int inputDir = moveInputX > 0.1f ? 1 : (moveInputX < -0.1f ? -1 : 0); _isAwayJump = (inputDir == 0 || inputDir != _wallDir); } public override void OnStateEnter() { // 施加对应类型的速度 if (_isAwayJump) Move?.WallJumpAway(_wallDir); else Move?.WallJumpToward(_wallDir); // 蹬墙跳视为第一段跳,重置空中跳跃次数(使二段跳可再次使用) Owner.ResetAirJumps(); _inputLockTimer = Cfg.WallJumpInputLockDuration; // 标记蹬墙跳后自动抓墙(在 FallState/JumpState 中消耗) Owner.SetPostWallJump(true); // 蹬墙成功后立即恢复空中冲刺次数 Owner.GetState()?.ResetDashCharge(); // 播放蹬墙跳动画:背墙跳/对墙跳使用各自专属 Clip,留空时回退到 Jump 动画 var wallJumpClip = _isAwayJump ? (AnimCfg?.WallJumpAway ?? AnimCfg?.Jump) : (AnimCfg?.WallJumpToward ?? AnimCfg?.Jump); if (wallJumpClip != null) Anim?.Play(wallJumpClip); Input.JumpCancelledEvent += OnJumpCancelled; } public override void OnStateExit() { Input.JumpCancelledEvent -= OnJumpCancelled; } 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()); return; } } public override void OnStateFixedUpdate() { _inputLockTimer -= Time.fixedDeltaTime; // 输入锁结束后允许水平控制 if (_inputLockTimer <= 0f && Mathf.Abs(Input.MoveInput.x) > 0.01f) Move?.Move(Input.MoveInput.x * Cfg.RunSpeed); } private void OnJumpCancelled() => Move?.CutJump(); } }