Celeste风格角色控制器移动时跳高异常问题求助
问题分析与修复方案
核心问题根源
- 跳跃高度不一致:原
jump()方法直接覆盖整个刚体速度,导致移动状态下的水平速度被清空,同时FixedUpdate中重复调用减速逻辑,干扰了跳跃时的速度状态,最终造成移动/静止跳跃高度差异。 - 浮空与缓慢下落:
jumpingGravityController()中,角色处于跳跃顶点(rb.velocity.y == 0)时会进入默认分支,重力缩放被设为1,导致下落初期加速度极低;长按跳跃时的重力缩放1过小,让上升阶段几乎不受重力,出现浮空效果。 - 逻辑顺序混乱:
FixedUpdate中重复调用decelerateCharacter(),导致地面拖拽逻辑被多次覆盖,影响移动和跳跃的状态判断。 - 地面检测精度不足:从transform.position发射射线,若角色 pivot不在底部,会导致落地检测延迟,触发错误的重力逻辑。
修复后的完整代码
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerMovement : MonoBehaviour { [Header("Components")] [SerializeField] private Rigidbody2D rb; [Header("Layer Masks")] [SerializeField] private LayerMask groundLayer; [Header("Movement Variables")] [SerializeField] private float maxMoveSpeed = 8f; [SerializeField] private float acceleration = 50f; [SerializeField] private float deceleration = 10f; private bool isChangingDirection => (rb.velocity.x > 0f && horizontalDirection < 0f) || (rb.velocity.x < 0f && horizontalDirection > 0f); private float horizontalDirection; [Header("Jump Variables")] [SerializeField] private float jumpForce = 12f; [SerializeField] private float airDecelerationRate = 2.5f; [SerializeField] private float normalGravityScale = 8f; [SerializeField] private float jumpHoldGravityScale = 2f; // 长按跳跃时的重力缩放,兼顾浮空感与下落速度 [Header("Ground Check Variables")] [SerializeField] private float groundRaycastLength = 0.2f; [SerializeField] private Vector2 groundRaycastOffset = new Vector2(0, -0.5f); // 射线起点偏移到角色底部 private bool onGround; private void Start() { // 初始化地面重力缩放 rb.gravityScale = normalGravityScale; } private void Update() { horizontalDirection = Input.GetAxisRaw("Horizontal"); if (Input.GetButtonDown("Jump") && onGround) { Jump(); } } private void FixedUpdate() { CheckCollision(); if (onGround) { GroundMovement(); } else { AirMovement(); JumpGravityController(); } } private void GroundMovement() { // 地面移动:添加水平力 rb.AddForce(Vector2.right * horizontalDirection * acceleration); // 限制最大速度 if (Mathf.Abs(rb.velocity.x) > maxMoveSpeed) { rb.velocity = new Vector2(Mathf.Sign(rb.velocity.x) * maxMoveSpeed, rb.velocity.y); } // 地面减速逻辑 if (Mathf.Abs(horizontalDirection) < 0.1f || isChangingDirection) { rb.drag = deceleration; } else { rb.drag = 0f; } } private void AirMovement() { // 空中移动:添加水平力(倍率降低,符合Celeste手感) rb.AddForce(Vector2.right * horizontalDirection * acceleration * 0.5f); // 限制空中最大速度 if (Mathf.Abs(rb.velocity.x) > maxMoveSpeed) { rb.velocity = new Vector2(Mathf.Sign(rb.velocity.x) * maxMoveSpeed, rb.velocity.y); } // 空中减速 rb.drag = airDecelerationRate; } private void Jump() { // 只覆盖竖直方向速度,保留水平速度,确保跳跃高度一致 rb.velocity = new Vector2(rb.velocity.x, jumpForce); } private void CheckCollision() { // 调整射线起点到角色底部,避免pivot位置导致的检测错误 Vector2 raycastOrigin = (Vector2)transform.position + groundRaycastOffset; onGround = Physics2D.Raycast(raycastOrigin, Vector2.down, groundRaycastLength, groundLayer); } private void OnDrawGizmos() { Gizmos.color = Color.black; Vector2 raycastOrigin = (Vector2)transform.position + groundRaycastOffset; Gizmos.DrawLine(raycastOrigin, raycastOrigin + Vector2.down * groundRaycastLength); } private void JumpGravityController() { if (rb.velocity.y < 0f) { // 下落时用正常重力 rb.gravityScale = normalGravityScale; } else if (rb.velocity.y > 0f) { // 上升时,根据是否按住跳跃调整重力 rb.gravityScale = Input.GetButton("Jump") ? jumpHoldGravityScale : normalGravityScale; } else { // 顶点时强制用正常重力,避免缓慢下落 rb.gravityScale = normalGravityScale; } } }
关键修复点说明
跳跃逻辑修正:
Jump()方法不再覆盖整个velocity,而是保留水平方向速度,确保移动和静止时的跳跃高度完全一致。
重力控制重构:
- 拆分
normalGravityScale(正常重力)和jumpHoldGravityScale(长按跳跃重力),避免用1这种过小的值,让长按跳跃时有轻微的重力,既保留可控浮空感,又不会完全悬浮。 - 单独处理跳跃顶点的重力设置,强制应用正常重力,解决顶点后缓慢下落的问题。
- 拆分
FixedUpdate逻辑梳理:
- 将地面和空中的移动逻辑拆分到独立方法,避免重复调用减速逻辑,流程更清晰。
- 空中移动添加了水平力(倍率降低),更贴近Celeste的空中控制手感。
地面检测优化:
- 添加
groundRaycastOffset参数,将射线起点偏移到角色底部,避免因pivot在中心导致的落地检测延迟。
- 添加
初始化重力:
- 在
Start()中初始化地面重力缩放,确保角色初始状态重力正常。
- 在
内容的提问来源于stack exchange,提问作者Kanav Gupta
相关产品推荐
相关产品推荐

