Getting Error: Index was outside the bounds of the array

So I have been following a tutorial on making a “Fortnite” style inventory system and all has been going well until today I ran into some issues. For some reason the code doesn’t work when I run it but it does on the tutorial. Whenever you try and switch/organize the items in your inventory it doesn’t act how it should. It basically is supposed to move the sprite from one spot to another but it moves the sprite and keeps it on the cursor even after you let go of the mouse button and you get this error:

Please help!

Here is the tutorial I am following:

Here are the two scripts that I use:

Script 1 (This script is the one to move the items:

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

public class UIElementDragger : MonoBehaviour
{

    public const string DRAGGABLE_TAG = "UIDraggable";
    public const string STATIC_TAG = "UIStatic";  // it is imperative that ONLY the InvSlot GameObjects have this tag

    private float[,] UIBoundaries = new float[,]
    {
      //left, right
      {1353f, 1900f},
      //bottom, top
      {55f, 160f}
    };
    private bool dragging = false;

    private Vector2 originalPosition;
    private Transform objectToDrag;
    private Image objectToDragImage;

    List<RaycastResult> hitObjects = new List<RaycastResult>();
    private BRS_InventoryManager _IM;
    private GameObject[] UISlots;  //UI InventorySlots
    private int[] _slots = new int[2];

    #region Monobehaviour API

    void Start()
    {
        _IM = GameObject.Find("BRS_Mechanics").GetComponent<BRS_InventoryManager>();
        UISlots = GameObject.FindGameObjectsWithTag(STATIC_TAG);
    }

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            objectToDrag = GetDraggableTransformUnderMouse();

            if (objectToDrag != null)
            {
                //BRS - Addition
                // remember the slot number
                _slots[0] = GetUISlotNumber(objectToDrag.position);

                dragging = true;

                objectToDrag.SetAsLastSibling();

                originalPosition = objectToDrag.position;
                objectToDragImage = objectToDrag.GetComponent<Image>();
                objectToDragImage.raycastTarget = false;
            }
        }

        if (dragging)
        {
            objectToDrag.position = Input.mousePosition;
            //Debug.Log(objectToDrag.position);
        }

        if (Input.GetMouseButtonUp(0))
        {
            if (objectToDrag != null)
            {
                var objectToReplace = GetDraggableTransformUnderMouse();

                if (BoundaryCheck(objectToDrag.position))
                {
                    //BRS - Addition
                    //Debug.Log("OBJECT OUT OF RANGE");
                    Destroy(objectToDrag.gameObject);
                    _IM.RemoveFromInventory(_slots[0]); ClearUISlots();
                    //Debug.Log("INSTANTIATE A NEW PICKUP ITEM HERE");
                    objectToDrag = null;
                    dragging = false;
                    return;
                }
                //BRS - Addition
                // We need the ability to drop an item on an empty slot
                // it also must snap in place
                if (objectToReplace != null)
                {
                    _slots[1] = GetUISlotNumber(objectToReplace.position);

                    objectToDrag.position = objectToReplace.position;
                    objectToReplace.position = originalPosition;
                }
                else
                {
                    objectToDrag.position = GetStaticTransformUnderMouse().position;
                    _slots[1] = GetUISlotNumber(objectToDrag.position);
                }

                _IM.UpdateInventory(_slots[0], _slots[1]); ClearUISlots();

                objectToDragImage.raycastTarget = true;
                objectToDrag = null;
            }

            dragging = false;
        }
    }

    //BRS - Addition
    // Reports if the object position exceeds the defined boundaries
    private bool BoundaryCheck(Vector3 pos)
    {
        float x = pos.x;
        float y = pos.y;
        float z = pos.z;  //probably will not use this

        if ((x < UIBoundaries[0, 0] || x > UIBoundaries[0, 1]) || (y < UIBoundaries[1, 0] || y > UIBoundaries[1, 1]))
            return true;

        // implied 'else' because we fell-through
        return false;
    }

    // This is a convenience method - reduces the repetitive code
    private void ClearUISlots()
    {
        _slots[0] = _slots[1] = -1;
    }

    private int GetUISlotNumber(Vector3 forPosition)
    {
        foreach (GameObject UISlot in UISlots)
        {
            if (UISlot.transform.position.x == forPosition.x)
            {
                int lastChar = UISlot.name.Length - 1;
                return System.Int32.Parse(UISlot.name[lastChar].ToString());
            }
        }
        return -1;
    }

    private GameObject GetObjectUnderMouse()
    {
        var pointer = new PointerEventData(EventSystem.current);

        pointer.position = Input.mousePosition;

        EventSystem.current.RaycastAll(pointer, hitObjects);

        if (hitObjects.Count <= 0) return null;

        return hitObjects.First().gameObject;
    }

    private Transform GetDraggableTransformUnderMouse()
    {
        var clickedObject = GetObjectUnderMouse();

        // get top level object hit
        if (clickedObject != null && clickedObject.tag == DRAGGABLE_TAG)
        {
            return clickedObject.transform;
        }

        return null;
    }

    private Transform GetStaticTransformUnderMouse()
    {
        var clickedObject = GetObjectUnderMouse();

        if (clickedObject != null && clickedObject.tag == STATIC_TAG)
        {
            return clickedObject.transform;
        }
        return null;
    }

    #endregion
}

This is the second script (used to manage the items, whether that be dropping the item, switching it around, or something else):

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;

public class BRS_InventoryManager : MonoBehaviour
{
    public int numSlots = 6;
    public InventoryItem[] ItemInv;
    public int nextFreeSlot;
    public GameObject newIcon;
    public GameObject InventoryUIArea;
    private Transform[] SlotCenters;

    private GameObject SlotFrame;

    public FN_ItemManager inItemMgr;
    public string inItemName;
    public bool canStack = false;
    public bool canAdd = false;

