OnDrop called on wrong GameObject

I am implementing a drag and drop inventory interface - I have a UI Panel with an Inventory script on it, which instantiates an appropriate number of InventorySlot prefabs on Start(). The prefabs consist of a UI Image with an InventorySlot script and a InventoryDropHandler attached, and a child UI Image with a InventoryDragHandler attached. The idea is that the child image has the item icon on it, can be dragged to other slots and the parent InventorySlot object will handle the drop.

I’ve been using this tutorial as a basis: Unity UI Drag and Drop Tutorial - YouTube - the only thing I’m doing slightly differently is that I’m not changing the parent of the child item icon object to the new InventorySlot, instead I am assigning the internal GameObject which represents my actual item to the new slot, setting the child icon based on that, and resetting the original item icon back to it’s previous state.

My problem is that after moving from one slot to the other (which works as expected), if I try to move back to the original slot (or any other slot that has held an item), the OnDrop function gets called on whichever InventorySlot the item is currently in, rather than the destination. I can’t see anyway this is happening so I’m baffled.

Any help would be much appreciated - here are the appropriate scripts:

Inventory.cs (on the parent panel)

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Inventory : MonoBehaviour {

	public int slotSize = 32;
	public int offset = 5;
	public List<InventorySlot> slots = new List<InventorySlot>();
	public GameObject slotPrefab;

	public static GameObject draggingItem;
	public static bool isDragging = false;

	// Use this for initialization
	void Start () {
		// create an appropriate number of slots for the size of the panel
		RectTransform panelRect = GetComponent<RectTransform> ();
		int columns = (int)Mathf.Floor (panelRect.rect.width / (slotSize + offset));
		int rows = (int)Mathf.Floor (panelRect.rect.height / (slotSize + offset));

		for (int x = 0; x < columns; x++) {
			for (int y = 0; y < rows; y++)
			{
				GameObject newSlot = Instantiate(slotPrefab);
				newSlot.transform.SetParent(this.transform);
				slots.Add (newSlot.GetComponent<InventorySlot>());
			}
		}
	}

	public void AddItemToInventory(GameObject item)
	{
		if (item.GetComponent<InventoryItem> () != null) {
			foreach (InventorySlot slot in slots)
			{
				if (slot.itemInSlot == null)
				{
					slot.PutItemInSlot(item);
					break;
				}
			}
		}
	}
}

InventorySlot.cs (on the parent Image in the prefab)

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

public class InventorySlot : MonoBehaviour  {

	public GameObject itemInSlot;
	protected Image itemIcon;

	// Use this for initialization
	void Start () {
		itemIcon = this.transform.Find ("ItemIcon").GetComponent<Image> ();
	}
	
	public void ResetImagePosition()
	{
		transform.Find ("ItemIcon").position = Vector3.zero;
		transform.Find ("ItemIcon").gameObject.GetComponent<CanvasGroup> ().blocksRaycasts = true;
	}

	public void PutItemInSlot(GameObject obj)
	{
		InventoryItem item = obj.GetComponent<InventoryItem> ();
		if (!item) {
			return;
		}
		obj.SetActive (false);
		itemInSlot = obj;
		itemIcon.gameObject.SetActive (true);
		itemIcon.sprite = item.icon;
		if (item.currentSlot != null) {
			item.currentSlot.RemoveItemFromSlot();
		}
		item.currentSlot = this;
	}

	public void RemoveItemFromSlot()
	{
		itemInSlot = null;
		itemIcon.gameObject.SetActive (false);
		itemIcon.sprite = null;
	}
}

InventoryDragHandler.cs (on the child Image in the prefab)

using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;

public class InventoryDragHandler : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {

	protected Vector3 startPosition;

	#region IBeginDragHandler implementation
	public void OnBeginDrag (PointerEventData eventData)
	{
		Inventory.draggingItem = GetComponentInParent<InventorySlot> ().itemInSlot;
		Inventory.isDragging = true;
		GetComponent<CanvasGroup> ().blocksRaycasts = false;
		startPosition = transform.position;
	}
	#endregion
	
	#region IDragHandler implementation
	public void OnDrag (PointerEventData eventData)
	{
		transform.position = Input.mousePosition;
	}
	#endregion

	#region IEndDragHandler implementation

	public void OnEndDrag (PointerEventData eventData)
	{
		transform.position = startPosition;
		Inventory.isDragging = false;
		Inventory.draggingItem = null;
	}

	#endregion
}

InventoryDragHandler.cs (on the parent Image in the prefab)

using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;

public class InventoryDropHandler : MonoBehaviour, IDropHandler {


	public InventorySlot slot;
	void Start()
	{
		slot = GetComponent<InventorySlot> ();
		//Debug.Log ("Started drop handler", slot);
	}

	#region IDropHandler implementation

	public void OnDrop (PointerEventData eventData)
	{
		Debug.Log ("Dropped", slot);
		if (slot.itemInSlot == null) {
			Inventory.draggingItem.GetComponent<InventoryItem>().currentSlot.ResetImagePosition();
			slot.PutItemInSlot(Inventory.draggingItem);
		}
	}

	#endregion


}

InventoryItem.cs (this goes on any GameObject I want to be able to place in an inventory

using UnityEngine;
using System.Collections;

public class InventoryItem : MonoBehaviour {

	public Sprite icon;

	public InventorySlot currentSlot;

}

I also used same tutorial and had same problem. Then I started experimenting with Canvas/Canvas component. I changed “render mode” to three ooptions, and concluded that “screen space - overlay” works best. also, see that there is “PIXEL PERFECT” chaeckbox? I just checked it and it seems like this Drag-and-drop Inventory is now working perfectly. COOL!!! :wink: