Files
zeling_v2/Assets/_Game/Scripts/Player/States/WallSlideState.cs
Joywayer 68d4c699ae 修复内容:
PlayerMovement:新增 _facingLocked 字段 + LockFacing(bool) 方法;UpdateFacing() 锁定时直接返回
WallSlideState:OnStateEnter 调用 LockFacing(true) + FlipFacing(_wallDir);OnStateExit 调用 LockFacing(false) 解锁
WallJumpState:OnStateEnter 保险性再调一次 LockFacing(false);WallJumpAway/Toward 同步写入 _inputVelocityX,确保解锁后 UpdateFacing 朝向正确(背墙跳 = 离墙方向,对墙跳 = 朝墙方向)
2026-05-22 10:48:52 +08:00

178 lines
7.3 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using UnityEngine;
namespace BaseGames.Player.States
{
/// <summary>
/// 抓墙状态(自定义设计,架构 05_PlayerModule §2
///
/// 触发条件:空中贴墙时玩家按下朝向墙壁的方向键(由 FallState/JumpState 检测并调用 PrepareEnter
/// 维持条件:进入后无需持续按键;按下↓键或落地时解除;跳跃键触发蹬墙跳后由 WallJumpState 接管。
///
/// 脱离方式:
/// - 跳跃键 → 蹬墙跳WallJumpState由 OnJumpPressed 响应)
/// - ↓ 键 / 反方向键 → 主动脱离,进入 FallStatewall coyote 窗口内仍可触发蹬墙跳
/// - 离开墙面 → 自然下落FallState
/// - 着地 → 闲置IdleState
///
/// 下滑速度分为两档(均在 PlayerMovementConfigSO 配置):
/// - 正常模式(低于等于 wallGrabY可蹬墙跳→ WallHangSpeed缓慢下滑
/// - 受限模式(高于 wallGrabY不可蹬墙跳 → WallSlideSpeed较快下滑
///
/// 高度记忆机制(防止单面墙反复爬升):
/// - 首次抓墙(或切换到另一侧墙壁)时记录 _wallGrabY。
/// - 若当前 Y > _wallGrabY + 容差 → 受限模式:持续下滑,不可蹬墙跳。
/// - 若当前 Y ≤ _wallGrabY + 容差 → 正常模式:静止悬挂,可触发蹬墙跳。
/// - 重置时机:① 落地时(由 IdleState/RunState 调用 ResetWallGrab
/// ② 切换到另一侧墙壁时OnStateEnter 内自动判断)。
/// </summary>
public class WallSlideState : PlayerStateBase
{
// ── 运行时状态 ────────────────────────────────────────────────────────
/// <summary>首次抓住该侧墙壁时记录的 Y 坐标。</summary>
private float _wallGrabY = float.MinValue;
/// <summary>上次记录 wallGrabY 时的墙壁方向(+1 右墙 / -1 左墙)。</summary>
private int _lastGrabDir = 0;
/// <summary>本次进入时确认的墙壁方向(由 PrepareEnter 设置)。</summary>
private int _wallDir = 0;
/// <summary>当前是否处于正常模式可蹬墙跳。受限模式isRestricted时不可跳。</summary>
private bool _canJump = false;
public WallSlideState(PlayerController owner) : base(owner) { }
/// <summary>
/// 由 FallState / JumpState 在调用 TransitionTo 之前调用,传入已确认的墙壁方向。
/// </summary>
public void PrepareEnter(int wallDir) => _wallDir = wallDir;
/// <summary>
/// 落地时由 IdleState / RunState 调用,重置高度记忆,允许下次抓同侧墙时重新计算。
/// </summary>
public void ResetWallGrab()
{
_wallGrabY = float.MinValue;
_lastGrabDir = 0;
}
public override void OnStateEnter()
{
// 若切换到另一侧墙壁,重置高度记录(视为全新的墙壁)
if (_wallDir != _lastGrabDir)
{
_wallGrabY = Owner.transform.position.y;
_lastGrabDir = _wallDir;
}
// 锁定自动朝向,防止 LateUpdate 的 UpdateFacing 覆盖手动设置的朝向
Move?.LockFacing(true);
// 抓墙时始终面朝墙壁,确保背墙跳的 FlipFacing(-_wallDir) 能正确翻转朝向
Move?.FlipFacing(_wallDir);
// 计算当前是否处于正常模式
UpdateCanJump();
if (AnimCfg?.WallSlide != null)
Anim?.Play(AnimCfg.WallSlide);
// 消耗蹬墙跳后的自动抓墙标记
Owner.SetPostWallJump(false);
Input.JumpStartedEvent += OnJumpPressed;
}
public override void OnStateExit()
{
Move?.LockFacing(false);
Input.JumpStartedEvent -= OnJumpPressed;
}
public override void OnStateUpdate()
{
var wd = Owner.WallDetector;
// 离开墙壁 → 启动墙壁土狼时间后下落
if (wd == null || !wd.IsTouchingWall)
{
Move.StartWallCoyote(_wallDir);
Owner.TransitionTo(Owner.GetState<FallState>());
return;
}
// 着地 → 闲置
if (Move.IsGrounded)
{
Owner.TransitionTo(Owner.GetState<IdleState>());
return;
}
// ── 抓墙攻击:优先于方向键脱离检测,朝离墙方向翻转后进入空中攻击(无土狼时间)──
if (Buffer.ConsumeAttack())
{
Move.FlipFacing(-_wallDir);
Owner.TransitionTo(Owner.GetState<AirAttackState>());
return;
}
// ── 抓墙冲刺:优先于方向键脱离检测,朝离墙方向翻转后冲出(无土狼时间)──────────
var ds = Owner.GetState<DashState>();
if (ds != null && ds.CanDashMidAir
&& Stats != null && Stats.HasAbility(AbilityType.Dash)
&& Buffer.ConsumeDash())
{
Move.FlipFacing(-_wallDir);
Owner.TransitionTo(ds);
return;
}
// 按下方向键 → 启动墙壁土狼时间后主动脱离,自然下落
if (Input.MoveInput.y < -0.5f)
{
Move.StartWallCoyote(_wallDir);
Owner.TransitionTo(Owner.GetState<FallState>());
return;
}
// 按反方向键 → 启动墙壁土狼时间后脱离
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)
// 正常模式(低于等于 wallGrabY可蹬墙跳缓慢下滑给玩家操作窗口
Move?.ApplyWallSlide(Cfg.WallHangSpeed);
else
// 受限模式(高于 wallGrabY较快下滑不可蹬墙跳
Move?.ApplyWallSlide(Cfg.WallSlideSpeed);
}
// ── 内部 ──────────────────────────────────────────────────────────────
private void UpdateCanJump()
{
float tolerance = Cfg?.WallGrabHeightTolerance ?? 0.05f;
_canJump = Owner.transform.position.y <= _wallGrabY + tolerance;
}
private void OnJumpPressed()
{
// 受限模式禁止蹬墙跳
if (!_canJump) return;
var wjs = Owner.GetState<WallJumpState>();
if (wjs == null) return;
wjs.PrepareEnter(_wallDir, Input.MoveInput.x);
Owner.TransitionTo(wjs);
}
}
}