    public bool isViewingInventory = true;
    public GameObject InventoryOverlay;
    public GameObject TPC;

    private GameObject _BRS_Mechanics;
    public GameObject weaponHolder;

    public GameHandler gameHandler;

    private PhotonView view;

    GameObject newItemObject;
    GameObject newItemObject1;
    GameObject newItemObject2;
    GameObject newItemObject3;
    GameObject newItemObject4;
    GameObject newItemObject5;

    bool hasArmorEquipped = false;
    //private BRS_UIManager _UIM;

    // Use this for initialization
    void Start()
    {
        view = GetComponent<PhotonView>();
        if (!view.IsMine)
        {
            Destroy(gameObject);
        }

        InventoryOverlay.SetActive(false);
        ItemInv = new InventoryItem[numSlots];
        _BRS_Mechanics = GameObject.Find("BRS_Mechanics");
        //_UIM = GameObject.Find ("BRS_UI").GetComponent<BRS_UIManager> ();
        InventoryUIArea = GameObject.Find("InvUI");
        SlotCenters = InventoryUIArea.GetComponentsInChildren<Transform>();

        //foreach(Transform t in SlotCenters)
        //{
        //Debug.Log(t.position.y);
        //}

        //Get the Slot Frames
        SlotFrame = GameObject.Find("SlotFrame");
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyUp(KeyCode.Tab))
        {
            isViewingInventory = InvScreenViewing(isViewingInventory);
        }

        //We ignore the 0 index because for some reason a list of its children includes itself

        if (Input.GetKeyUp("1"))
        {
            SlotFrame.transform.position = SlotCenters[1].position;
            UpdateIcon(1);
        }

        if (Input.GetKeyUp("2"))
        {
            SlotFrame.transform.position = SlotCenters[2].position;
            UpdateIcon(2);
        }

        if (Input.GetKeyUp("3"))
        {
            SlotFrame.transform.position = SlotCenters[3].position;
            UpdateIcon(3);
        }

        if (Input.GetKeyUp("4"))
        {
            SlotFrame.transform.position = SlotCenters[4].position;
            UpdateIcon(4);
        }

        if (Input.GetKeyUp("5"))
        {
            SlotFrame.transform.position = SlotCenters[5].position;
            UpdateIcon(5);
        }

        if (Input.GetKeyUp("6"))
        {
            SlotFrame.transform.position = SlotCenters[6].position;
            UpdateIcon(6);
        }
    }

    public void UpdateIcon(int Slot)
    {
        GameObject[] ListofTiles = GameObject.FindGameObjectsWithTag("UIDraggable");
        foreach (GameObject go in ListofTiles)
        {
            //Set them all to unselcted first
            var SelectedIcon = go.transform.GetChild(0);
            var UnSelectedIcon = go.transform.GetChild(1);

            SelectedIcon.gameObject.SetActive(false);
            UnSelectedIcon.gameObject.SetActive(true);

            //Then find the one that matches our x value and set it to selected
            if (go.transform.position.x == SlotCenters[Slot].position.x)
            {
                SelectedIcon.gameObject.SetActive(true);
                UnSelectedIcon.gameObject.SetActive(false);
            }
        }
    }

    public bool InvScreenViewing(bool viewState)
    {
        if (viewState == true)
        {
            //If we are going to show the inventory

            //Show the overlay
            InventoryOverlay.SetActive(true);
        }
        else
        {
            //If we are NOT going to show the inventory


            //Hide the overlay
            InventoryOverlay.SetActive(false);
        }
        return !viewState;
    }

    public InventoryItem AddToInventory(InventoryItem newItem)
    {
        canAdd = false;

        //Get a look at what type of item we are adding
        //inItemMgr = newItem.GetComponent<FN_ItemManager> ();

        //Get the name of the item
        inItemName = newItem.ItemName;
        //Debug.Log (inItemName);

        //Do we already have one of those items?
        //TODO - Full Paid Asset coming soon!

        //Is it stackable?
        canStack = newItem.isStackable;

        //If we don't already have one, and it is stackable...
        if (canStack && newItem.ItemsType != InventoryItem.ItemsTypeEnum.Ammo)
        {
            //Debug.Log ("Increase the inventory for this item.");
            return newItem;
        }
        else
        {
            //If we don't already have one, and its NOT stackable...
            //Debug.Log("Attempt to add it to our inventory if we have room");
            if (newItem.isUseable)
            {
                newItemObject = PhotonNetwork.Instantiate(newItem.useableObject.name, newItem.useableObject.transform.position, newItem.useableObject.transform.rotation);
                newItemObject.transform.SetParent(weaponHolder.transform);
                WeaponSwitching weaponSwitching = weaponHolder.GetComponent<WeaponSwitching>();
                weaponSwitching.EnableWeapon();
            }
        }

        //Do we have room?
        //If we get back a -1 that means that we are full up!
        if (newItem.ItemsType != InventoryItem.ItemsTypeEnum.Ammo)
        {
            if (newItem.ItemsType == InventoryItem.ItemsTypeEnum.Armor && hasArmorEquipped == false)
            {
                hasArmorEquipped = true;
                switch (newItem.ItemRarity)
                {
                    case InventoryItem.ItemRarityEnum.Common:
                        gameHandler.SetBodyArmor(Character.BodyArmor.Tier_1);
                        break;
                    case InventoryItem.ItemRarityEnum.Uncommon:
                        gameHandler.SetBodyArmor(Character.BodyArmor.Tier_2);
                        break;
                    case InventoryItem.ItemRarityEnum.Rare:
                        gameHandler.SetBodyArmor(Character.BodyArmor.Tier_3);
                        break;
                    case InventoryItem.ItemRarityEnum.Epic:
                        Debug.Log("Epic Rarity");
                        gameHandler.SetBodyArmor(Character.BodyArmor.Tier_4);
                        break;
                }
            }
            if (NextFreeSlot() < 0)
            {
                //Debug.Log ("You are overincumbered!");
                return null;
            }
            else
            {
                // Adds the item
                var newSlotID = NextFreeSlot();

                ItemInv[newSlotID] = newItem;
                canAdd = true;

                AddInventoryIcon(newItem, newSlotID);

            }
            //canAdd = false;
            return newItem;
        }
        else
        {
            WeaponSwitching weaponSwitching = weaponHolder.GetComponent<WeaponSwitching>();
            int amount = int.Parse(newItem.ItemAmmount);

            // Adds ammo to the weapon switching script
            switch (newItem.AmmoType)
            {
                case InventoryItem.AmmoTypeEnum.Light:
                    weaponSwitching.lightAmmo += amount;
                    break;
                case InventoryItem.AmmoTypeEnum.Heavy:
                    weaponSwitching.heavyAmmo += amount;
                    break;
                case InventoryItem.AmmoTypeEnum.Sniper:
                    weaponSwitching.sniperAmmo += amount;
                    break;
                case InventoryItem.AmmoTypeEnum.Shotgun:
                    weaponSwitching.shotgunAmmo += amount;
                    break;
            }
            return newItem;
        }
    }

    public void RemoveFromInventory(int SlotNum)
    {
        var dropItem = ItemInv[SlotNum].LootPickUp;
        if (ItemInv[SlotNum].ItemsType == InventoryItem.ItemsTypeEnum.Armor)
        {
            hasArmorEquipped = false;
            gameHandler.SetBodyArmor(Character.BodyArmor.None);
        }
        else if (ItemInv[SlotNum].ItemsType == InventoryItem.ItemsTypeEnum.Gun)
        {
            GameObject weapon = weaponHolder.transform.GetChild(SlotNum).gameObject;
            Destroy(weapon);

        }
        ItemInv[SlotNum] = null;
        Instantiate(dropItem, TPC.transform.position, new Quaternion(0, 0, 0, 0));
    }

    public void UpdateInventory(int SlotNum1, int SlotNum2)
    {
        var tempSlot = ItemInv[SlotNum2];
        //weaponHolder.transform.GetChild(SlotNum2).SetSiblingIndex(SlotNum1);
        ItemInv[SlotNum2] = ItemInv[SlotNum1];
        //weaponHolder.transform.GetChild(SlotNum1).SetSiblingIndex(SlotNum2);
        ItemInv[SlotNum1] = tempSlot;
    }

    public int NextFreeSlot()
    {
        nextFreeSlot = System.Array.IndexOf(ItemInv, null);
        //Return the next free slot index
        return nextFreeSlot;
    }

    public void AddInventoryIcon(InventoryItem item, int SlotNum)
    {
        newIcon = Instantiate(item.InvTile);
        newIcon.transform.SetParent(InventoryUIArea.transform);
        newIcon.transform.localScale = new Vector3(1, 1, 1);

        var Magic = 40 + (SlotNum * 92);
        newIcon.transform.localPosition = new Vector3((Magic), 40, 0);
    }
}

