Search Unity

Inventory drag and drop code

Discussion in 'Scripting' started by Phobiegames, Feb 18, 2018.

  1. Phobiegames

    Phobiegames

    Joined:
    Apr 3, 2017
    Posts:
    113
    Okay guys, this is a rather complex question so ill do my best to write it out as clear as I can. I have two scripts, on goes on the inventory slot which is this code here:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.EventSystems;
    5.  
    6. public class ItemDropHandler : MonoBehaviour, IDropHandler {
    7.  
    8.     public void OnDrop(PointerEventData eventData)
    9.     {
    10.         RectTransform invPanel = transform as RectTransform;
    11.  
    12.         if(!RectTransformUtility.RectangleContainsScreenPoint(invPanel, Input.mousePosition))
    13.         {
    14.             if (invPanel.CompareTag("Slot"))
    15.             {
    16.                 Debug.Log("Drop item");
    17.             }
    18.         }
    19.     }
    20. }
    which I wanna use to swap out an inventory item in the slot, which I started to psuedo code out to understand it better. Which i know i need to check if another item is in the other slot, but my inventory system uses scriptableobjects for each items data.

    here is the actual dragging code:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.EventSystems;
    5.  
    6. public class ItemDragHandler : MonoBehaviour, IDragHandler, IEndDragHandler {
    7.  
    8.     public void OnDrag(PointerEventData eventData)
    9.     {
    10.         transform.position = Input.mousePosition;
    11.     }
    12.  
    13.     public void OnEndDrag(PointerEventData eventData)
    14.     {
    15.         transform.localPosition = Vector3.zero;
    16.     }
    17. }
    which works fine for dragging the item icon out of the slot and snapping back to the origin.

    and finally here are the scripts that control the actual inventory ui and inventory list:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3.  
    4. public class InventorySlot : MonoBehaviour {
    5.  
    6.     public Image icon;
    7.     public Button removeButton;
    8.  
    9.     Item item;
    10.  
    11.     public void AddItem(Item newItem)
    12.     {
    13.         item = newItem;
    14.  
    15.         icon.sprite = item.itemSprite;
    16.         icon.enabled = true;
    17.         removeButton.interactable = true;
    18.     }
    19.  
    20.     public void ClearSlot()
    21.     {
    22.         item = null;
    23.  
    24.         icon.sprite = null;
    25.         icon.enabled = false;
    26.         removeButton.interactable = false;
    27.     }
    28.  
    29.     public void OnRemoveButton()
    30.     {
    31.         InventorySystem.instance.SubtractItem(item);
    32.     }
    33. }
    Inventory UI:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class InventoryUI : MonoBehaviour {
    6.  
    7.     public Transform itemsParent;
    8.     public GameObject inventoryPanel;
    9.  
    10.     private Player player;
    11.  
    12.     InventorySystem inventory;
    13.  
    14.     InventorySlot[] slots;
    15.  
    16.     // Use this for initialization
    17.     void Start () {
    18.         player = FindObjectOfType<Player>();
    19.         inventory = InventorySystem.instance;
    20.         inventory.onItemChangedCallback += UpdateUI;
    21.  
    22.         slots = itemsParent.GetComponentsInChildren<InventorySlot>();
    23.     }
    24.    
    25.     // Update is called once per frame
    26.     void Update () {
    27.         if (Input.GetKeyDown(KeyCode.Tab))
    28.         {
    29.             inventoryPanel.SetActive(!inventoryPanel.activeSelf);
    30.             player.enabled = !player.enabled;
    31.         }
    32.     }
    33.  
    34.     void UpdateUI()
    35.     {
    36.         for (int i = 0; i < slots.Length; i++)
    37.         {
    38.             if(i < inventory.items.Count)
    39.             {
    40.                 slots[i].AddItem(inventory.items[i]);
    41.             }
    42.             else
    43.             {
    44.                 slots[i].ClearSlot();
    45.             }
    46.         }
    47.     }
    48. }
    49.  
    Inventory Manager:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class InventorySystem : MonoBehaviour {
    6.  
    7.     public List<Item> items = new List<Item>();
    8.  
    9.     public int storageCount = 20;
    10.  
    11.     public static InventorySystem instance;
    12.  
    13.     public delegate void OnItemChanged();
    14.     public OnItemChanged onItemChangedCallback;
    15.  
    16.  
    17.     private void Awake()
    18.     {
    19.         if(instance != null)
    20.         {
    21.             Debug.LogWarning("More than one instance of object");
    22.             return;
    23.         }
    24.  
    25.         instance = this;
    26.     }
    27.  
    28.  
    29.     public bool AddItem(Item item)
    30.     {
    31.         if(items.Count >= storageCount)
    32.         {
    33.             Debug.Log("Out of Storage!!");
    34.             return false;
    35.         }
    36.         else
    37.         {
    38.             items.Add(item);
    39.             if(onItemChangedCallback != null)
    40.             {
    41.                 onItemChangedCallback.Invoke();
    42.             }
    43.             return true;
    44.         }
    45.     }
    46.  
    47.     public void SubtractItem(Item item)
    48.     {
    49.         items.Remove(item);
    50.  
    51.         if (onItemChangedCallback != null)
    52.         {
    53.             onItemChangedCallback.Invoke();
    54.         }
    55.     }
    56. }
    Obviously this is a lot to sort through and I mainly posted all of this so everyone knows where I'm at. My main focus is in the event handlers because I've looked at the documentation on using them and am still a little fuzzy on the subject. The only way I could think of going about it is if the mouse position is in another slot, it checks to see if a item occupies the slot, if it does, then it swaps out the items, if it doesn't then it drops it into the slot. Would I need to use a raycast for this? or is there another route that is easier? I've searched around on the web to see other peoples solutions but not sure which route to take.
     
  2. Phobiegames

    Phobiegames

    Joined:
    Apr 3, 2017
    Posts:
    113
  3. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    I looked over the code I wrote, but haven't touched or read in months....

    Trying to offer a summarized version: If you use the 'slot' as the dragged item, you can have access to the item the slot contains when you drop it in its new slot.
    Then, check if the slot is a valid location, and call a method to do the assignment. In your situation, I guess that would something like:
    if slot is empty, assign item and update the visual content of old + new
    otherwise, if valid and not empty, temp variable for old, old slot = current slot, current slot = temp.
    Something like that :)

    My code had partial moves, and stacking, too .. as well as probably a few errors. :) It's in 1 of the threads I made, if you were ever interested in looking at it.

    I never kept a list of items, I don't think. I only had a list of slots in my inventory, and each slot was either an item (scriptable object) or null.

    Let me know if some of that makes sense. I'd be happy to chat more or explain, if I can help. I'm a bit rusty on my own code, even. :)
     
    Phobiegames likes this.
  4. Phobiegames

    Phobiegames

    Joined:
    Apr 3, 2017
    Posts:
    113
    I'll def check it out! Yeah my only concern with the way this is set up is finding a way to check whether or not a slot has an item using the ondrop event handler on top of that im also concerned with updating what item is in which slot. Plus I don't know how to use the event handler to detect the other slot and item held with in it
     
  5. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Ya, I'm not saying the way I did it is best/the only way, but when dragging the slot (which also has OnDrop), I was able to check something like:
    if pointerDrag is not null, and pointerDrag (has component Slot), then... followed by checking things I wrote in my previous answer (ie: is empty, put new item, else move them).

    I had a method like .. originally named "UpdateSlot" that I would call on the old and new that would show the icon if the item wasn't null, after being moved/dropped. lol
     
  6. Phobiegames

    Phobiegames

    Joined:
    Apr 3, 2017
    Posts:
    113
    With the current logic that I have and set up, could I make the OnDrop check another slot? Since I have an inventory slot class? If so how would I go about doing it? My main focus at the moment is to just get items to drag to another slot. Then I'll continue forward with stacks, swapping, etc.. I know I should be able to update the UI after a drag and drop using the updateUI delegate I have set up correct? I'm trying to check if another slot has an item OnDrop, if item isn't present then I need to remove it from the old slot and add it to the new. I just don't know how to go about checking another slot is all.

    sorry for my lack of knowledge of event systems, I just started using them. This is also my first attempt at an inventory system like this. Currently my item drag script is on the item icon, which is under two other parents for each slot
     
  7. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Well, I don't think you have to check if where you're dropping contains the mouse position. OnDrop should only be called if you're over something with OnDrop.

    Anyways, I think your way (if I understand it correctly) is a little diff than mine. If you drag the icon, you need to reference the slot from which you start. Then, when you drop you have to find the slot. If the OnDrop and InventorySlot are on the same game object, you should be able to use GetComponent to retrieve it and then work with the reference of where you began and where you dropped?

    Sorry, I'm a bit slow to absorb how your code is designed & setup. Let me know if any of that is making sense. :)
     
    Kipey1 likes this.
  8. Phobiegames

    Phobiegames

    Joined:
    Apr 3, 2017
    Posts:
    113
    I know i can use getcomponent to get the starting slot but how does that work for searching for the other slot being dropped on's slot?
     
  9. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Well, if you drop it somewhere that has a slot, you can use it there, too. Otherwise, if it's a parent/child or you use GetComponentInParent/Child.
     
  10. AbandonedCrypt

    AbandonedCrypt

    Joined:
    Apr 13, 2019
    Posts:
    73
    You can raycast UI elements and access their children like that im pretty sure. I think i did it like that when i wrote my d&d inv quite a while ago.

    Edit: There it is
     
  11. howler123

    howler123

    Joined:
    May 7, 2017
    Posts:
    17
    Yes, same problem in 2021, EventSystems OnDrop does not work.(I have tried so many variation of setup, it just makes me mad to try another) You have to use a RaycastAll down in the onEndDrag function. It will return all objects below you on a List/Array, from there find the object you need. If you use the one associated with PointerEventData it only gives the very first object and nothing below. Don't forget about Canvas groups and allowing raycasting. I used the debugger in VS with breakpoints set in OnDrop and never ever got a hit. I have never seen a Unity Evangilest try to explain how this all works, or point to a location in the manual, not the UI 1.0 script manual, but the part of the manual that explains the inner working of these things.