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

Unity C#物体跟随脚本到达目标最小距离时发生抖动如何解决

抖动问题触发原因
  • 阈值判断逻辑存在过冲漏洞:当前仅使用单一阈值判断是否移动,每帧移动步长为speed * Time.deltaTime,受帧率波动影响步长不固定,极容易出现「当前帧移动后距离略小于阈值停止,下一帧因刚体同步误差、浮点数精度问题或目标微小位移,距离又略大于阈值重新移动」的反复触发情况,直接表现为来回抖动。
  • Transform与Rigidbody操作混合冲突:第三版优化代码中同时存在直接修改transform.rotation的操作和Rigidbody.MovePosition的物理API调用,两类更新的触发时机不同步(Transform更新在渲染帧,Rigidbody更新在物理帧),会产生额外的位置、旋转跳变,放大抖动效果。
  • 早期版本代码逻辑错误:第二版直接操作Rigidbody的代码中,robot.MovePosition(target.position * speed * Time.deltaTime)的位移计算逻辑完全错误,会直接将机器人向目标点位置拉拽,而非沿方向逐步移动,也会导致异常位移抖动。
可行修复方案

1. 新增双层阈值死区

放弃单一停止阈值,设置两层阈值构成缓冲区间:外层为启动移动阈值,内层为停止移动阈值,只有距离大于外层阈值时才启动移动,距离小于内层阈值时才停止移动,中间区间保持当前运动状态,避免临界值反复触发。

2. 统一使用Rigidbody物理API

旋转也改用Rigidbody.MoveRotation进行更新,保证位置、旋转的修改都在物理帧同步执行,避免跨帧不同步导致的跳变。

3. 进入停止区间后锁定刚体状态

当距离小于停止阈值时,主动将刚体的速度、角速度重置为0,避免物理惯性、碰撞反弹带来的额外位移导致重复触发移动。

修复后参考代码
public class RobotMovement : MonoBehaviour {
    private Rigidbody robot;
    private Transform ball;

    // 移动参数
    [Header("Movement Settings")]
    private float robotMovSpeed = 2f;
    private float distanceToTarget;
    // 双层阈值:外层启动移动,内层停止移动,差值避免抖动
    public float startMoveThreshold = 2.2f;
    public float stopMoveThreshold = 1.8f;
    private bool isMoving = false;

    // 旋转参数
    [Header("Rotation Settings")]
    private Vector3 targetDirection;
    private Quaternion lookAtTarget;
    private float rotationSpeed = 2f;

    void Start() {
        robot = GetComponent<Rigidbody>();
        // 禁用刚体自带的旋转约束,避免和我们的旋转逻辑冲突
        robot.freezeRotation = true;
        ball = GameObject.FindGameObjectWithTag("Ball").transform;
    }

    void FollowTarget(Transform target, float speed) {
        distanceToTarget = Vector3.Distance(robot.position, target.position);
        targetDirection = (target.position - robot.position).normalized;
        lookAtTarget = Quaternion.LookRotation(targetDirection);

        // 双层阈值判断更新运动状态
        if (distanceToTarget >= startMoveThreshold) {
            isMoving = true;
        } else if (distanceToTarget <= stopMoveThreshold) {
            isMoving = false;
            // 停止时清空速度避免惯性位移
            robot.velocity = Vector3.zero;
            robot.angularVelocity = Vector3.zero;
        }

        if (isMoving) {
            // 统一用Rigidbody API更新旋转和位置
            robot.MoveRotation(Quaternion.Slerp(robot.rotation, lookAtTarget, rotationSpeed * Time.fixedDeltaTime));
            robot.MovePosition(robot.position + targetDirection * speed * Time.fixedDeltaTime);
        }
     }  

    void FixedUpdate() {
        FollowTarget(ball, robotMovSpeed);
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 22:45:07