I don’t know if I have something setup wrong in the inspector or my script or what.
Thanks your help is greatly appreaciated!

Okay, im not gonna try and read all that, please use code tags to post both your scripts and post your FULL error message

Sorry, never really done this before… It should be fixed now tho.

1 Like

Well so we know the error is in this method

public void UpdateInventory(int SlotNum1, int SlotNum2)
    {
        var tempSlot = ItemInv[SlotNum2];
        //weaponHolder.transform.GetChild(SlotNum2).SetSiblingIndex(SlotNum1);
        ItemInv[SlotNum2] = ItemInv[SlotNum1];
        //weaponHolder.transform.GetChild(SlotNum1).SetSiblingIndex(SlotNum2);
        ItemInv[SlotNum1] = tempSlot;
    }

And when you called it in the other script

_IM.UpdateInventory(_slots[0], _slots[1]); ClearUISlots();

I would just go over the tutorial again, you probably missed something, seems like slots[0] can’t be reached but I am a bit confused, and it doesn’t look like you went watch the full tutorial (I might be wrong) but yeah just go over and you’ll find your mistake

I would suspect that the parameters you are passing to UpdateInventory are not correct. Debug log and see if it returns what you expect. I assume from looking at your code that there are only two possible numbers that should be passed; 0 and 1.

My guess would be the debug log will show a number that is not 0 or 1 which would be the cause of the index going outside of the array bounds.

public void UpdateInventory(int SlotNum1, int SlotNum2)
{
    Debug.Log(SlotNum1, SlotNum2);
}

Also, apart from a few issues with formatting (private stuff should be prefixed with an underscore, etc) using var here is not a good idea because it is not precisely clear what the value of the right side returns. It could be a string of an item name, or an item ID number, etc.

public void UpdateInventory(int SlotNum1, int SlotNum2)
{
    var tempSlot = ItemInv[SlotNum2];
    ItemInv[SlotNum2] = ItemInv[SlotNum1];
    ItemInv[SlotNum1] = tempSlot;
}

Var should only be used when it is expressly clear what it is storing either because it’s obvious from the name used:

 var playerHealthFloat = player.currentHealth;

or it is obvious from the assignment.

 var someValue = player.username;
1 Like

I agree, I did do some Debug.Logs earlier and this code was occasionally passing a “-1”, although I couldn’t figure out why.

    private int GetUISlotNumber(Vector3 forPosition)
    {
        foreach (GameObject UISlot in UISlots)
        {
            if (UISlot.transform.position.x == forPosition.x)
            {
                int lastChar = UISlot.name.Length - 1;
                return System.Int32.Parse(UISlot.name[lastChar].ToString());
            }
        }
        return -1;
    }

Another thing that was happening occasionally was that I would get errors saying this:

