Files
zeling_v2/Assets/_Game/Scripts/Player/States/WallJumpState.cs

115 lines
4.5 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自定义蹬墙跳设计
///
/// 从 WallSlideState 进入(正常模式下按跳跃键触发)。
/// 分为两种子类型,由 PrepareEnter 时的水平输入决定:
/// - 背墙跳Jump Away无输入或反方向输入 → 远离墙壁斜上方弹出WallJumpAwayForce
/// - 对墙跳Jump Toward朝向墙壁方向输入 → 沿墙壁方向斜上方弹出WallJumpTowardForce
///
/// 公共规则:
/// 视为第一段跳(不消耗空中跳跃次数);支持可变高度(提前松键截断);
/// 蹬墙后标记 PostWallJump允许空中靠近墙壁时自动抓墙
/// 上升结束后转 FallState。
/// </summary>
public class WallJumpState : PlayerStateBase
{
private float _inputLockTimer;
private int _wallDir;
private bool _isAwayJump; // true = 背墙跳false = 对墙跳
public WallJumpState(PlayerController owner) : base(owner) { }
/// <summary>
/// 由 WallSlideState 在调用 TransitionTo 之前调用。
/// wallDir抓墙方向+1 右墙 / -1 左墙)。
/// moveInputX触发跳跃时的水平输入值。
/// </summary>
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);
// 背墙跳:立即转向远离墙壁的方向(不清零速度)
Move?.FlipFacing(-_wallDir);
}
else
Move?.WallJumpToward(_wallDir);
// 蹬墙跳视为第一段跳,重置空中跳跃次数(使二段跳可再次使用)
Owner.ResetAirJumps();
_inputLockTimer = Cfg.WallJumpInputLockDuration;
// 标记蹬墙跳后自动抓墙(在 FallState/JumpState 中消耗)
Owner.SetPostWallJump(true);
// 蹬墙成功后立即恢复空中冲刺次数
Owner.GetState<DashState>()?.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)
{
// 传入真实墙壁方向,确保切换到对面墙时 WallSlideState 能正确刷新抓墙高度
var wss = Owner.GetState<WallSlideState>();
if (wss != null)
{
wss.PrepareEnter(wd.WallDirection);
Owner.TransitionTo(wss);
}
return;
}
}
// 上升结束 → 下落isPostWallJump 标记保留FallState 中继续支持自动抓墙)
if (!Move.IsRising)
{
Owner.TransitionTo(Owner.GetState<FallState>());
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();
}
}