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

Unity中2D拖拽动态刚体失控旋转问题求助

问题描述

我是Unity新手,正在开发一款2D游戏,需要实现拖拽动态物理物体(如方块)并模拟拖拽时物体其余部分带有重量的效果(类似ragdoll但无关节),即物体的质心会以点击点为支点摆动。

目前我已制作出可跟随光标移动的刚体对象,通过子物体设置质心,但仅在松开物体时生效。我尝试使用Rigidbody2D.AddForceAtPosition方法,让初始抓取点跟随光标拖动物体,但物体出现旋转、失控跳跃的问题。此前实现光标跟随时曾通过添加与速度成正比的阻力解决过力过冲问题,现在我在父物体上挂载了两个脚本(拖拽脚本和质心设置脚本),希望能获得代码修改或方案调整的帮助。

现有代码

拖拽脚本(ApplyContinuousForce)

using UnityEngine;
public class ApplyContinuousForce : MonoBehaviour
{
    public float forceMag = 0.5f; // Force magnitude
    public float resistance = 2f;
    public float maxLaunchVelocity = 10f;

    private bool isDragging = false;
    private Vector2 force;
    private Camera mainCamera;
    private Vector3 initialClickOffset;
    private Rigidbody2D rb;

    void Start()
    {
        mainCamera = Camera.main;
        rb = GetComponent<Rigidbody2D>();

        if (rb == null)
        {
            Debug.LogError("No Rigidbody2D component found on this GameObject.");
        }
    }

    void OnMouseDown()
    {
        isDragging = true;
        Vector3 mousePosition = GetMouseWorldPosition();
        initialClickOffset = transform.position - mousePosition;
        Debug.Log("Mouse Down - Start Dragging");
    }

    void OnMouseUp()
    {
        isDragging = false;
        rb.velocity = Vector2.ClampMagnitude(rb.velocity, maxLaunchVelocity);
        Debug.Log("Mouse Up - Stop Dragging. Clamped Velocity: " + rb.velocity);
    }

    void Update()
    {
        if (isDragging)
        {
            Vector3 currentMousePosition = GetMouseWorldPosition();
            Vector2 targetPosition = currentMousePosition + initialClickOffset;
            Vector2 offset = targetPosition - (Vector2)transform.position;

            // Calculate the force based on the mouse movement and apply resistance
            force = offset * forceMag - rb.velocity*resistance;

            // Apply the force continuously while dragging
            rb.AddForceAtPosition(force, currentMousePosition);
            Debug.Log("Dragging - Force: " + force+" Torque: "+rb.angularVelocity);
        }
    }

    private Vector3 GetMouseWorldPosition()
    {
        Vector3 mousePoint = Input.mousePosition;
        mousePoint.z = 10f; // Set this to the distance from the camera to the object
        return mainCamera.ScreenToWorldPoint(mousePoint);
    }
}

质心设置脚本(ChangeCenterOfMass)

using UnityEngine;

public class ChangeCenterOfMass : MonoBehaviour
{
    public Transform CenterOfMassReference; // The reference transform for the new center of mass
    private Vector2 centerOfMass;
    private Rigidbody2D rb;

    void Start()
    {
        rb = GetComponent<Rigidbody2D>();

        if (rb != null && CenterOfMassReference != null)
        {
            // Calculate the new center of mass in local space coordinates
            centerOfMass = transform.InverseTransformPoint(CenterOfMassReference.position);
            // Set the new center of mass
            rb.centerOfMass = centerOfMass;
        }
    }
}

解决方案与代码修改

拖拽脚本优化版

using UnityEngine;
public class ApplyContinuousForce : MonoBehaviour
{
    public float forceMag = 0.8f;
    public float resistance = 2.5f;
    public float maxLaunchVelocity = 10f;
    public float dragAngularResistance = 6f; // 拖拽时临时提升角阻力

    private bool isDragging = false;
    private Camera mainCamera;
    private Vector3 localGrabPoint; // 记录点击点的本地坐标
    private Rigidbody2D rb;
    private float originalAngularDrag; // 保存原始角阻力