Object reference not set to an instance of an object (at Assets/Scripts/UIElementDragger.cs:70)

which is this part of the code:

        if (dragging)
        {
            objectToDrag.position = Input.mousePosition;
            //Debug.Log(objectToDrag.position);
        }

So I think that something weird is also happening with the transforms of the objects but I just can’t figure out what…

  1. You have an array of InventoryItem called ItemInv
  2. You initialise it with a value determined by numSlots (which is 6).
ItemInv = new InventoryItem[numSlots];

This makes your possible valid input options:

ItemInv[0]
ItemInv[1]
ItemInv[2]
ItemInv[3]
ItemInv[4]
ItemInv[5]

  1. You invoke the UpdateInventory method and pass it two parameters.
_IM.UpdateInventory(_slots[0], _slots[1]); ClearUISlots();

This is where you issue apparently exists.

I only see three assignments in your code.

// Line 50
_slots[0] = GetUISlotNumber(objectToDrag.position);

// Line 90
_slots[1] = GetUISlotNumber(objectToReplace.position);

// Line 98
_slots[1] = GetUISlotNumber(objectToDrag.position);

Debug each of these and see what you get.

_slots[0] = GetUISlotNumber(objectToDrag.position);

I tried this one twice and it returned 1 then the second time it returned 3 (I did do it on different slots).

_slots[1] = GetUISlotNumber(objectToReplace.position);

I tried this one twice and it returned 1 then the second time it returned 2 (I did do it on different slots).

_slots[1] = GetUISlotNumber(objectToDrag.position);

I tried this one twice and it returned -1 both times (I did do it on different slots).

So what could this mean?

You can see the function… you can see it returning -1 at the end (line 142 in the element dragger script).

It’s pretty clear that it is doing so because line 136 never evaluates true on the position check to return the found slot.

Line 136 appears to compare floating point numbers for equality. That’s ALWAYS going to cause you issues, so just do not compare floating point numbers for equality.

Here’s why:

https://starmanta.gitbooks.io/unitytipsredux/content/floating-point.html

Beyond that, debugging stuff like this is always the same:

You must find a way to get the information you need in order to reason about what the problem is.

What is often happening in these cases is one of the following:

  • the code you think is executing is not actually executing at all <— bingo
  • the code is executing far EARLIER or LATER than you think
  • the code is executing far LESS OFTEN than you think
  • the code is executing far MORE OFTEN than you think
  • the code is executing on another GameObject than you think it is
  • you’re getting an error or warning and you haven’t noticed it in the console window

To help gain more insight into your problem, I recommend liberally sprinkling Debug.Log() statements through your code to display information in realtime.

Doing this should help you answer these types of questions:

  • is this code even running? which parts are running? how often does it run? what order does it run in?
  • what are the values of the variables involved? Are they initialized? Are the values reasonable?
  • are you meeting ALL the requirements to receive callbacks such as triggers / colliders (review the documentation)

Knowing this information will help you reason about the behavior you are seeing.

If your problem would benefit from in-scene or in-game visualization, Debug.DrawRay() or Debug.DrawLine() can help you visualize things like rays (used in raycasting) or distances.

You can also call Debug.Break() to pause the Editor when certain interesting pieces of code run, and then study the scene manually, looking for all the parts, where they are, what scripts are on them, etc.

You can also call GameObject.CreatePrimitive() to emplace debug-marker-ish objects in the scene at runtime.

You could also just display various important quantities in UI Text elements to watch them change as you play the game.

If you are running a mobile device you can also view the console output. Google for how on your particular mobile target, such as this answer or iOS: https://discussions.unity.com/t/700551 or this answer for Android: https://discussions.unity.com/t/699654

Another useful approach is to temporarily strip out everything besides what is necessary to prove your issue. This can simplify and isolate compounding effects of other items in your scene or prefab.

Here’s an example of putting in a laser-focused Debug.Log() and how that can save you a TON of time wallowing around speculating what might be going wrong:

https://discussions.unity.com/t/839300/3

Also, these things (character customization, inventories, shop systems) are fairly tricky hairy beasts, definitely deep in advanced coding territory.

They contain elements of:

  • a database of items that you may possibly possess / equip
  • a database of the items that you actually possess / equip currently
  • perhaps another database of your “storage” area at home base?
  • persistence of this information to storage between game runs
  • presentation of the inventory to the user (may have to scale and grow, overlay parts, clothing, etc)
  • interaction with items in the inventory or on the character or in the home base storage area
  • interaction with the world to get items in and out
  • dependence on asset definition (images, etc.) for presentation

Just the design choices of an inventory system can have a lot of complicating confounding issues, such as:

  • can you have multiple items? Is there a limit?
  • are those items shown individually or do they stack?
  • are coins / gems stacked but other stuff isn’t stacked?
  • do items have detailed data shown (durability, rarity, damage, etc.)?
  • can users combine items to make new items? How? Limits? Results? Messages of success/failure?
  • can users substantially modify items with other things like spells, gems, sockets, etc.?
  • does a worn-out item (shovel) become something else (like a stick) when the item wears out fully?
  • etc.

Your best bet is probably to write down exactly what you want feature-wise. It may be useful to get very familiar with an existing game so you have an actual example of each feature in action.

Once you have decided a baseline design, fully work through two or three different inventory tutorials on Youtube, perhaps even for the game example you have chosen above.

Or… do like I like to do: just jump in and make it up as you go. It is SOFT-ware after all… evolve it as you go! :slight_smile:

Breaking down a large problem such as inventory:

https://discussions.unity.com/t/826141/4

“Combining a bunch of stuff into one line always feels satisfying, but it’s always a PITA to debug.” - Star Manta on the Unity3D forums

2 Likes
private Transform objectToDrag;
objectToDrag = GetDraggableTransformUnderMouse();

