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

Unity 3D重力切换玩法下玩家控制器跳跃功能异常修复求助

故障根因

你当前的代码存在4个核心问题导致跳跃表现混乱:

  1. 物理逻辑放在了Update()中执行:Update的执行帧率随设备性能波动,而Rigidbody相关的物理操作必须放在FixedUpdate()中才能保证表现一致,直接在Update中修改rb.velocity会出现卡顿、力度忽大忽小的问题。
  2. 地面检测逻辑错误:OnCollision系列方法只要碰到任何物体(包括墙面、天花板)都会触发isGrounded = true,会出现贴墙跳、碰顶跳的异常表现,没有校验接触点是否是可站立的地面。
  3. 跳跃触发没有加地面判定:只要按空格就会赋值跳跃力度,会出现空中无限跳的问题;同时重力计算没有乘时间增量,不同帧率下重力加速度表现不一致。
  4. 速度赋值逻辑冗余错误:开头的if (((isShifting && isRunning)||!(isShifting && isRunning)))判断恒为真,没有实际作用;同时直接全量覆盖rb.velocity会冲掉物理引擎产生的碰撞速度,且存在重复乘速度系数的可能,导致力度计算错误。
修复方案

1. 代码结构调整

把输入检测和物理逻辑拆分,输入检测保留在Update()中,所有Rigidbody操作、重力、跳跃逻辑移到FixedUpdate()中,新增jumpRequested变量缓存跳跃请求:

// 新增类变量
private bool jumpRequested = false;

void Update()
{
    // 输入检测保留在Update中
    isRunning = Input.GetKey(KeyCode.LeftControl);
    isShifting = IntToBool((int)((BoolToInt(Input.GetKey(KeyCode.LeftAlt)))*BoolToInt(!isShiftable))+(BoolToInt(Input.GetKey(KeyCode.LeftShift))*BoolToInt(isShiftable)));

    // 删掉冗余的恒真判断
    curSpeedX = walkingSpeed * Input.GetAxis("Vertical");
    curSpeedY = walkingSpeed * Input.GetAxis("Horizontal");
    if (isShifting) {
        curSpeedX = ShiftSpeed * Input.GetAxis("Vertical");
        curSpeedY = ShiftSpeed * Input.GetAxis("Horizontal");
    }
    if (isRunning) {
        curSpeedX = runningSpeed * Input.GetAxis("Vertical");
        curSpeedY = runningSpeed * Input.GetAxis("Horizontal");
    }

    // 跳跃输入检测加地面判定
    if (Input.GetKeyDown(KeyCode.Space) && isGrounded) {
        jumpRequested = true;
    }
}

void FixedUpdate()
{
    // 重力计算乘固定时间增量,保证不同设备表现一致
    if (!isGrounded) {
        ypoa -= gravity * Time.fixedDeltaTime;
    }
    // 处理跳跃请求
    if (jumpRequested) {
        ypoa = jumpheight;
        jumpRequested = false;
        isGrounded = false;
    }
    // 计算最终速度并赋值
    Vector3 moveDir = (ypoa * transform.up) + (curSpeedX * transform.forward) + (curSpeedY * transform.right);
    rb.velocity = moveDir * speed; // 若速度数值异常可先去掉*speed,调整walkingSpeed等基础参数到合适值
}

2. 修正地面检测逻辑

修改碰撞检测代码,仅当接触点法线和玩家当前up向量夹角小于30°(可调整)时判定为站在地面:

void OnCollisionStay(Collision collision) {
    foreach (ContactPoint contact in collision.contacts)
    {
        if (Vector3.Angle(contact.normal, transform.up) < 30f)
        {
            isGrounded = true;
            ypoa = 0;
            jumcount = 0;
            break;
        }
    }
}

void OnCollisionExit(Collision collision) {
    isGrounded = false;
}
// 删掉原有的OnCollisionEnter逻辑,合并到OnCollisionStay中即可

3. Rigidbody组件配置

  • 关闭组件的Use Gravity选项,避免自行计算的重力和引擎自带重力叠加
  • 勾选Freeze Rotation下的X、Y、Z三个选项,避免玩家碰撞后发生旋转,导致transform.up方向异常
优化建议
  • 可以额外增加短射线检测辅助地面判定:在玩家位置向-transform.up方向发射长度为0.1~0.2f的射线,碰到碰撞体时也判定为isGrounded = true,避免碰撞检测延迟导致跳不起来
  • 重力切换时直接修改transform.up为当前重力的反方向,这套逻辑可直接适配全角度地图的重力切换需求

内容的提问来源于stack exchange,提问作者sam oakes

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 20:15:05