Unity UI: Skip Count (Drag & Drop)

Ryan McCoach
10 min readJan 25, 2024

This article will cover how to drag and drop tiles into a specific slot. As the tiles are being dragged they will be appear on top of everything else and once dropped they will no longer be able to be dragged again.

SETUP

For this setup, it will require two separate groups of images with text. These images will act as either the draggable tile or the slot to drop the tile into.

My example has 100 tiles and slots, to help make the arrangement of these items easier created an empty game object that will be the parent object.

Attach a Grid Layout Group to this parent object. This will allow you to easily set the amount of rows or columns along with spacing and alignment.

Add the tiles or slots to grid group and rename each the number they will hold. This will be repetitive, but just make one grid groups then duplicate and rename it.

Once both the Tile and Slot groups are setup make sure to REMOVE the Grid Group Layout. This will allow us to make the Tiles draggable and constrained to their grid positin

Level Manager

Create an empty game object outside of the Canvas attach a new script to it. This script will control the flow of the level and it will do it by having the following variable:

  • Target Number (int) — this will hold the number we will be skip counting by.
  • Multiples List (int) — this be a list of all the numbers the user will skip count to.
  • Tile List (rect transform) — This list is only needed if the draggables tiles are to be shuffled. It will be a list of RectTransform instead of Transform since these are UI components & not game objects.

In Awake, you can assign the number you want to skip count by to the target number value. My example is getting this number from a pervious scene where the user pick a number from 2–12 and I created a bool that allows me to test this scene without going through that previous step. You can ignore the Level Selector & Test Mode and just assign the Target Number.

In creating the list of numbers the user will have to count to (Multiples List) we will use a For Loop that will iterate up to 100. Each time we iterate through the loop we check to see if the current index divided by the target number will produce a quotient with no remainder AND if the current index is not 0.

Checking to see if there is no remainder when dividing a number by the Target Number will be mean that current number is a multiple of the Target Number.

Again, if you want to shuffle the draggable Tiles you will need to populate the list by locking the Level Manager script (click on the Lock Icon), selecting all of the Tile images and adding them to the list in the Inspector.

I am not going to explain the Shuffle method in this article, but below is the code.

Since the Level Manager script has the list of multiples the user will need to count, we will need to create a way to remove the number from the list when the tile is successfully dropped on the slot.

This will need to be a public method, since it will be called from Slot script (later in the article). We remove the number that is at the first index or index 0 of the multiples list. Once that number is removed the list will automatically resize and all of the slots will move up.

Each time we remove a number, we check to see if the list is empty.

Lastly, the Drop Slots need to know what the current Target Number is as it will need to determine if the correct Tile is being placed.

This will be another Public Int method as it will return the current number that is at the first spot in the multiples list (or index 0).

Drag Script

Create a new script for the Tiles we are going to drag and attach it to all of those objects.

For this script we need access to the Unity Engine UI & Event Systems library. The Event Systems give us access to the I Drag Handlers which allows the detection of the mouse dragging an UI component.

The variables we need are:

  • Image (image) — this give use access to the image the script is on.
  • Original Position (vector 3) — this will hold the position of the Tile at the start of the program.

In Start, we grab the Image and assign the current position of the Image (at the start) to Original Position.

To make use of the IDragHandlers you will to need to right-click on each Hnadler > Quick Actions and Refactoring > Implement Interface. This will create the methods that will be called when those Events happen.

When the item first starts to drag, we are going to disable it as a raycast target so we can raycast target the Slots underneath the Tile when it comes time to drop it.

As the Tile is being Dragged, we want to set its position to the current position of the mouse. We can do this be using the eventData parameter, which stores a ton of information about the current action of the mouse, and we set the position of the Tile to the eventData position.

When the Tile is no longer being dragged (after it is dropped), we need to set the Tile to be a target for the mouse’s raycast to detect again. This will allow it to be draggable again.

Also, the Tile’s position will no longer be the mouse’s current position but it will be set back to the start position of the Tile.

Drop Script

Create a new script this will handle when the Tile is dropped on the Slot items. Again we are using the UI & Event System library, but this time we are using the IDropHandler.

Lets declare the following variables:

  • Drop Image (image) — this is the slots that we are dropping the tiles on and we need to get the name of the slot & position.
  • Target Number (int) — this is the current number tile the user has to find and drop into the matching slot.
  • Level Manager (DragDropLevelManager or what you called the Lever Manager script) — this will give us access to the Level Manager script for the target number.

In Start, we grab the Image component that the script is attached and access the Level Manager, so we can call the Get Target Number method.

This script should be attached to all of the Slot items and the Level Manager needs to be assigned to the script in the Inspector.

Right-Click on the IDropHandler to implement the interface using Quick Actions and Refactoring.

When Tile is dropped, we grab the current Target Number from the Level Manager then check to see if the Target Number (converted to a string) is the same as the name of the Tile the mouse was dragging.

If that condition is true then we will check if that same Tile being dragged has the same name as the Slot name. All this means is the user has the correct Tile and is dropping it in the correct Slot.

We have the correct Tile in the correct Slot, so now we need to set that Tile to the Slot. This is done by creating a temporary variable (droppedTile) and assign the Drag component using the eventData parameter. This will give us access to the Dragging script of the Tile that was dropped.

From there we can set the original position of that Tile to the Slot’s position. This means when the Tile is dropped it will no longer return to where it started from, but it now starts at the Slot.

Finally, we remove that number from the List through the Level Manager.

Clean Up

There are a few cosmetic things to address, such as the Tile is being dragged it appears behind the other tiles and once the tile is placed you can still drag it (even though it will return back to the dropped slot when released).

To make the drag tile appear above the rest, we will need to create another Canvas to place it on. Since UI elements are drawn based on their order in the Hierarchy i.e. the top tile in the hierarchy is in the back while the bottom tile is in the front, we the Tile starts to be drag we are going to move it to this Second Canvas which will be at the bottom of the hieracrchy.

Create a Canvas and GameObject variable type and give them a Serialized Field. Assign both the Drag Tile Holder GameObject and the Second Canvas to all of draggable Tile scripts.

Make sure the Second Canvas is below the Drag Tile Holder game object.

In the script, when the Tile starts to drag we are going to set the Tile’s parent to the Second Canvas and make World Position false (this prevents scaling issues) . When the Tile is finished being dragged, we set its parent back to the Drag Tile Holder.

You can see below that when the Tile begins to drag it moves from the Drag Tile Holder as its parent to the Second Canvas. Since the Second Canvas is at the below the Tile Holder, anything place in it will no be placed to the front.

The second piece of polish will be to prevent the Tile from being dragged once it is correctly dropped in its slot.

In the Drop Slot script, we are going to create a Coroutine that will take in a Drag class (whatever you named your Tile Dragging script) parameter. This will allow us to disable the draggable Tile after we wait a 1/10 of second.

We start this Coroutine in the On Drop method, where we check if it is the correct Tile being dropped in the correct slot and we pass in that Dropped Tile into the Coroutine parameter.

We need to wait a tick before disabling the Tile to give it a second to take the position of the Slot. If this was NOT done, the Tile will be place exactly where it was dropped and not snap into the slot. Then you would not be able to move it.

Now the Tile will be locked into the Slot and unable to be dragged out.

--

--