This appears to be the source of the value that ends up being used and causing the array boundary exception.

private Transform GetDraggableTransformUnderMouse()
{
    var clickedObject = GetObjectUnderMouse();
    // get top level object hit

    if (clickedObject != null && clickedObject.tag == DRAGGABLE_TAG)
    {
        return clickedObject.transform;
    }

    return null;
}

Replace it with a hard-coded value to see if you get an exception or not. If you don’t get an exception you need to figure out why it’s returning unexpected results.

Ok I think I’m getting somewhere, so in the script it checks whether or not the new icon has the same x pos as the inventory slot, but I noticed, when you switch the inventory items the x position of the new icon is a few decimals off of its icon holder which causes it to return -1. So I think the problem could have nothing to do with the UIElementDragger Script but I’m not sure. So in the UIElementDragger Script in this method it checks if the inventory slot has the same x position as the new icon which ends up not being true because when you switch icons it changes the x pos by like a few decimals off.

    private int GetUISlotNumber(Vector3 forPosition)
    {
        foreach (GameObject UISlot in UISlots)
        {
            if (UISlot.transform.position.x == forPosition.x)
            {
                int lastChar = UISlot.name.Length - 1;
                Debug.Log("Get UI slot number returned" + System.Int32.Parse(UISlot.name[lastChar].ToString()));
                return System.Int32.Parse(UISlot.name[lastChar].ToString());
            }
        }
        Debug.Log("GetUISlotNumber returned -1");
        return -1;
    }

So we just need to figure out why it is a few decimals off which I’m not sure why but I think it could have to do with one of these methods in the BRS_InventoryManager Script.

Either this one:

    public void AddInventoryIcon(InventoryItem item, int SlotNum)
    {
        newIcon = Instantiate(item.InvTile);
        newIcon.transform.SetParent(InventoryUIArea.transform);
        newIcon.transform.localScale = new Vector3(1, 1, 1);

        var Magic = 40 + (SlotNum * 92);
        newIcon.transform.localPosition = new Vector3((Magic), 40, 0);
    }

or this one:

    public void UpdateIcon(int Slot)
    {
        GameObject[] ListofTiles = GameObject.FindGameObjectsWithTag("UIDraggable");
        foreach (GameObject go in ListofTiles)
        {
            //Set them all to unselcted first
            var SelectedIcon = go.transform.GetChild(0);
            var UnSelectedIcon = go.transform.GetChild(1);

            SelectedIcon.gameObject.SetActive(false);
            UnSelectedIcon.gameObject.SetActive(true);

            //Then find the one that matches our x value and set it to selected
            if (go.transform.position.x == SlotCenters[Slot].position.x)
            {
                SelectedIcon.gameObject.SetActive(true);
                UnSelectedIcon.gameObject.SetActive(false);
            }
        }
    }

Although I might be wrong about why the x pos is off, I do know that it is off.

I actually think it has something to do with the delay in the Update function of the UIElementDragger Script
Right Here (I moved it up just for testing purposes):

 if (Input.GetMouseButtonDown(0))
        {
            objectToDrag = GetDraggableTransformUnderMouse();

            if (objectToDrag != null)
            {
                originalPosition = objectToDrag.position;

                //BRS - Addition
                // remember the slot number
                Debug.Log("Object to drag is not null");

                _slots[0] = GetUISlotNumber(objectToDrag.position);
                Debug.Log(_slots[0]);

                dragging = true;

                objectToDrag.SetAsLastSibling();


                objectToDragImage = objectToDrag.GetComponent<Image>();
                objectToDragImage.raycastTarget = false;
            }

            Debug.Log("Object to drag is null");
        }

Where it sets the originalPosition to the objectToDrag.position I think is a bit off because the player may move their mouse a bit before it gets called. Anyone know how to fix this?

You could always just check if the number is negative then turn it positive.

myInt = myInt * -1;

But I’m guessing that might not give you the result that you want either. In any case you’re trying to pass an integer to an array so trying to use a float is not ideal.

Can you not convert it/round it up or down to a single digit? or remove all numbers after the decimal place to get a result?

I am now using the name instead of the transform to check which is now working, but it seems to have either created another issue or shown one. For some reason it is now mixing things up… when you switch the slots in your inventory and then drop them it doesn’t always drop the item that you were trying to drop. I’m not sure why it’s doing this but here is the code now:

BRS_InventoryManager:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;
//=============================================================================
// This script was created by Matt Parkin as a part of the Polygon Pilgrimage
// Go to www.youtube.com/mattparkin and subscribe for more awesome tutorials!
// You are free to use/modify/edit this script however you like.  Please give
// credit to the orginal creator and share your work!
//=============================================================================
public class BRS_InventoryManager : MonoBehaviour
{
    public int numSlots = 6;
    public InventoryItem[] ItemInv;
    public int nextFreeSlot;
    public GameObject newIcon;
    public GameObject InventoryUIArea;
    private Transform[] SlotCenters;

    private GameObject SlotFrame;

    public FN_ItemManager inItemMgr;
    public string inItemName;
    public bool canStack = false;
    public bool canAdd = false;

    public bool isViewingInventory = true;
    public GameObject InventoryOverlay;
    public GameObject TPC;

    private GameObject _BRS_Mechanics;
    public GameObject weaponHolder;

    public GameHandler gameHandler;

    private PhotonView view;

    GameObject newItemObject;

    bool hasArmorEquipped = false;
    //private BRS_UIManager _UIM;

