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

Unity中如何将物体从碰撞点平滑移动至目标物体末端?

Unity中实现球体碰撞后平滑移动至方形Sprite末端的问题修复

原脚本存在的核心问题:

  • elapsedTime未重置:作为类成员变量,第一次协程执行后值会保留,后续碰撞触发协程时会直接跳过循环,导致移动不生效。
  • 末端位置计算逻辑错误:contactPoint.x是世界坐标,直接与Collider的尺寸做运算无法得到正确的末端位置,需要基于Roller的世界坐标范围计算。
  • 物理组件冲突:直接修改transform.position时,Rigidbody2D的物理属性(比如重力、碰撞)会干扰移动,应该暂时禁用物理或者使用Rigidbody的移动方法。
  • 变量拼写错误:rollerLegth应为rollerLength,属于不良编码习惯。

修正后的完整脚本:

public class HandleRollerMovement : MonoBehaviour
{
    private float desiredDuration = 1f;
    private BoxCollider2D boxCollider2D;
    private float rollerLength;

    private void Start()
    {
        boxCollider2D = GetComponent<BoxCollider2D>();
        rollerLength = boxCollider2D.bounds.size.x;
    }

    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.CompareTag("Egg"))
        {
            var eggRigidBody = collision.gameObject.GetComponent<Rigidbody2D>();
            if (eggRigidBody == null) return;

            var startPosition = eggRigidBody.transform.position;
            
            if (collision.contacts.Length == 0) return;
            var contactPoint = collision.contacts[0].point;

            // 获取Roller的世界坐标左右边界
            var rollerLeft = boxCollider2D.bounds.min.x;
            var rollerRight = boxCollider2D.bounds.max.x;

            // 根据碰撞点位置确定移动到Roller的哪一端
            Vector3 endPosition;
            if (contactPoint.x < boxCollider2D.bounds.center.x)
            {
                endPosition = new Vector3(rollerRight, startPosition.y, 0);
            }
            else
            {
                endPosition = new Vector3(rollerLeft, startPosition.y, 0);
            }

            StartCoroutine(MoveEgg(eggRigidBody, startPosition, endPosition));
        }
    }

    private IEnumerator MoveEgg(Rigidbody2D eggRb, Vector3 startPos, Vector3 endPos)
    {
        float elapsedTime = 0f;
        // 暂时禁用物理,避免干扰平滑移动
        var originalBodyType = eggRb.bodyType;
        eggRb.bodyType = RigidbodyType2D.Kinematic;

        while (elapsedTime < desiredDuration)
        {
            var currentPos = Vector3.Lerp(startPos, endPos, elapsedTime / desiredDuration);
            eggRb.MovePosition(currentPos);
            
            elapsedTime += Time.deltaTime;
            yield return null;
        }

        // 确保最终位置准确
        eggRb.MovePosition(endPos);
        // 恢复刚体原有物理状态
        eggRb.bodyType = originalBodyType;
    }
}

关键修改说明:

  1. 重置计时变量:将elapsedTime改为协程内的局部变量,每次触发协程都会重新初始化,保证每次移动都能正常执行。
  2. 修正位置计算:通过bounds.min.x和bounds.max.x获取Roller的世界坐标边界,根据碰撞点相对于Roller中心的位置,判断移动到左端还是右端,逻辑更准确。
  3. 处理物理干扰:移动时将Rigidbody设为Kinematic,避免重力、碰撞等物理效果打断平滑移动,完成后恢复原属性。
  4. 规范移动方式:使用Rigidbody2D的MovePosition方法而非直接修改transform.position,保证物理状态的一致性。
  5. 优化参数传递:将协程所需参数作为参数传入,避免依赖类成员变量,逻辑更清晰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 16:33:37