I feel really stupid for asking this, but I can't seem to find anything about it here. Maybe because it's so obvious that nobody asked before? But I'll bite the bullet and ask anyway:

I want my inventory to be icon/slot based and not text lists. Let's take the inventory window for example. I set up the GUI box for it - in that GUI box, in the Choice settings, I chose "Position" and in the "Choice Width/Height" I set the dimension of the slot/icon I want to have correct?

I set the window to dragable, because I want the whole window to be dragable too.

But how do I make those icons dragable. Let's say from the inventory to the equipment window or to the world. It always only drags the window itself (which again, I want to be dragable too) but I can'd drag the icons.

  • There is no slot based inventory in ORK, but you can achieve that look.

    Having 'slots' is handled via the Choice Settings of the used GUI box. Use the List mode and set the Choice Columns setting to have the choices create a 'grid' instead of being listed, and the Choice Width/Height setting for defining the size of the choices.

    In menu screens, how something is presented is handled by the Content Layout settings of the individual parts/settings. E.g. to only show icons, you'd set the content type to Icon.
    Drag/drop is also set up in menu screens - there are Drag and Drop settings available where it's possible. As said, there's no slot inventory, so you can't really use that to sort the inventory - just e.g. give it to someone else, drop it into the world or use it on someone.
    Please consider rating/reviewing my products on the Asset Store (hopefully positively), as that helps tremendously with getting found.
    If you're enjoying my products, updates and support, please consider supporting me on patreon.com!
  • I see, thanks.
    This would be a must have feature for ORK 3 in my opinion, since it's a very standard feature for western RPGs. I hope you are considering it :)
  • It's not that difficult to create a grid based GUI with the ORK API if you really need one. I knocked up a basic one today to trial some ideas.

    Mine was a mix character plus inventory. Handles drag and drop.

    Basically a control script that populated a Scroll View with content components Grid Layout Group and Content Size fitter for the grid,with Inventory Item prefabs, each with a script to handle drag and drop.
    Other prefabs for the Character equipment slots (and another script for dragging them)

    Ork framework API calls was mostly calls to ORK.Game.ActiveGroup.Leader.Equipment and ORK.Game.ActiveGroup.Leader.Inventory

    I'm no Unity expert but it works well. Let me know if you want any more details or code.

    (Excuse programmer graphics)
    image
  • Cool! I sent you a PM
  • Just in case anyone else is interested, I've replied here as well if that's ok.

    This is not a tutorial on Drag and Drop and just includes a base class you can inherit from to give any mono-develop class some dragging capability.

    The drag/drop may look like a lot of code, but it is generic (Does not rely on ORK) and handles a few things including leaving a faded icon in place when dragging and layer issues.

    That code is a mixture of code I discovered, tutorials I did and my changes over time. I hope it works as well as I think it does.

    The idea:
    - We have a prefab for each inventory item with 2 images, a BORDER and a CHILD IMAGE for the item itself.
    - We add the prefabs to a scroll view in a grid form. Any empty inventory items will just show the border
    - The scroll view has a controller script on it that clears and populates the list on demand
    - The inventory item prefab has a drag script on it that allows dragging that image (and leaving a ghost behind)
    - The inventory item, when dragged, contains a link to the ORK.IInventoryShortcut so that when dropped, the receiving slot can decide if this is a good idea or not.
    - A second "equipment slot" prefab is created with a script handling drag *AND* drop. It has two images same as the Inventory Item.
    - You add as many prefabed equipment slots to your screen as you want and lay them out as you want. Each one has a property with a ID for the ORK equipment slot it is matched to

    Riders:
    - I did not see for a way in ORK to know inventory item A is in inventory slot X (and so on) it just seemed to be a linear list. So this code doesn't allow you to drag items around the inventory and place in specific slots.
    - You don't *ACTUALLY* need the border images, this can all be done with one image but it may look a little odd.

    Enhancements:
    - You can easily reduce the list of inventory items returned from ORK by items/weapons/equipment etc. So you can add filter buttons to the GUI to allow the user to JUST see Weapons for example
    - You can determine if something is an Item and add a counter text to the item so the user can see how many items are in the stack.
    - Add more images to the inventory or equipment slots prefabs to do stuff like masks or special effects. As long as the script is attached to the image that is ACTUALLY the inventory icon, all should work fine. (that could be changed too if you really wanted by making a property for the icon image in the script)
    - If you want to drag the equipment off their slots to remove equipped items, you can add a OnDrop event on the Inventory script, *or* just code it in the Equipment drag script that *any* drag off is removing that item.
    - You can also enhance the equipment slot OnDrop events to handle an Equipment drag event so that you can drag from equipment slot to another equipment slot.
    - Maybe add a text box to each equipment slot to describe what it is? Really lots you can do.

    Step -1: Make sure ORK actually works
    Whoops, got half way through this and realized that my new scene did not have ORK in it!

    Create a empty scene for all this as usual and add a new ORK Game Starter with Quick Testing enabled.

    Add a spawn point that spawns your Player Combatant that actually has an inventory

    Step 0: Base class to handle dragging

    Create a script called DragDropBaseClass.cs and paste in the following code.

    The idea here is we capture the begin, end drags etc and create a duplicate image to the one we are wanting to drag, and ensure that image is on the top canvas layer so while dragging it does not fall behind other UI objects.

    It also reduces the Alpha of the image you left behind to ghost it a bit (Could FX that up a bit with a material to make the image grey or something too.)

    On "drop" all this code does is destroy the dragged image and reset the original back to full Alpha.

    It is up to the surface that was "dropped on" to decide if anything needs to actually change.

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

    public class DragDropBaseClass : MonoBehaviour, IEndDragHandler, IDragHandler, IBeginDragHandler
    {
    public bool dragOnSurfaces = true;

    public GameObject m_DraggingIcon;
    internal RectTransform m_DraggingPlane;

    public void OnEndDrag(PointerEventData eventData)
    {
    if (m_DraggingIcon != null)
    {
    Destroy(m_DraggingIcon);
    Image img = transform.GetComponent<Image>();
    img.color = new Color(img.color.r, img.color.g, img.color.b, 1f);
    }
    }

    public void OnDrag(PointerEventData data)
    {
    if (m_DraggingIcon != null)
    SetDraggedPosition(data);
    }

    public virtual void OnBeginDrag(PointerEventData eventData)
    {
    var canvas = FindInParents<Canvas>(gameObject);
    if (canvas == null)
    return;

    Image img = transform.GetComponent<Image>();
    img.color = new Color(img.color.r, img.color.g, img.color.b, 0.2f);

    // We have clicked something that can be dragged.
    // What we want to do is create an icon for this.
    m_DraggingIcon = new GameObject("icon");

    m_DraggingIcon.transform.SetParent(canvas.transform, false);
    m_DraggingIcon.transform.SetAsLastSibling();

    m_DraggingIcon.AddComponent<CanvasGroup>();
    m_DraggingIcon.GetComponent<CanvasGroup>().blocksRaycasts = false;

    var image = m_DraggingIcon.AddComponent<Image>();

    image.sprite = GetComponent<Image>().sprite;

    if (dragOnSurfaces)
    m_DraggingPlane = transform as RectTransform;
    else
    m_DraggingPlane = canvas.transform as RectTransform;

    SetDraggedPosition(eventData);
    }

    private void SetDraggedPosition(PointerEventData data)
    {
    if (dragOnSurfaces && data.pointerEnter != null && data.pointerEnter.transform as RectTransform != null)
    m_DraggingPlane = data.pointerEnter.transform as RectTransform;

    var rt = m_DraggingIcon.GetComponent<RectTransform>();
    Vector3 globalMousePos;
    if (RectTransformUtility.ScreenPointToWorldPointInRectangle(m_DraggingPlane, data.position, data.pressEventCamera, out globalMousePos))
    {
    rt.position = globalMousePos;
    rt.rotation = m_DraggingPlane.rotation;
    }
    }

    private T FindInParents<T>(GameObject go) where T : Component
    {
    if (go == null) return null;
    var comp = go.GetComponent<T>();

    if (comp != null)
    return comp;

    Transform t = go.transform.parent;
    while (t != null && comp == null)
    {
    comp = t.gameObject.GetComponent<T>();
    t = t.parent;
    }
    return comp;
    }
    }


    Step 1: Create an inventory icon

    Add a canvas and add a UI Image GameObject. Call this object "InventoryBorder". Add a child to this object, call it "InventoryItem"
    image

    Ideally, you'd make the "BorderImage" some sort of square border, and resize the "InventoryItem" to a smaller size than the border (85x85 maybe). You can also give the "InventoryItem" a different Source Image just to test it out.

    You can also add a third image and a mask to ensure the Inventory item is constrained within the border if it is an odd shape etc. sky is the limit.

    Create a new script called "InventoryDrag.cs" and add to the "InventoryItem" gameobject.

    The code for this script is as follows. This inherits from the DragDrop base class and gives the Image you attached the script to full Drag capability is most cases.

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

    public class InventoryDrag : DragDropBaseClass
    {
    public IInventoryShortcut inventoryItem = null;

    public override void OnBeginDrag(PointerEventData eventData)
    {
    // don't drag if this is an empty slot
    if (inventoryItem == null)
    return;

    base.OnBeginDrag(eventData);
    }
    }


    You probably won't be able to drag the image yet since we have not allocated "inventory item". you can comment out the "if (inventoryItem == null) return;" bit and you should be able to drag as a test.

    Now create a prefab from "InventoryBorder" + child and delete the original from the scene. (prefab will probably be called "InventoryBorder")

    Step 2: Grid Inventory using Scroll View

    Create a new Scroll View (UI -> Scroll View) - If you haven't used them before it looks like a lot, as it creates a tree of probably half a dozen game objects but you really only need to be interested in one, the "Content" (Scroll View | Viewport | Content)

    Resize the Scroll View (Top level GameObject) to suite your needs.

    Go to content Game Object and add three components

    a) Grid Layout Group
    b) Content Size Fitter
    c) a new script called "InventoryControl.cs"

    All you really want to change is Grid Layout Group : Spacing X=3 and Y=3
    And you can also change Content Size Fitter : Vertical Fit = Preferred Size

    But these are really optional and just for looks. Play around.

    image

    In the script "InventoryControl.cs" you will need the following code. First use of ORK framework!

    Note this code populates the Scroll View grid list with one prefab per slot requested and links the actual ORK.IInventoryShortcut when available to the GameObject. When this dragged we have access to the actual item at the drop event.

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

    public class InventoryControl : MonoBehaviour
    {
    private Combatant playerCombatant;

    [Tooltip("The game object prefab that will be created for every inventory item")]
    [SerializeField]
    private GameObject prefab;

    [Tooltip("The number of inventory items to create")]
    [SerializeField]
    private int numberToCreate;

    void Start()
    {
    StartCoroutine(GetPlayer());
    }

    private IEnumerator GetPlayer()
    {
    do
    {
    playerCombatant = ORK.Game.ActiveGroup.Leader;
    yield return null;
    }
    while (this.playerCombatant == null);

    PopulateInventory();
    }

    public void PopulateInventory()
    {
    if (playerCombatant != null)
    {
    List<IInventoryShortcut> inventory = new List<IInventoryShortcut>();

    // determine at THIS point what we actually want to see, items, weapons, armor, ALL?
    playerCombatant.Inventory.GetAll(false, false, true, true, true, false, false, false, -1, false, ref inventory);

    // clear the inventory and populate with the new inventory
    clearScrollView();

    GameObject newObj;

    for (int i = 0; i < numberToCreate; i++)
    {
    // Create new instances of our prefab until we've created as many as we specified
    newObj = (GameObject)Instantiate(prefab, transform);

    if (inventory.Count > i)
    {
    Texture tex = inventory[i].GetIcon();
    Transform trItem = newObj.transform.Find("InventoryItem");
    Image img = trItem.GetComponent<Image>();

    ((InventoryDrag)trItem.GetComponent<InventoryDrag>()).inventoryItem = inventory[i];

    img.sprite = Sprite.Create(tex as Texture2D, new Rect(0, 0, tex.width, tex.height), new Vector2(.5f, .5f));
    img.color = new Color(img.color.r, img.color.g, img.color.b, 1);
    }
    else
    {
    // here we are creating a blank border image with no inventrory item inside
    Image img = newObj.transform.Find("InventoryItem").GetComponent<Image>();
    img.color = new Color(img.color.r, img.color.g, img.color.b, 0); // make transparent
    }
    }
    }
    }

    private void clearScrollView()
    {
    foreach (Transform child in transform)
    {
    Destroy(child.gameObject);
    }
    }
    }


    Now drag the "InventoryBorder" prefab we made onto the script property Prefab and make the Number to Create property something like 20.

    Run and test and you should see a grid list of all your inventory items (I hope) and you can drag them about

    Step 3: Equipment slots
    Now we need somewhere to drop the inventory item.

    Create a new Image called "EquipBorder" and add a child to it called "EquipItem" in the same way as the Inventory item

    (These names can all be changed of course to suite your naming convention)

    Add a script to the "EquipItem" gameobject called "EquipDrag.cs" containing the following code.

    using ORKFramework;
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.Events;
    using UnityEngine.EventSystems;
    using UnityEngine.UI;
    using ORKEnums;

    public class EquipDrag : DragDropBaseClass, IDropHandler
    {
    [Tooltip("ORK Framework Equipment slot ID")]
    [SerializeField]
    private int equipmentSlotID;

    private Combatant playerCombatant;

    public InventoryControl controller;
    public Image borderImage;
    public Image equipmentImage;

    void Start()
    {
    StartCoroutine(GetPlayer());
    }

    private IEnumerator GetPlayer()
    {
    do
    {
    playerCombatant = ORK.Game.ActiveGroup.Leader;
    yield return null;
    }
    while (this.playerCombatant == null);

    displayEquipmentSlot();
    }

    private void displayEquipmentSlot()
    {
    if (playerCombatant.Equipment[(int)equipmentSlotID].Available)
    {
    this.gameObject.SetActive(true);

    if (!playerCombatant.Equipment[(int)equipmentSlotID].Equipped)
    {
    borderImage.color = new Color(borderImage.color.r, borderImage.color.g, borderImage.color.b, 1);
    equipmentImage.color = new Color(equipmentImage.color.r, equipmentImage.color.g, equipmentImage.color.b, 0);
    }
    else
    {
    // display this image
    borderImage.color = new Color(borderImage.color.r, borderImage.color.g, borderImage.color.b, 1);
    equipmentImage.color = new Color(equipmentImage.color.r, equipmentImage.color.g, equipmentImage.color.b, 1);

    Texture tex = playerCombatant.Equipment[(int)equipmentSlotID].Equipment.GetIcon();
    equipmentImage.sprite = Sprite.Create(tex as Texture2D, new Rect(0, 0, tex.width, tex.height), new Vector2(.5f, .5f));
    }

    }
    else
    {
    // hide it
    borderImage.color = new Color(borderImage.color.r, borderImage.color.g, borderImage.color.b, 0);
    equipmentImage.color = new Color(equipmentImage.color.r, equipmentImage.color.g, equipmentImage.color.b, 0);
    this.gameObject.SetActive(false);
    }
    }

    public void OnDrop(PointerEventData eventData)
    {
    if (eventData.pointerDrag != null)
    {
    InventoryDrag itemScript = ((InventoryDrag)eventData.pointerDrag.GetComponent<InventoryDrag>());

    if (itemScript != null && playerCombatant.Equipment[(int)equipmentSlotID].Available && !playerCombatant.Equipment[(int)equipmentSlotID].IsBlocked)
    {
    if (itemScript.inventoryItem != null)
    {
    if (itemScript.inventoryItem is ItemShortcut)
    {
    //; we can't actually *equip* items so we can ignore this (for the moment)

    }
    else if (itemScript.inventoryItem is EquipShortcut)
    {
    EquipShortcut eshort = (EquipShortcut)itemScript.inventoryItem;
    if (eshort.CanEquip(playerCombatant))
    {
    playerCombatant.Equipment.Equip((int)equipmentSlotID, eshort, playerCombatant, playerCombatant, true);
    }
    }
    }
    }

    if (itemScript != null && itemScript.m_DraggingIcon != null)
    Destroy(itemScript.m_DraggingIcon);

    displayEquipmentSlot();
    if (controller != null) controller.PopulateInventory();
    }
    }


    public override void OnBeginDrag(PointerEventData eventData)
    {
    // don't drag if this is an empty slot
    if (!playerCombatant.Equipment[(int)equipmentSlotID].Equipped)
    {
    return;
    }

    base.OnBeginDrag(eventData);
    }
    }


    You will see this script has multiple properties. I just like doing it this way since these sort of links work even if you change the names of the game object they are linked to (unlike using FindComponent etc even though I see I did it in the InventoryControl script, probably because it was a prefab)

    So you will need to link a few things:
    a) The EquipBorder gameobject needs to be dragged to "Border Image" property
    b) The EquipImage gameobject needs to be dragged to "Equipment Image" property
    c) And lastly, and the HARDEST, drag the "Inventory Control (script)" from the Content gameobject onto "Controller" property

    image

    The last step is so when the item is dragged and dropped, the Equipment Slot can request the controller refresh the inventory grid.

    If you have issues linking these, a tip to see two inspectors at the same time:
    - Right click the "Inspector" tab
    - Click on Add Tab | Inspector
    - You now have two inspectors.
    - Click on the Content Game object (Both inspectors should show the same stuff at this point)
    - Click on the LOCK symbol on one of the tabs (Top right)
    - Next click on the EquipImage game object so you can see the inventory control script *and* see the equipment Drag script components allowing you to drag the controller script to link it.

    image

    The important code here is we handle the OnDrop event then check to see if:
    - the object being dropped contains an InventoryDrag script
    - the equipment slot is available
    - the equipment slot is not blocked (probably already checked above, not sure)
    - and finally checks to see if the player can ACTUALLY equip this item

    If all that is true, we equip it with playerCombatant.Equipment.Equip letting ORK deal with adding and removing from the inventory and then we refresh the inventory (using that nasty link to the Inventory Controller we had to add)

    The other important thing is we have a link to the equipmentSlotID which maps directly to the ORK equipment slots, so we need to change this number for each slot.

    Once you have this one Equipment Slot working you can drag it off into a prefab and then add as many as you need, just changing the equipment slot id for each one.

    The code does check in DisplayEquipmentSlot if the slots should be shown or not in case your game adds or remove slots as the player progresses.

    I actually used an ENUM as the equipmentSlotID generated from Keldryn's resource that can create a nice script automatically from ORK found here.

    I highly recommend this for all code.

    In this case it allows you to pick from the equipment slot id property "Main Weapon" or "Helmet" rather than 3 or 6

    Sorry this went long, never really written a code tutorial before and honestly this is a bit messy

    Let me know if you have any questions

Sign In or Register to comment.