    // Use this for initialization
    void Start()
    {
        view = GetComponent<PhotonView>();
        if (!view.IsMine)
        {
            Destroy(gameObject);
        }

        InventoryOverlay.SetActive(false);
        ItemInv = new InventoryItem[numSlots];
        _BRS_Mechanics = GameObject.Find("BRS_Mechanics");
        //_UIM = GameObject.Find ("BRS_UI").GetComponent<BRS_UIManager> ();
        InventoryUIArea = GameObject.Find("InvUI");
        SlotCenters = InventoryUIArea.GetComponentsInChildren<Transform>();

        //foreach(Transform t in SlotCenters)
        //{
        //Debug.Log(t.position.y);
        //}

        //Get the Slot Frames
        SlotFrame = GameObject.Find("SlotFrame");
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyUp(KeyCode.Tab))
        {
            isViewingInventory = InvScreenViewing(isViewingInventory);
        }

        //We ignore the 0 index because for some reason a list of its children includes itself

        if (Input.GetKeyUp("1"))
        {
            SlotFrame.transform.position = SlotCenters[1].position;
            UpdateIcon(1);
        }

        if (Input.GetKeyUp("2"))
        {
            SlotFrame.transform.position = SlotCenters[2].position;
            UpdateIcon(2);
        }

        if (Input.GetKeyUp("3"))
        {
            SlotFrame.transform.position = SlotCenters[3].position;
            UpdateIcon(3);
        }

        if (Input.GetKeyUp("4"))
        {
            SlotFrame.transform.position = SlotCenters[4].position;
            UpdateIcon(4);
        }

        if (Input.GetKeyUp("5"))
        {
            SlotFrame.transform.position = SlotCenters[5].position;
            UpdateIcon(5);
        }

        if (Input.GetKeyUp("6"))
        {
            SlotFrame.transform.position = SlotCenters[6].position;
            UpdateIcon(6);
        }
    }

    public void UpdateIcon(int Slot)
    {
        GameObject[] ListofTiles = GameObject.FindGameObjectsWithTag("UIDraggable");
        foreach (GameObject go in ListofTiles)
        {
            //Set them all to unselcted first
            var SelectedIcon = go.transform.GetChild(0);
            var UnSelectedIcon = go.transform.GetChild(1);

            SelectedIcon.gameObject.SetActive(false);
            UnSelectedIcon.gameObject.SetActive(true);

            //Then find the one that matches our x value and set it to selected
            if (go.transform.name == SlotCenters[Slot].name)
            {
                SelectedIcon.gameObject.SetActive(true);
                UnSelectedIcon.gameObject.SetActive(false);
            }
        }
    }

    public bool InvScreenViewing(bool viewState)
    {
        if (viewState == true)
        {
            //If we are going to show the inventory

            //Show the overlay
            InventoryOverlay.SetActive(true);
        }
        else
        {
            //If we are NOT going to show the inventory


            //Hide the overlay
            InventoryOverlay.SetActive(false);
        }
        return !viewState;
    }

    public InventoryItem AddToInventory(InventoryItem newItem)
    {
        canAdd = false;

        //Get a look at what type of item we are adding
        //inItemMgr = newItem.GetComponent<FN_ItemManager> ();

        //Get the name of the item
        inItemName = newItem.ItemName;
        //Debug.Log (inItemName);

        //Do we already have one of those items?
        //TODO - Full Paid Asset coming soon!

        //Is it stackable?
        canStack = newItem.isStackable;

        //If we don't already have one, and it is stackable...
        if (canStack && newItem.ItemsType != InventoryItem.ItemsTypeEnum.Ammo)
        {
            //Debug.Log ("Increase the inventory for this item.");
            return newItem;
        }
        else
        {
            //If we don't already have one, and its NOT stackable...
            //Debug.Log("Attempt to add it to our inventory if we have room");
            if (newItem.isUseable)
            {
                newItemObject = PhotonNetwork.Instantiate(newItem.useableObject.name, newItem.useableObject.transform.position, newItem.useableObject.transform.rotation);
                newItemObject.transform.SetParent(weaponHolder.transform);
                WeaponSwitching weaponSwitching = weaponHolder.GetComponent<WeaponSwitching>();
                weaponSwitching.EnableWeapon();
            }
        }

        //Do we have room?
        //If we get back a -1 that means that we are full up!
        if (newItem.ItemsType != InventoryItem.ItemsTypeEnum.Ammo)
        {
            if (newItem.ItemsType == InventoryItem.ItemsTypeEnum.Armor && hasArmorEquipped == false)
            {
                hasArmorEquipped = true;
                switch (newItem.ItemRarity)
                {
                    case InventoryItem.ItemRarityEnum.Common:
                        gameHandler.SetBodyArmor(Character.BodyArmor.Tier_1);
                        break;
                    case InventoryItem.ItemRarityEnum.Uncommon:
                        gameHandler.SetBodyArmor(Character.BodyArmor.Tier_2);
                        break;
                    case InventoryItem.ItemRarityEnum.Rare:
                        gameHandler.SetBodyArmor(Character.BodyArmor.Tier_3);
                        break;
                    case InventoryItem.ItemRarityEnum.Epic:
                        Debug.Log("Epic Rarity");
                        gameHandler.SetBodyArmor(Character.BodyArmor.Tier_4);
                        break;
                }
            }
            if (NextFreeSlot() < 0)
            {
                //Debug.Log ("You are overincumbered!");
                return null;
            }
            else
            {
                // Adds the item
                var newSlotID = NextFreeSlot();

                ItemInv[newSlotID] = newItem;
                canAdd = true;

                AddInventoryIcon(newItem, newSlotID);

            }
            //canAdd = false;
            return newItem;
        }
        else
        {
            WeaponSwitching weaponSwitching = weaponHolder.GetComponent<WeaponSwitching>();
            int amount = int.Parse(newItem.ItemAmmount);

            // Adds ammo to the weapon switching script
            switch (newItem.AmmoType)
            {
                case InventoryItem.AmmoTypeEnum.Light:
                    weaponSwitching.lightAmmo += amount;
                    break;
                case InventoryItem.AmmoTypeEnum.Heavy:
                    weaponSwitching.heavyAmmo += amount;
                    break;
                case InventoryItem.AmmoTypeEnum.Sniper:
                    weaponSwitching.sniperAmmo += amount;
                    break;
                case InventoryItem.AmmoTypeEnum.Shotgun:
                    weaponSwitching.shotgunAmmo += amount;
                    break;
            }
            return newItem;
        }
    }

    public void RemoveFromInventory(int SlotNum)
    {
        GameObject dropItem = ItemInv[SlotNum].LootPickUp;
        if (ItemInv[SlotNum].ItemsType == InventoryItem.ItemsTypeEnum.Armor)
        {
            hasArmorEquipped = false;
            gameHandler.SetBodyArmor(Character.BodyArmor.None);
        }
        /*else if (ItemInv[SlotNum].ItemsType == InventoryItem.ItemsTypeEnum.Gun)
        {
            GameObject weapon = weaponHolder.transform.GetChild(SlotNum).gameObject;
            Destroy(weapon);

        } */
        ItemInv[SlotNum] = null;
        Instantiate(dropItem, TPC.transform.position, new Quaternion(0, 0, 0, 0));
    }

    public void UpdateInventory(int SlotNum1, int SlotNum2)
    {
        var tempSlot = ItemInv[SlotNum2];
        //weaponHolder.transform.GetChild(SlotNum2).SetSiblingIndex(SlotNum1);
    //    ItemInv[SlotNum2].name = SlotNum1.ToString();
    Debug.Log(ItemInv[SlotNum2].name);
        ItemInv[SlotNum2] = ItemInv[SlotNum1];
        //weaponHolder.transform.GetChild(SlotNum1).SetSiblingIndex(SlotNum2);
        //    ItemInv[SlotNum1].name = tempSlot.ToString();
        Debug.Log(ItemInv[SlotNum1].name);
        ItemInv[SlotNum1] = tempSlot;
    }

    public int NextFreeSlot()
    {
        nextFreeSlot = System.Array.IndexOf(ItemInv, null);
        //Return the next free slot index
        return nextFreeSlot;
    }

    public void AddInventoryIcon(InventoryItem item, int SlotNum)
    {
        newIcon = Instantiate(item.InvTile);
        newIcon.transform.SetParent(InventoryUIArea.transform);
        newIcon.transform.localScale = new Vector3(1, 1, 1);
        newIcon.name = SlotNum.ToString();

        var Magic = 40 + (SlotNum * 92);
        newIcon.transform.localPosition = new Vector3((Magic), 40, 0);
        Debug.Log(newIcon.transform.position);
    }
}

