Drag and Drop in Unity Part One

I’m starting to experiment with Unity for a hobby project involving a card game.

In almost any card game, there are certain areas of the table for a collection of cards. Whether this is the vertical stack in Solitaire, the lands in Magic: The Gathering or a hidden hand of cards that aren’t ‘on’ the table, there are different areas that contain groups of cards. These cards will usually need to move between the card containers, so I figured the first thing to do would be to implement dragging and dropping. After watching a few video tutorials (the most useful was this one), I was ready to try some things out.

I started with a 2D game in Unity – card games are famously 2-dimensional. To this, I added a Panel and named it “Container”. Then I added a Panel inside the Container and named it “Item”. I made the Containers a little bigger than the Items so it was easier to see them, and duplicated this until there were 4.

I added a Grid Layout component to all of the “Container” objects and set the Child Alignment to ‘Middle Center’, then added a Constraint of ‘Fixed Row Count’ and the Constraint Count to 1 so that each container would only ever have one row of items.

Each “Item” needed a script – named DragHandler – to handle the dragging behaviour. The script describes how the object should behave when being dragged around, including how to begin dragging it in the first place. To do this it needs to reference the UnityEngine.EventSystems namespace, and implement the three interfaces IBeginDragHandler, IDragHandler and IEndDragHandler.

First of all, it needs some properties and fields set up.

public static GameObject ItemBeingDragged;
private Vector2 _startPosition;
private Transform _startParent;

This is almost verbatim from the tutorial video, and I will probably be refactoring that static GameObject field at some point in the future (it ought to be a property if it’s public).

public void OnBeginDrag(PointerEventData eventData)
{     ItemBeingDragged = gameObject;     _startPosition = transform.position;     _startParent = transform.parent;     GetComponent<CanvasGroup>().blocksRaycasts = false;
}

OnBeginDrag starts the process off by setting the ItemBeingDragged to the current GameObject, saving the start position (so it can be returned later if the drop is unsuccessful) and saving the original parent (same reason). Finally it finds the CanvasGroup component and stops blocking raycasts so that events can be fired through the item being dragged (this will be needed to drop it).

public void OnDrag(PointerEventData eventData)
{     transform.position = Input.mousePosition;
}

OnDrag just makes the dragged item follow the mouse cursor until it is dropped.

public void OnEndDrag(PointerEventData eventData)
{     ItemBeingDragged = null;     if (_startParent == transform.parent)     {         transform.position = _startPosition;     }     GetComponent<CanvasGroup>().blocksRaycasts = true; 
}

Finally, when the drag is over, the ItemBeingDragged is forgotten (set to null), and if the parent item at the end is the same as the beginning it is returned to it’s starting position. Finally, raycast blocking is restored so it can pick up further events.

The Container objects need a script too, which will also need to reference UnityEngine.EventSystems and implement an interface. This time, IDropHandler so we can drop items there.

public void OnDrop(PointerEventData eventData)
{     DragHandler.ItemBeingDragged.transform.SetParent(transform);
}

This is a simple script. It references the static ItemBeingDragged property from the DragHandler script, and sets its parent to whatever the drop target is.

With all this set up, I can drag Item panels from one Container to another. Or move a card from one location to another, eventually.

Join the conversation

1 Comment

Leave a comment

Your email address will not be published. Required fields are marked *