unity-game-enginegame-physics

Click and drag in Unity - maintain offset


The following code works to click and drag an object:

    private void MousePressed(InputAction.CallbackContext context)
    {
        Vector2 mousePosition = Input.mousePosition;

        Vector3 worldPosition = mainCamera.ScreenToWorldPoint(new Vector3(mousePosition.x, mousePosition.y, -mainCamera.transform.position.z));
        worldPosition.z = 0;    

        Collider2D hitCollider = Physics2D.OverlapPoint(worldPosition);

        if (hitCollider != null)
        {
            StartCoroutine(DragUpdate(hitCollider.gameObject));
        }
    }

    private IEnumerator DragUpdate(GameObject clickedObject)
    {
        float initialDistance = Vector3.Distance(clickedObject.transform.position, mainCamera.transform.position);
        clickedObject.TryGetComponent<Rigidbody2D>(out var rb);
        while (mouseClick.ReadValue<float>() != 0) {
            Ray ray = mainCamera.ScreenPointToRay(Mouse.current.position.ReadValue());

            if (rb != null) {
                Vector3 direction = ray.GetPoint(initialDistance) - clickedObject.transform.position;
                rb.velocity = direction * mouseDragPhysicsSpeed;
                yield return waitForFixedUpdate;
            } else {
                clickedObject.transform.position = Vector2.SmoothDamp(clickedObject.transform.position, ray.GetPoint(initialDistance), ref velocity, mouseDragSpeed);
                yield return null;
            }
        }
    }

The problem is that the object first moves so that its center is on the cursor. How can I keep the initial offset so that the cursor remains in the same location relative to the object as when it first started dragging?

I'm open to any other suggestions to improving this code - I'm new to Unity/C#. Thanks!


Solution

  • To keep the cursor's initial offset relative to the object during dragging, just make the following changes:

    1- In MousePressed, capture the initial offset and pass it to the coroutine:

    StartCoroutine(DragUpdate(hitCollider.gameObject, worldPosition - hitCollider.transform.position));
    

    2- Update the DragUpdate signature to accept the offset parameter:

    private IEnumerator DragUpdate(GameObject clickedObject, Vector3 initialOffset)
    

    3- Apply the offset when calculating targetPosition in DragUpdate:

    Vector3 targetPosition = ray.GetPoint(initialDistance) - initialOffset;
    

    These changes will maintain the cursor's initial position relative to the object.