Unity: Drag Objects With A Mouse

Developing Pixel Pub required learning how to make objects in my game click and draggable.

If you’re developing mobile game, I figured this was essential enough that it deserved a write up on how to do it.

In this post I’m going to explain quickly how to make objects draggable while also giving a deeper understanding into what’s going on under the hood of some function calls.

If you just want the code, you can just copy and paste it from the bottom.

Dragging with a Mouse

There are many different approaches to this subject, but I found this way to be the most accurate and responsive.

You’ll want to create a standard Unity script that inherits from MonoBehaviour. (Unity should do this by default when you create the script)

The reason you want to do this is because we’re going to be using some built in functionality that comes with MonoBehaviour.

The two functions that we’re going to use are OnMouseDown( ) and OnMouseUp( ).

OnMouseDown( ) : This detects when a user has pressed the mouse button over a Collider. Why did I bold that? Because! This means if you don’t attach a Box Collider onto your object, then Unity won’t be able to detect the object being clicked on.

OnMouseDrag( ) : This is called when the user has clicked on a Collider (don’t forget to add this thing!) and is still holding it down. It will continue to call this function for every frame that the mouse is down.

Cool. Now we know the two basic functions for having fun with clicking and dragging.

Code Bits for Dragging with a Mouse

Here comes the code.

Variables and Start()

public class DragMouseMove : MonoBehaviour
{
    //This is where we store the Plane for dragging objects
    private Plane daggingPlane;
    
    //This will store the difference between where the mouse is clicked
    //on the Plane and where the origin of the object is
    private Vector3 offset;

    //This will be used to cache to main camera
    //You could also use a serialized field to accomplish the same thing
    private Camera mainCamera;

Starting off we have four variables.

daggingPlane – This will hold the plane in which we’re dragging objects across.

offset – This will be used for when we click on the object. Players aren’t going to click directly on the object’s center origin every time so we need to determine the difference between the StartingPosition and where they clicked (the Offset).

mainCamera – Here we’re going to store the Main Camera for later. It will be used to help construct the Plane.

    void Start()
    {
        // Cache the camera at the start. 
        mainCamera = Camera.main; 
    }

In the Start( ) functions, we’re caching the main camera for later use as well as the starting position of the Object that the Script is on.

OnMouseDown()

    void OnMouseDown()
    {
            daggingPlane = new Plane(mainCamera.transform.forward, 
                                  transform.position);
            Ray camRay = mainCamera.ScreenPointToRay(Input.mousePosition);

            float planeDistance;
            daggingPlane.Raycast(camRay, out planeDistance);
            offset = transform.position - camRay.GetPoint(planeDistance);
    }

This is where things get interesting.

The daggingPlane is created using the Plane constructor. A Plane is used to draw out this theoretical plane to do mathematical calculations on. It doesn’t exist as an actual Object in the game.

You create one by giving it a facing direction and Vector3 points. In this example we give it the forward direction of the camera and the Vector3 of the Object we are trying to click.

Next we create a Ray from our mainCamera based on where the User clicks. This is essentially like shooting a beam from where the User clicks out into the game world and through our Object.

We then make a variable to store the distance of the Ray to our Object called planeDistance.

daggingPlane.Raycast(camRay, out planeDistance) is used to then populate our variable planeDistance with the distance of the Ray if it determines that the click did hit the Object.

camRay.GetPoint(planeDistance) will calculate the point at the distance units along the ray. In this case it’s the distance calculated from by RayCast. Subtracting that from the objects origin position and we get the offset!

What Everything Above Looks Like In Action!

OnMouseDrag()

Don’t worry. This one is easier.

    void OnMouseDrag()
    {
            Ray camRay = mainCamera.ScreenPointToRay(Input.mousePosition);
            float planeDistance;
            daggingPlane.Raycast(camRay, out planeDistance);
            transform.position = camRay.GetPoint(planeDistance) + offset;
    }

Starting off again we store the mouse position on the screen while the User is dragging the mouse.

We perform the same ray distance function that we did previously to store in planeDistance.

Finally, we update the position of our object by taking getting the point from the camRay and adding the offset we calculated in OnMouseDown().

Wrapping Up

With that, you have the basic functionality to click on an object and move it around. I’ll post the complete code below so you can copy and paste it.

Hope it helps! Happy coding.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DragMouseMove : MonoBehaviour
{

    private Plane daggingPlane;
    private Vector3 offset;

    private Camera mainCamera;

    void Start()
    {
        mainCamera = Camera.main;
    }

    void OnMouseDown()
    {
            daggingPlane = new Plane(mainCamera.transform.forward, 
                                  transform.position);
            Ray camRay = mainCamera.ScreenPointToRay(Input.mousePosition);
            Debug.DrawRay(camRay.origin, camRay.direction *10, Color.green);

            float planeDistance;
            daggingPlane.Raycast(camRay, out planeDistance);
            offset = transform.position - camRay.GetPoint(planeDistance);
    }
    void OnMouseDrag()
    {
            Ray camRay = mainCamera.ScreenPointToRay(Input.mousePosition);
            float planeDistance;
            daggingPlane.Raycast(camRay, out planeDistance);
            transform.position = camRay.GetPoint(planeDistance) + offset;
    }
}