Drag Object Script Problem

So i was making a script that would allow me to click and drag a rigidbody with the mouse, and for the most part it was working, but a minute problem i had with it was that that when i clicked on it the object snapped to my cursor, because in the script it just goes to the mouse position, but i wanted it to drag it around by the point on the object it clicked. I was hoping somebody could help by telling me how i could do that. Feel free to ask about anything you don’t understand. Thank You.

My script:

public class Drag : MonoBehaviour {
	float d;

	public Rigidbody2D rigid;

	public bool held;

	void Update (){
		d = Input.GetAxis("Mouse ScrollWheel");
		if(held == true && d > 0f){
			transform.Rotate(Vector3.forward * d * 7);
		}else if(held == true && d < 0f){
			transform.Rotate(Vector3.forward / d / 7);
		}
	}

	void OnMouseDrag (){
		Vector2 mousePosition = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
		Vector2 objPosition = Camera.main.ScreenToWorldPoint(mousePosition);

		transform.position = objPosition;
		rigid.constraints = RigidbodyConstraints2D.FreezePositionX | RigidbodyConstraints2D.FreezePositionY;
		held = true;
	}

	void OnMouseUp(){
		rigid.constraints = RigidbodyConstraints2D.None;
	}
}

Probably the simplest way to achieve something along these lines would be:

  • When the mouse is first clicked, calculate a 3D position the user clicked on in world space (presumably at z=0, as that’s what you’re current assuming here by converting ScreenToWorldPoint to a 2D vector
  • Use transform.InverseTransformPoint to get the ‘local space’ position that corresponds to, and store it
  • Each frame while dragging, convert the stored ‘local space’ position back to world space using transform.TransformPoint
  • Calculate the difference between the current mouse position and that new position
  • Add the difference to transform.position

What we’re effectively doing with that process is storing ‘where on the object’ the user originally clicked. Then each frame we’re taking that point on the object, and working out ‘where it is now’. Finally, we’re comparing that position to the current mouse position and working out the error, which we’re correcting by changing the position of the object.

Very roughly, it’d look like this:

        public class Drag : MonoBehaviour 
        {
            float d;
 
            public Rigidbody2D rigid;
 
            public bool held;           //stores whether currently holding the object
            Vector3 localHeldPosition;  //stores where we grabbed it
 
            void Update ()
            {
                d = Input.GetAxis("Mouse ScrollWheel");
                if(held == true && d > 0f){
                    transform.Rotate(Vector3.forward * d * 7);
                }else if(held == true && d < 0f){
                    transform.Rotate(Vector3.forward / d / 7);
                }
            }
 
            void OnMouseDrag ()
            {
                //get mouse position
                Vector2 screenMousePosition = new Vector2(Input.mousePosition.x, Input.mousePosition.y);

                //convert to world space (on z=0 plane)
                Vector3 worldMousePosition = Camera.main.ScreenToWorldPoint(screenMousePosition);
                worldMousePosition.z = 0; //this is to make sure the position stays on the z=0 plane

                //if weren't holding last frame, remember where we grabbed it in local space
                if(!held)
                {
                    localHeldPosition = transform.InverseTransformPoint(worldMousePosition);
                    held = true;
                }

                //calculate the current position of the grabbed point in world space
                Vector3 worldHeldPosition = transform.TransformPoint(localHeldPosition);

                //calculate the 'error' - i.e. how much we need to move that point to fix things
                Vector3 worldDelta = worldMousePosition-worldHeldPosition;
                 
                //fix it just by moving out object
                transform.position += worldDelta;
                rigid.constraints = RigidbodyConstraints2D.FreezePositionX | RigidbodyConstraints2D.FreezePositionY;
                held = true;
            }
 
            void OnMouseUp()
            {
                rigid.constraints = RigidbodyConstraints2D.None;
                held = false;
            }
        }

But there’s definitely work to do:

  • I’m not sure how your scene is setup, but unless you’re using an orthographc camera and all your objects are on the z=0 plain, there’s likely to be issues with conversion from screen to world space
  • Moving rigid bodies by directly setting the transform.position isn’t good - you should look into how to correctly move rigid bodies around (look up MovePosition)
  • This will move the object, but won’t give an actual ‘drag’ effect - the object won’t rotate towards the cursor. If you want that behaviour you’ll need to look into point forces.

Hope that helps get you started :slight_smile:

-Chris

I figured out the best way to move the object for anyone who stumbles upon this thread wondering how to do this.

So here’s the script i ended up with:

   float d;

	public Rigidbody2D rigid;

	public bool held;           //stores whether currently holding the object
	Vector3 localHeldPosition;  //stores where we grabbed it

	void Update ()
	{
		d = Input.GetAxis("Mouse ScrollWheel");
		if(held == true && d > 0f){
			transform.Rotate(Vector3.forward * d * 7);
		}else if(held == true && d < 0f){
			transform.Rotate(Vector3.forward / d / 7);
		}
	}

	void OnMouseDrag ()
	{
		//get mouse position
		Vector2 screenMousePosition = new Vector2(Input.mousePosition.x, Input.mousePosition.y);

		//convert to world space (on z=0 plane)
		Vector3 worldMousePosition = Camera.main.ScreenToWorldPoint(screenMousePosition);
		worldMousePosition.z = 0; //this is to make sure the position stays on the z=0 plane

		//if weren't holding last frame, remember where we grabbed it in local space
		if(!held)
		{
			localHeldPosition = transform.InverseTransformPoint(worldMousePosition);
			held = true;
		}

		//calculate the current position of the grabbed point in world space
		Vector3 worldHeldPosition = transform.TransformPoint(localHeldPosition);

		//calculate the 'error' - i.e. how much we need to move that point to fix things
		Vector3 worldDelta = worldMousePosition-worldHeldPosition;

		//fix it just by moving out object
		rigid.MovePosition(transform.position + worldDelta);
		held = true;
	}

	void OnMouseUp()
	{
		rigid.constraints = RigidbodyConstraints2D.None;
		held = false;
	}