Files
zeling_v2/Assets/_Game/Scripts/Player/States/WallSlideState.cs
Joywayer f1fc356d99 Merge branch 'agents/wall-jump-logic-optimization'
# Conflicts:
#	Assets/_Game/Scripts/Player/PlayerMovement.cs
#	Assets/_Game/Scripts/Player/States/IdleState.cs
#	Assets/_Game/Scripts/Player/States/RunState.cs
#	Assets/_Game/Scripts/Player/States/WallJumpState.cs
#	Assets/_Game/Scripts/Player/States/WallSlideState.cs
2026-05-19 13:03:04 +08:00

138 lines
5.1 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
/// 维持条件:进入后无需持续按键;主动按下反方向键或落地时解除。
///
/// 高度记忆机制(防止单面墙反复爬升):
/// - 首次抓墙(或切换到另一侧墙壁)时记录 _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;
}
// 计算当前是否处于正常模式
UpdateCanJump();
if (AnimCfg?.WallSlide != null)
Anim?.Play(AnimCfg.WallSlide);
// 消耗蹬墙跳后的自动抓墙标记
Owner.SetPostWallJump(false);
Input.JumpStartedEvent += OnJumpPressed;
}
public override void OnStateExit()
{
Input.JumpStartedEvent -= OnJumpPressed;
}
public override void OnStateUpdate()
{
var wd = Owner.WallDetector;
// 离开墙壁 → 下落
if (wd == null || !wd.IsTouchingWall)
{
Owner.TransitionTo(Owner.GetState<FallState>());
return;
}
// 着地 → 闲置
if (Move.IsGrounded)
{
Owner.TransitionTo(Owner.GetState<IdleState>());
return;
}
// 主动按反方向键 → 脱离(松墙下落)
float mx = Input.MoveInput.x;
if (Mathf.Abs(mx) > 0.1f)
{
int inputDir = mx > 0f ? 1 : -1;
if (inputDir != _wallDir)
{
Owner.TransitionTo(Owner.GetState<FallState>());
return;
}
}
// 每帧刷新正常/受限状态
UpdateCanJump();
}
public override void OnStateFixedUpdate()
{
if (_canJump)
// 正常模式:静止悬挂,阻止向下速度
Move?.ZeroVerticalVelocity();
else
// 受限模式:持续下滑
Move?.ApplyWallSlide();
}
// ── 内部 ──────────────────────────────────────────────────────────────
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);
}
}
}