Grid Inventory : Move Item & Slot Highlighting

Hi !

I’m working on an inventory system with large items (Diablo/Men of War/Commandos/“insertnameofagamehere”-style) for an RPG, but I can’t find a proper way to move an item from one group of slot to another.

At first I was seeking for a way to get the UI elements that were under another UI element (dragging the item, get the hovered slots, check if they are occupied, red highlight them if they are, green highlight if they aren’t, etc), but I didn’t find a way to do that.

Of course, I tried to use the Event Handlers interfaces (and using the PointerEventData too). Sadly it doesn’t behave exactly like I wanted because, for instance, PointerEventData.hovered returns all the UI elements under the Pointer, not the ones under the InventoryItem that I am dragging.

Raycasts could do it but I think it is a bit “overkill” to use raycasts between UI elements just for that, I’m sure there is a more performance-friendly way to do it.

I also tried colliders but the OnCollider/TriggerEnter/Exit don’t seem to work on UI, or maybe I got it wrong.

I roamed Unity Answers and Youtube to find a way to do it but ain’t find what I was looking for. If you got an idea or a tutorial that I missed that could do, please share it with me. I hope the post is clear enough so any can understand the problem here. Thank you by advance.

Here are the concerned scripts at the moment so everyone can understand where I am standing at the moment :

InventoryItem.cs

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

public class InventoryItem : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IBeginDragHandler, IDragHandler, IEndDragHandler
{
	[SerializeField] Image image;
	[SerializeField] Animator animator;

	Item item;
	List<InventorySlot> containingSlots = new List<InventorySlot>();
	List<InventorySlot> hoveredSlots = new List<InventorySlot>();
	Inventory containingInventory;



	public void Initialize(Item newItem, List<InventorySlot> newContainingSlots, Inventory newContainingInventory)
	{
		item = newItem;
		containingSlots = newContainingSlots;
		containingInventory = newContainingInventory;

		name = item.name;
		GetComponent<RectTransform>().sizeDelta = new Vector2(50 * item.width, 50 * item.height);
		GetComponent<RectTransform>().anchoredPosition = containingSlots[0].GetComponent<RectTransform>().anchoredPosition;
		image.sprite = item.icon;
	}



	void IPointerEnterHandler.OnPointerEnter (PointerEventData eventData)
	{
		animator.SetBool("Highlighted", true);
	}



	public void OnPointerExit (PointerEventData eventData)
	{
		animator.SetBool("Highlighted", false);
	}



	public void OnBeginDrag(PointerEventData eventData)
	{
		// This allows to set the contaning slots to be set to innocuppied when starting to drag the item
		containingInventory.RemoveItemAtSlot(containingSlots[0].slot);
	}



	public void OnDrag(PointerEventData eventdata)
	{
		// Some elegant logic to make the InventoryItem follow the pointer smoothly
		transform.position = new Vector2(transform.position.x + eventdata.delta.x, transform.position.y + eventdata.delta.y);

		// Some logic to get the slots that are hovered by the InventoryItem, which is our issue here that would look like this
		/*
		hoveredSlots.Clear();
		
		for(int i = 0; i < SomeEventData.hovered.Count; i++)
		{
			if(SomeEventData.hovered*.GetComponent<InventorySlot>() != null)*

_ hoveredSlots.Add(SomeEventData.hovered*.GetComponent();_
_
}_
_ /_
_
}
_

* public void OnEndDrag(PointerEventData eventData)*
* {*
* CheckForRoom();*

* containingInventory.AddItemAtSlot(item, containingSlots[0]);*
* }*

* void CheckForRoom()*
* {*
* if(hoveredSlots[0].slot.containedItem == null)*
* {*
* for(int i = 0; i < hoveredSlots.Count; i++)*
* {*
* if(hoveredSlots[0].slot.occupied)*
* return;*
* }*
* }*

* containingSlots = hoveredSlots;*
* }*
}

This system you want is achievable with the UI events. These can be accessed by implementing the correct interface for the event or by also using the EventTrigger component (you can see all the event interfaces on this page as well) and joining a method to the event just like you buttons. I believe extending this class gives access to all events at once.

But instead of trying to explain an example on how to use them you should look at the examples in this UI example pack by Unity. One of the examples is a drag and drop system working. I would re purpose this to work for your inventory (which i have done in the past).

Hope this helps, typed this on my phone soo apologies for any mistakes!

In the end I preferred this way :

On dragging, the InventoryItem that is dragged is snapped on the pointer. On drop, the slot that is under the pointer is checked to see if it isn’t containing an item, and check the x slots on the right and the y slots under to make sure they are innocupied (x is the width of the item, y is its height). If it is the case, the InventoryItem takes place in the slot that is under the pointer and occupies the others. If it isn’t, the InventoryItem naturally retakes place on the original slot and reoccupies the slots that are required.