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

Unity基于Rigidbody的角色控制器问题:抖动、穿模及游戏卡顿

Unity Rigidbody角色控制器抖动、卡顿与穿模问题排查与修复建议

问题现象

  • 角色移动轨迹异常、抖动
  • 偶尔出现卡顿甚至卡死,卡顿后玩家可能穿模到地面下方或飞出场景
  • 触发滑行动作(仅播放动画,未修改碰撞体或Rigidbody)、落地后偶尔出现穿模,该问题非持续触发

核心代码

移动逻辑(原Move()函数,在FixedUpdate()中调用)

private void Move() { 
    if(!_hasAnimator) { return;}

    float targetSpeed = _inputManager.Run ? _runSpeed : _walkSpeed;
    if(_inputManager.Move == Vector2.zero) targetSpeed = 0;

    _currentVelocity.x = Mathf.Lerp(_currentVelocity.x, _inputManager.Move.x * targetSpeed, AnimBlendSpeed * Time.fixedDeltaTime);
    _currentVelocity.y = Mathf.Lerp(_currentVelocity.y, _inputManager.Move.y * targetSpeed, AnimBlendSpeed * Time.fixedDeltaTime);

    if(Mathf.Abs(_currentVelocity.x) < 0.01) _currentVelocity.x = 0;
    if(Mathf.Abs(_currentVelocity.y) < 0.01) _currentVelocity.y = 0;

    var xVelDifference = _currentVelocity.x - _playerRigidbody.velocity.x;
    var zVelDifference = _currentVelocity.y - _playerRigidbody.velocity.z;

    _playerRigidbody.AddForce(transform.TransformVector(new Vector3(xVelDifference, 0, zVelDifference)), ForceMode.VelocityChange);

    _animator.SetFloat(_xVelHash, _currentVelocity.x);
    _animator.SetFloat(_yVelHash, _currentVelocity.y); 
}

相机与角色旋转逻辑(原CameraMovements()函数,在LateUpdate()中调用)

private void CameraMovements() { 
    if(!_hasAnimator) { return;}

    var Mouse_X = _inputManager.Look.x;
    var Mouse_Y = _inputManager.Look.y;
    Camera.position = CameraRoot.position;

    _xRotation -= Mouse_Y * MouseSensitivity * Time.smoothDeltaTime;
    _xRotation = Mathf.Clamp(_xRotation, UpperLimit, BottomLimit);

    Camera.localRotation = Quaternion.Euler(_xRotation, 0, 0);
    _playerRigidbody.MoveRotation(_playerRigidbody.rotation * Quaternion.Euler(0, Mouse_X * MouseSensitivity * Time.smoothDeltaTime, 0));
}

问题根源分析

  1. 移动逻辑的力控制冲突
    使用ForceMode.VelocityChange强制修正速度,会和物理引擎的重力、碰撞反馈等原生力产生冲突,导致角色抖动;Mathf.Lerp结合Time.fixedDeltaTime的插值方式逻辑错误,插值比例随物理帧率波动,导致速度过渡不稳定。

  2. 旋转与物理帧不同步
    在LateUpdate()(渲染帧)中调用_playerRigidbody.MoveRotation,物理操作和渲染帧不同步,会引发角色旋转与移动的错位抖动;鼠标旋转使用Time.smoothDeltaTime,旋转速度随渲染帧率变化,进一步加剧不同步问题。

  3. 物理属性与碰撞体潜在问题
    未明确的刚体约束、碰撞体贴合度不足、碰撞检测模式设置不当,会在碰撞或卡顿后引发穿模。

修复方案

1. 重构移动逻辑,改用稳定的速度控制

替换原Move()函数,使用Mathf.SmoothDamp实现物理帧同步的速度平滑,直接设置刚体速度避免力冲突:

// 新增两个缓存变量用于SmoothDamp
private float _velXSmooth;
private float _velYSmooth;

private void Move() { 
    if(!_hasAnimator) { return;}

    float targetSpeed = _inputManager.Run ? _runSpeed : _walkSpeed;
    if(_inputManager.Move == Vector2.zero) targetSpeed = 0;

    // 计算目标局部速度,先归一化避免斜向移动超速
    Vector2 targetLocalVel = _inputManager.Move.normalized * targetSpeed;
    // 使用SmoothDamp实现稳定的速度过渡,适配物理帧
    _currentVelocity.x = Mathf.SmoothDamp(_currentVelocity.x, targetLocalVel.x, ref _velXSmooth, 0.1f, float.MaxValue, Time.fixedDeltaTime);
    _currentVelocity.y = Mathf.SmoothDamp(_currentVelocity.y, targetLocalVel.y, ref _velYSmooth, 0.1f, float.MaxValue, Time.fixedDeltaTime);

    // 将局部速度转换为世界空间,保留Y轴原有速度(重力)
    Vector3 targetWorldVel = transform.TransformVector(new Vector3(_currentVelocity.x, _playerRigidbody.velocity.y, _currentVelocity.y));
    // 直接设置刚体速度,避免力的冲突
    _playerRigidbody.velocity = targetWorldVel;

    _animator.SetFloat(_xVelHash, _currentVelocity.x);
    _animator.SetFloat(_yVelHash, _currentVelocity.y); 
}

2. 修正旋转逻辑的帧同步问题

将角色旋转逻辑移到FixedUpdate()中,保证物理操作与物理帧同步;相机垂直旋转保留在LateUpdate():

// 角色旋转逻辑,在FixedUpdate()中调用
private void RotatePlayer() {
    if(!_hasAnimator) { return;}
    var Mouse_X = _inputManager.Look.x;
    // 使用fixedDeltaTime保证物理帧同步的旋转速度
    float turnAmount = Mouse_X * MouseSensitivity * Time.fixedDeltaTime;
    _playerRigidbody.MoveRotation(_playerRigidbody.rotation * Quaternion.Euler(0, turnAmount, 0));
}

// 相机垂直旋转逻辑,保留在LateUpdate()
private void CameraMovements() { 
    if(!_hasAnimator) { return;}

    var Mouse_Y = _inputManager.Look.y;
    Camera.position = CameraRoot.position;

    _xRotation -= Mouse_Y * MouseSensitivity * Time.deltaTime;
    _xRotation = Mathf.Clamp(_xRotation, UpperLimit, BottomLimit);

    Camera.localRotation = Quaternion.Euler(_xRotation, 0, 0);
}

3. 物理属性与碰撞体优化

  • 刚体设置:锁定X、Z轴旋转(避免角色倾斜),设置Mass为8-12,Drag为0.1-0.5,Angular Drag为5-10,减少不必要的物理波动
  • 碰撞体设置:角色使用胶囊碰撞体(CapsuleCollider)并贴合模型;地面若使用网格碰撞体,勾选Convex以支持动态物体交互
  • 碰撞检测:角色刚体设为Continuous Dynamic,地面设为Continuous,减少高速移动时的穿模概率

4. 卡顿与穿模额外排查

  • 检查是否有其他脚本在非FixedUpdate()中修改刚体位置/速度(如动画脚本误操作Transform)
  • 滑行动作关闭动画根运动,避免动画位移与刚体移动冲突
  • 检查项目物理设置中的Fixed Timestep,建议设为0.02(50帧/秒),避免物理帧间隔过大引发卡顿

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 17:45:55