UIElementDragger:

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

/* Copyright (C) Xenfinity LLC - All Rights Reserved
* Unauthorized copying of this file, via any medium is strictly prohibited
* Proprietary and confidential
* Written by Bilal Itani <bilalitani1@gmail.com>, June 2017
*/

public class UIElementDragger : MonoBehaviour
{

    public const string DRAGGABLE_TAG = "UIDraggable";
    public const string STATIC_TAG = "UIStatic";  // it is imperative that ONLY the InvSlot GameObjects have this tag

    private float[,] UIBoundaries = new float[,]
    {
      //left, right
      {1353f, 1900f},
      //bottom, top
      {55f, 160f}
    };
    private bool dragging = false;

    private Vector2 originalPosition;
    private Transform objectToDrag;
    private Image objectToDragImage;

    List<RaycastResult> hitObjects = new List<RaycastResult>();
    private BRS_InventoryManager _IM;
    private GameObject[] UISlots;  //UI InventorySlots
    private int[] _slots = new int[2];

    #region Monobehaviour API

    void Start()
    {
        _IM = GameObject.Find("BRS_Mechanics").GetComponent<BRS_InventoryManager>();
        UISlots = GameObject.FindGameObjectsWithTag(STATIC_TAG);
    }

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            objectToDrag = GetDraggableTransformUnderMouse();

