You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Celeste风格角色控制器移动时跳高异常问题求助

问题分析与修复方案

核心问题根源

  1. 跳跃高度不一致:原jump()方法直接覆盖整个刚体速度,导致移动状态下的水平速度被清空,同时FixedUpdate中重复调用减速逻辑,干扰了跳跃时的速度状态,最终造成移动/静止跳跃高度差异。
  2. 浮空与缓慢下落:jumpingGravityController()中,角色处于跳跃顶点(rb.velocity.y == 0)时会进入默认分支,重力缩放被设为1,导致下落初期加速度极低;长按跳跃时的重力缩放1过小,让上升阶段几乎不受重力,出现浮空效果。
  3. 逻辑顺序混乱:FixedUpdate中重复调用decelerateCharacter(),导致地面拖拽逻辑被多次覆盖,影响移动和跳跃的状态判断。
  4. 地面检测精度不足:从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;
        }
    }
}

关键修复点说明

  1. 跳跃逻辑修正:

    • Jump()方法不再覆盖整个velocity,而是保留水平方向速度,确保移动和静止时的跳跃高度完全一致。
  2. 重力控制重构:

    • 拆分normalGravityScale(正常重力)和jumpHoldGravityScale(长按跳跃重力),避免用1这种过小的值,让长按跳跃时有轻微的重力,既保留可控浮空感,又不会完全悬浮。
    • 单独处理跳跃顶点的重力设置,强制应用正常重力,解决顶点后缓慢下落的问题。
  3. FixedUpdate逻辑梳理:

    • 将地面和空中的移动逻辑拆分到独立方法,避免重复调用减速逻辑,流程更清晰。
    • 空中移动添加了水平力(倍率降低),更贴近Celeste的空中控制手感。
  4. 地面检测优化:

    • 添加groundRaycastOffset参数,将射线起点偏移到角色底部,避免因pivot在中心导致的落地检测延迟。
  5. 初始化重力:

    • 在Start()中初始化地面重力缩放,确保角色初始状态重力正常。

内容的提问来源于stack exchange,提问作者Kanav Gupta

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 05:44:50