    void Start()
    {
        mainCamera = Camera.main;
        rb = GetComponent<Rigidbody2D>();

        if (rb == null)
        {
            Debug.LogError("No Rigidbody2D component found on this GameObject.");
            return;
        }
        originalAngularDrag = rb.angularDrag;
    }

    void OnMouseDown()
    {
        isDragging = true;
        Vector3 mouseWorldPos = GetMouseWorldPosition();
        // 记录点击点相对于物体的本地位置,确保拖拽时始终对同一点施力
        localGrabPoint = transform.InverseTransformPoint(mouseWorldPos);
        // 拖拽时提升角阻力,抑制失控旋转
        rb.angularDrag = dragAngularResistance;
    }

    void OnMouseUp()
    {
        isDragging = false;
        rb.velocity = Vector2.ClampMagnitude(rb.velocity, maxLaunchVelocity);
        // 恢复原始角阻力
        rb.angularDrag = originalAngularDrag;
    }

    // 改用FixedUpdate处理物理逻辑,避免帧率波动导致力的施加不稳定
    void FixedUpdate()
    {
        if (!isDragging || rb == null) return;

        Vector3 mouseWorldPos = GetMouseWorldPosition();
        // 计算抓取点的目标世界位置
        Vector3 targetGrabWorldPos = mouseWorldPos;
        // 计算当前抓取点的世界位置
        Vector3 currentGrabWorldPos = transform.TransformPoint(localGrabPoint);

        // 基于抓取点的偏移计算力,而非物体整体位置
        Vector2 offset = (Vector2)(targetGrabWorldPos - currentGrabWorldPos);
        Vector2 force = offset * forceMag - rb.velocity * resistance;

        // 在抓取点施加力,让质心自然绕抓取点摆动
        rb.AddForceAtPosition(force, currentGrabWorldPos);

        // 可选:限制角速度,彻底避免过度旋转
        rb.angularVelocity = Mathf.Clamp(rb.angularVelocity, -8f, 8f);
    }

    private Vector3 GetMouseWorldPosition()
    {
        Vector3 mousePoint = Input.mousePosition;
        mousePoint.z = 10f;
        return mainCamera.ScreenToWorldPoint(mousePoint);
    }
}

质心脚本优化(支持动态更新)

using UnityEngine;

public class ChangeCenterOfMass : MonoBehaviour
{
    public Transform CenterOfMassReference;
    private Rigidbody2D rb;

    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
        UpdateCenterOfMass();
    }

    // 如果需要质心随参考子物体动态变化,取消下方注释
    // void Update()
    // {
    //     UpdateCenterOfMass();
    // }

    private void UpdateCenterOfMass()
    {
        if (rb != null && CenterOfMassReference != null)
        {
            Vector2 localCenter = transform.InverseTransformPoint(CenterOfMassReference.position);
            rb.centerOfMass = localCenter;
        }
    }
}

关键修改说明

  1. 改用FixedUpdate处理物理:Unity物理更新逻辑在FixedUpdate中执行,避免Update帧率波动导致力的施加不稳定。
  2. 跟踪本地抓取点:记录点击时相对于物体的本地坐标,确保拖拽过程中始终对同一个点施力,避免偏移计算错误。
  3. 临时调整角阻力:拖拽时提升刚体的角阻力,抑制失控旋转,拖拽结束后恢复原值。
  4. 基于抓取点计算力:直接计算抓取点当前位置与目标位置的偏移,施加指向目标的力,让质心自然绕抓取点摆动。
  5. 可选角速度限制:强制限制刚体的角速度范围,彻底解决旋转失控问题。

额外调试建议

  • 调整forceMag和resistance参数:根据物体质量(Rigidbody2D的mass属性)微调,质量越大的物体需要更大的forceMag。
  • 开启刚体Gizmos:在Scene视图中勾选Rigidbody2D组件的"Gizmos"选项,直观查看质心位置是否正确。
  • 临时关闭重力测试:如果物体出现失控跳跃,可先关闭gravityScale测试拖拽效果,再逐步调整参数。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 14:38:14