            if (objectToDrag != null)
            {
                //BRS - Addition
                // remember the slot number
                _slots[0] = GetUISlotNumber(objectToDrag.name);

                dragging = true;

                objectToDrag.SetAsLastSibling();

                originalPosition = objectToDrag.position;
                objectToDragImage = objectToDrag.GetComponent<Image>();
                objectToDragImage.raycastTarget = false;
            }
        }

        if (dragging)
        {
            objectToDrag.position = Input.mousePosition;
            //Debug.Log(objectToDrag.position);
        }

        if (Input.GetMouseButtonUp(0))
        {
            if (objectToDrag != null)
            {
                var objectToReplace = GetDraggableTransformUnderMouse();

                if (BoundaryCheck(objectToDrag.position))
                {
                    //BRS - Addition
                    //Debug.Log("OBJECT OUT OF RANGE");
                    Destroy(objectToDrag.gameObject);
                    _IM.RemoveFromInventory(_slots[0]); ClearUISlots();
                    //Debug.Log("INSTANTIATE A NEW PICKUP ITEM HERE");
                    objectToDrag = null;
                    dragging = false;
                    return;
                }
                //BRS - Addition
                // We need the ability to drop an item on an empty slot
                // it also must snap in place
                if (objectToReplace != null)
                {
                    _slots[1] = GetUISlotNumber(objectToReplace.name);

                    objectToDrag.position = objectToReplace.position;
                    objectToReplace.position = originalPosition;
                }
                else
                {
                    objectToDrag.position = GetStaticTransformUnderMouse().position;
                    _slots[1] = GetUISlotNumber(objectToDrag.name);
                }

                _IM.UpdateInventory(_slots[0], _slots[1]); ClearUISlots();

                objectToDragImage.raycastTarget = true;
                objectToDrag = null;
            }

            dragging = false;
        }
    }

    //BRS - Addition
    // Reports if the object position exceeds the defined boundaries
    private bool BoundaryCheck(Vector3 pos)
    {
        float x = pos.x;
        float y = pos.y;
        float z = pos.z;  //probably will not use this

        if ((x < UIBoundaries[0, 0] || x > UIBoundaries[0, 1]) || (y < UIBoundaries[1, 0] || y > UIBoundaries[1, 1]))
            return true;

        // implied 'else' because we fell-through
        return false;
    }

    // This is a convenience method - reduces the repetitive code
    private void ClearUISlots()
    {
        _slots[0] = _slots[1] = -1;
    }

    private int GetUISlotNumber(string name)
    {
        foreach (GameObject UISlot in UISlots)
        {
            int lastChar = UISlot.name.Length - 1;
            int UISlotName = System.Int32.Parse(UISlot.name[lastChar].ToString());
            if (UISlotName.ToString() == name.Substring(0, 1))
            {
                //Debug.Log("Get UI slot number returned" + System.Int32.Parse(UISlot.name[lastChar].ToString()));
                return System.Int32.Parse(UISlot.name[lastChar].ToString());
            }
        }
        return -1;
    }

    private GameObject GetObjectUnderMouse()
    {
        var pointer = new PointerEventData(EventSystem.current);

        pointer.position = Input.mousePosition;

        EventSystem.current.RaycastAll(pointer, hitObjects);

        if (hitObjects.Count <= 0) return null;

        return hitObjects.First().gameObject;
    }

    private Transform GetDraggableTransformUnderMouse()
    {
        var clickedObject = GetObjectUnderMouse();

        // get top level object hit
        if (clickedObject != null && clickedObject.tag == DRAGGABLE_TAG)
        {
            return clickedObject.transform;
        }

        return null;
    }

    private Transform GetStaticTransformUnderMouse()
    {
        var clickedObject = GetObjectUnderMouse();

        if (clickedObject != null && clickedObject.tag == STATIC_TAG)
        {
            return clickedObject.transform;
        }
        //Debug.Log("GetStaticTransformUnderMouse returned null");
        //BRS - Addition
        //Debug.Log("OBJECT OUT OF RANGE");
        Destroy(objectToDrag.gameObject);
        _IM.RemoveFromInventory(_slots[0]); ClearUISlots();
        //Debug.Log("INSTANTIATE A NEW PICKUP ITEM HERE");
        objectToDrag = null;
        dragging = false;
       // Debug.Log("Object to drag wasn't in the boundary position so we dropped the item");
        return null;
    }

    #endregion
}

It may have something to do with the method on line 179 of this script.
Your help is greatly appreciated!

To be honest it’s quite a lot of code and a little messy for me to try and look through.

You’re not using camelcase, private and public are not distinguished with the typical underscore, const not prefixed with c_, etc.

All of that makes it harder for outsiders to understand when reading your stuff (especially having two scripts). I find myself constantly going back and forth to check for variables, or one script referencing the other script but not explicitly so it’s difficult to follow the code logic.

(https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/coding-conventions)

As for what to do if you do want to use a lot of debugs to find the source of the error…

Otherwise you need to follow the steps I outlined before where you debug stuff in a chain until you reach the core issue.

You say it doesn’t always drop the weapon expected so start with that. Debug log whatever is dictating the weapon to drop and then check where that gets it’s information from.

There is a good change you might have missed something in the tutorial if the code works as you say.

When the wrong thing drops you need to figure out what should have dropped and then you will find the error.

I have a feeling it’s because you’re giving it a range of possible numbers (A = 2 - 4)? Instead of just dictating A = 3.

Sorry its not even my code… when I realized the code didn’t work I just copied and pasted it from the tutorial to make sure I didn’t miss anything and apparently he is a pretty sloppy coder…

This can be the issue with not really understanding more fundamental aspects of programming. You end up relying on copy/paste and when you get an error it can stop you making any progress.

To be honest I think you need to take some time and go through the basics (lists, arrays, jagged arrays, interfaces, events, subscribe/publish relationships, switch statements, enumerations, extension methods, ternary operators, linear interpolation, vector maths etc.

I’ve never bothered with 99% of tutorials because once you know a decent set of fundamental stuff you can pretty much build anything you want.

That is your best bet when it comes to actually improving or you will just encounter another error/bug in the future that you can’t fix because you don’t understand how the code works.

https://learn.unity.com/pathways

Scripting (118 results)
https://learn.unity.com/search?k=["tag%3A5814655a090915001868ebec"]

I would say the system provided in the tutorial is pretty poor. It’s messy and difficult to follow. You would probably be better off finding a different tutorial or spending a few days browsing the learn section to see if you find anything relevant to what you want to do.

For example, you could use interfaces to implement part of the system where your character picks up an object and adds it to their inventory. You could use a list or dictionary to store the inventory, etc.

Just start with the most basic parts. Create a list and figure out how to add stuff to it. Viola, you have a basic inventory system. Then you just need to replicate the features you want from fortnite’s inventory system.

Alright thanks man, still pretty new to this stuff.

I understand the rush to build something but you would honestly better serve yourself by dedicating the next 3 months to studying via the Unity Learn section. Then take a shot at building a complex inventory system or something else.

It’s not always a bad idea to try and build something really complicated with the intention of abandoning the project later. For example, an open-world rpg. The idea is to just start building it and see how far you get. Eventually you will reach a point where your skills are insufficient and you should move on to something else.

Hopefully though you will have learned quite a lot that you can implement into your next idea.

Copying video tutorials will only get you so far. Eventually you will run into a point where you want to build something complex enough that there are not any full tutorials on the subject.

Also, if you are not a somewhat experienced programmer then video tutorials can pose the risk of teaching bad habits (which you seem to have picked up). You should take the chance to fix these issues now before they become more noticeable in the future when you start to work on more complex projects.

Do you know of any good resources to learn clean code? This is something I have been interested in.