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

158 lines
7.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>
/// 跳跃状态。
/// - 一段跳 / 郊狼跳OnStateEnter 时调用 Move.Jump()。
/// - 二段跳(二段跳能力解锁后可用):上升或下落途中再按跳跃且 AirJumpsLeft > 0
/// 调用 Move.DoubleJump(),重播跳跃动画,不离开本状态(保持速度截断逻辑)。
/// - 空中冲刺:上升途中按冲刺且 HasAbility(AirDash) → DashState。
/// - 变高跳:松开跳跃键触发 JumpCancelledEvent → CutJump()(系数 = JumpCutMultiplier
/// - _isDoubleJump由 FallState 在转换前通过 SetDoubleJump(true) 预设,
/// 使 OnStateEnter 对二段跳调用 Move.DoubleJump() 而非 Move.Jump()。
/// </summary>
public class JumpState : PlayerStateBase
{
private bool _isDoubleJump;
public JumpState(PlayerController owner) : base(owner) { }
/// <summary>
/// 由 FallState 在转换到 JumpState 前调用,标记本次进入为二段跳,
/// 以便 OnStateEnter 使用 DoubleJumpForce 而非 JumpForce。
/// </summary>
public void SetDoubleJump(bool isDouble) => _isDoubleJump = isDouble;
public override void OnStateEnter()
{
if (AnimCfg?.Jump != null)
Anim.Play(AnimCfg.Jump);
if (_isDoubleJump)
{
Move.DoubleJump();
// 优先播放专属空中跳跃动画,未配置时回退到普通跳跃动画
var airJumpClip = AnimCfg?.AirJump ?? AnimCfg?.Jump;
if (airJumpClip != null) Anim?.Play(airJumpClip);
}
else
Move.Jump();
_isDoubleJump = false; // 消耗标记
Input.JumpCancelledEvent += OnJumpCancelled;
}
public override void OnStateUpdate()
{
// 上升结束时转为下落
if (Move.Rb.velocity.y <= 0f)
{
_owner.TransitionTo(_owner.GetState<FallState>());
return;
}
// 冲刺(地面/空中统一使用 DashState空中限一次优先于二段跳冲刺可保存二段跳机会
// 先确认能力与冷却均满足,再消耗缓冲,避免无操作时静默吃掉输入
var dashState = Owner.GetState<DashState>();
if (dashState != null && dashState.CanDashMidAir
&& Stats != null && Stats.HasAbility(AbilityType.Dash)
&& Buffer.ConsumeDash())
{
_owner.TransitionTo(dashState);
return;
}
// 二段跳:上升阶段即可触发(先确认有次数再消耗缓冲,避免静默消耗)
if (Owner.AirJumpsLeft > 0 && Buffer.ConsumeJump())
{
Owner.UseAirJump();
Move.DoubleJump();
// 播放空中跳跃动画,未配置时回退到 Jump
var airJumpClip = AnimCfg?.AirJump ?? AnimCfg?.Jump;
if (airJumpClip != null) Anim?.Play(airJumpClip);
return;
}
// 空中攻击Move Y > 0 → 上劈Y < -0.5 且解锁下劈 → 下劈;其余 → 空中攻击
if (Buffer.ConsumeAttack())
{
if (Input.MoveInput.y > 0.5f)
_owner.TransitionTo(_owner.GetState<UpAttackState>());
else if (Input.MoveInput.y < -0.5f
&& Stats != null && Stats.HasAbility(AbilityType.DownSlash))
_owner.TransitionTo(_owner.GetState<DownAttackState>());
else
_owner.TransitionTo(_owner.GetState<AirAttackState>());
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)
&& Stats != null && Stats.HasAbility(AbilityType.WallCling))
{
_owner.TransitionTo(_owner.GetState<WallSlideState>());
return;
}
}
}
public override void OnStateFixedUpdate()
{
// ── 顶点悬停:第一判断 |vy|,动态切换重力缩放系数 ────────────────
// |垂直速度| 低于顶点阈值时,重力缩减至 ApexGravityMultiplier 倍,
// 产生角色在跳跃顶点起起“滒空”的手感,属于高重力平台游戏的标志性特征。
// 转入 FallState 时 OnStateExit 会恢复默认重力,不会漏到 FallState。
float absVY = Mathf.Abs(Move.Rb.velocity.y);
if (absVY < Cfg.ApexThreshold)
Move.SetGravityScale(Cfg.DefaultGravityScale * Cfg.ApexGravityMultiplier);
else
Move.SetGravityScale(Cfg.DefaultGravityScale);
// ── 空中水平移动(朝向墙壁时停止施力,防止贴墙悬停)──────────────
if (Mathf.Abs(Input.MoveInput.x) > 0.01f)
{
int inputDir = Input.MoveInput.x > 0 ? 1 : -1;
var wd = Owner.WallDetector;
if (wd != null && wd.IsTouchingWall && wd.WallDirection == inputDir)
{
// 按下朝墙方向键 + 在墙上(且未著地)→ 进入抓墙状态
var wss = Owner.GetState<WallSlideState>();
if (wss != null && !Move.IsGrounded)
{
wss.PrepareEnter(inputDir);
Owner.TransitionTo(wss);
return;
}
Move.ZeroHorizontalVelocity();
}
else if (wd != null && wd.HasPartialContact(inputDir))
{
// 仅部分射线命中(如检测点高于矮墙顶部),停止施加朝墙方向的水平速度,
// 防止角色边角被卡在墙顶而无法继续下落。
Move.ZeroHorizontalVelocity();
}
else
Move.Move(Input.MoveInput.x * Cfg.RunSpeed);
}
else
Move.ZeroHorizontalVelocity();
}
public override void OnStateExit()
{
// 顶点悬停可能已降低重力,离开本状态时必须恢复;
// 否则进入 FallState/DashState 等后续状态时重力仍低于默认值。
Move.SetGravityScale(Cfg.DefaultGravityScale);
Input.JumpCancelledEvent -= OnJumpCancelled;
}
private void OnJumpCancelled() => Move.CutJump();
}
}