Unity UI: Duplicating Items
This article will cover how to duplicate UI items when dragged, detecting different panels when the drag ends to either parent or delete items and controlling the amount of duplicates.
In this article I have two game objects called Group_Tile_Holder & Group_Tile_Prefab that may be confusing. I was building this for an application and forgot to change the names to something more understandable for this article. If there is any confusion replace the names with these to make it more clear.
- Group_Tile_Holder = Draggable_Item_Holder
- Group_Tile_Prefab = Draggable_Item_Prefab.
SETUP
The Build_Level_Manager is an empty gameobject that will hold the script that controls most of the logic.
Create a Canvas which will automatic create an EventSystem. Make sure that EventSystem is there or being able to drag the items will not be possible.
Create a two UI Panels (Draggable_Panel & Build_Panel) in the Canvas. UI panels are UI Images that uses Unity’s Backgroup Sprite.
Create a UI Image (Group_Tile_Holder) in the Draggable_Panel.
Resize the panel’s however you like, but the Draggable_Panel will hold the the items we are dragging/duplication and the Build_Panel is where we are placing those items.
The Group_Tile_Holder should be a child of the Draggable_Panel and place somewhere on it. This is where the items will be spawn and duplicated from.
Add a Grid Layout Group component to the Build_Panel to easily organize the items when they are dropped in the Build_Panel.
Lastly, duplicated the Group_Tile_Holder change the image color and make it a Prefab. This will be the item that is being dragged and duplicated. Delete it from your scene once you have made it a prefab.
Build Level Manager Script
Create a script (BuildLevelManager) and attach it to the Build_Level_Manager game object.
Declare the following variables with Serialized Fields.
- _buildPanel (RectTransform)
- _draggablePanel (RectTransform)
- _groupTileHolder (RectTransform)
- _groupTilePrefab (Image)
Make the BuildLevelManager a Singleton, so it can be accessed by any script and we can call methods from it.
In order to call methods from the BuildLevelManager Singleton, make sure to make the methods public. This method will get and return RectTransform of the Group_Tile_Holder. This will allow us to set the position of the duplicated item when they are created.
We are going to create a method that will handle creating another draggable item.
This requires us to Instantiate (spawn) the Group_Tile_Prefab and we will assign it to a local variable (groupTile). Using that local variable (groupTile), we can access its RectTransform to set its parent to the Group_Tile_Holder. Lastly, we set the position of this new Group_Tile_Prefab to the position of the Group_Tile_Holder.
In Start, we will call this method twice to create two Group_Tile_Prefabs.
To help clean up the duplicates and an unlimited amount, we can set a limit by getting a count of them, checking if the count is above a certain number and destroying the last one created.
We get the count of duplicates by accessing the parent object that is holding all of the duplicated (Group_Tile_Holder) and calling childCount. This will return an int of how many children objects the parent has starting at 1.
If this count goes above a limit (3), we will Destroy that numbered child object.
This last method, will check to see how many item are currently in the Build_Panel and if it goes above a certain amount we will destroy one of them to keep it at that desired amount.
Again, we are getting the childCount of the Build_Panel and storing it in a local variable (itemAmount). If this itemAmount is greater than the desired amount then will grab the numbered child and destroy it.
DRAGGABLE ITEMS SCRIPT
Create a script (DragnDrop) and attach it to the Group_Tile_Prefab.
For the draggable script, we will need access to Unity Engine’s UI & Event System’s library.
The Event System library allows us to use these interfaces that handles when an object starts being dragging, while its still dragging and when it stops being dragged.
Right-click on each handler and use the Quick Actions & Refactorings to create each of these methods.
We only need one variable which will be…
- _itemBeingDragged (Image).
In Start, we want to get the Image component of the Group_Tile_Prefab and assign it to _itemBeingDragged.
When the item starts to be dragged, we went to turn off this item being detected by the mouse’s raycast. This allows us to detect what object the mouse is currently touching under the item being dragged.
While the item is being dragged, we are going to the item’s poisiton to the mouse pointers current position using the eventData.
When the item stops being dragged we are going to grab the object behind the Group_Tile_Prefab being dragged, which will either be the Draggle_Panel, Build_Panel or another Group_Tile, and store it in a local variable (obj).
Then we are going get the RectTransform of that object using the local variable (obj) and store it in another local variable (objPos)/
Then, we use obj to access the name of the object the mouse is current on and create conditions depending on the object.
If the object is the Draggable_Panel we will destroy the item.
If it is the Build_Panel, we will parent the Group_Tile_Prefab to the Build_Panel by passing in the objPos variable.
Then call the CheckBuildItemAmout method from the BuildLevelManager script.
The final check will use Else and check it the object is another Group_Tile_Prefab. If that is the case we will destroy the item being dragged.
After conditions, we are going to turn on this item’s ability to be detected by the mouse’s raycast so it can be dragged again and call the CreateNewGroupTile method from the BuildLevelManager script to create a new draggable item.
Make sure the Draggable_Panel, Build_Panel and Group_Tile_Prefab all have the Raycast Target turned on. This will ensure it can be detected by the mouse pointer.
Let me know what you think about this article in the comments or if you have another way of creating this functionality. Cheers and keep coding!