Drag and Drop in Unity

Recently I got a question to help solve some problem in Drag and Drop implementation in Unity. So as a bonus I decided to make my example of how to implement that! ?

Drag and Drop mechanic is often used in mobile games, but you can also encounter then in PC games. But thanks to Unity’s Event System this implementation will work fine on both platforms! ?

So let’s get started!

First of all, we need to create UI for our example! It will look like that:

UI setup example

Great! We are over the hardest part!  ?

Event System

As mentioned in the beginning, we are going to use Unity’s Event System to build our Drag and Drop mechanism.

There are two ways we can do it, event trigger way or event handlers interfaces. Before we go forward let me explain what both means.

Event Trigger way

Event System gives you a lot of events that you can use. You can access them for example through Event Trigger component where you have to select events that you are interested and assign functions to call when an event is triggered.

Event trigger component

Event Handler Interfaces way

You can also access all of those events through code and interfaces available by using UnityEngine.EventSystems.

These are the same events, but by using interfaces, you are already getting them as PointerEventData in oppose to BaseEventData when using EventTrigger component.

Implementation

Now it’s time to start coding! And because I don’t like to configure tons of things I’m going to use events through interfaces, but you can do either version.

We are going to need three events: BeginDrag, Drag, and EndDrag. Each will do different things.

BeginDrag – here we are going to initialize our drag process.
Drag – over here we are going to move our object around.
EndDrag – and here we are going to finish our drag process and place our object in a slot or in starting position.

Easy? I guess so. ?

We are going to need only two scripts here. The first one is the slot script which only holds a reference to the object.

using UnityEngine;

/// <summary>
/// Item slot class.
/// Store reference to the object inside slot.
/// </summary>
public class UIDropSlot : MonoBehaviour
{
    // Reference to the item inside slot.
    public UIDragItem currentItem;

    // Tells if slot is filled by other item.
    public bool SlotFilled => currentItem;
}

Yeah, it’s nothing crazy. ?

The second one is more complex because it has functions!

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

/// <summary>
/// Class responsible for dragging itself.
/// Using EventSystems interfaces.
/// </summary>
public class UIDragItem : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
    // Reference to current item slot.
    public UIDropSlot currentSlot;

    // Reference to the canvas.
    private Canvas canvas;
    // Reference to UI raycaster.
    private GraphicRaycaster graphicRaycaster;

    /// <summary>
    /// IBeginDragHandler
    /// Method called on drag begin.
    /// </summary>
    /// <param name="eventData">Event data.</param>
    public void OnBeginDrag(PointerEventData eventData)
    {
        // Start moving object from the beginning!
        transform.localPosition += new Vector3(eventData.delta.x, eventData.delta.y, 0) / transform.lossyScale.x; // Thanks to the canvas scaler we need to devide pointer delta by canvas scale to match pointer movement.

        // We need a few references from UI.
        if (!canvas)
        {
            canvas = GetComponentInParent<Canvas>();
            graphicRaycaster = canvas.GetComponent<GraphicRaycaster>();
        }

        // Change parent of our item to the canvas.
        transform.SetParent(canvas.transform, true);
        // And set it as last child to be rendered on top of UI.
        transform.SetAsLastSibling();
    }

    /// <summary>
    /// IDragHandler
    /// Method called on drag continuously.
    /// </summary>
    /// <param name="eventData">Event data.</param>
    public void OnDrag(PointerEventData eventData)
    {
        // Continue moving object around screen.
        transform.localPosition += new Vector3(eventData.delta.x, eventData.delta.y, 0) / transform.lossyScale.x; // Thanks to the canvas scaler we need to devide pointer delta by canvas scale to match pointer movement.
    }

    /// <summary>
    /// IEndDragHandler
    /// Method called on drag end.
    /// </summary>
    /// <param name="eventData">Event data.</param>
    public void OnEndDrag(PointerEventData eventData)
    {
        // On end we need to test if we can drop item into new slot.
        var results = new List<RaycastResult>();
        graphicRaycaster.Raycast(eventData, results);

        // Check all hits.
        foreach (var hit in results)
        {
            // If we found slot.
            var slot = hit.gameObject.GetComponent<UIDropSlot>();
            if (slot)
            {
                // We should check if we can place ourselves​ there.
                if (!slot.SlotFilled)
                {
                    // Swapping references.
                    currentSlot.currentItem = null;
                    currentSlot = slot;
                    currentSlot.currentItem = this;
                }

                // In either cases we should break check loop.
                break;
            }
        }

        // Changing parent back to slot.
        transform.SetParent(currentSlot.transform);
        // And centering item position.
        transform.localPosition = Vector3.zero;
    }
}

Events System calls all three functions here as they are implementing event handler interfaces. For me, this is much easier to do than assigning these events manually.

The only thing left to make it work is to assign references to items and slots, and that will be it! Let’s see the results!

Excellent, isn’t it? ?

If you want to take a lot into the whole example project, I’ve uploaded it to my public repository. ?

And if you want to expand this implementation by adding more control or events to it, you can find some useful information in posts about Events and Listeners, as well as MVC design pattern.

Good luck! If you enjoyed it, share it with your friends and see you next time! ?

4 10 votes
Article Rating
Subscribe
Notify of
guest
3 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Unity Lover
Unity Lover
3 years ago

Thanks for being so helpful!

BMW2000
BMW2000
2 years ago

Hi, thanks for this – it is great. How would I stop an object from being draggable once it is dropped on the dropzone? So for example, a player drags a card from their hand to the dropzone and then they can no longer move the card from the dropzone?

3
0
Would love your thoughts, please comment.x
()
x