Filtering Inventory?

Yeah, I think I can see where that would flop for this, since we have rows missing in the middle. Ironically, this script looks like I optimized it to save some time as well as resources (removing the need to detach/attach the prefabs), but we’ll likely need to more to more of a “return to sender” model of object pool for the inventory. If I recall, we were trying to squeeze every clock we could out of the pooling script.

I’ll see what I can come up with.

By “return to sender” you mean a object pooler which spawns a bunch of objects beforehand, and then the inventory would request however many objects it needed? And it would do that by grabbing the object from the pool and set its position and parent to the inventory transform?

Yep. The model we were using for the shop just deactivated the items and left them attached to the shop, because we were trying to squeeze out performance, and we were taking a hit on those adjustment buttons.

This requires the traditional model, when the UI is redrawn, all existing InventoryUI prefabs should get returned to a cache, then ask the cache for new or cached items as needed.

Hey Brian, just thought i’d ask for a tiny followup question…

Is there any “counter argument” for changing

InventorySlot[ ] slots; //(from an array) 
//to
List<InventorySlot> slots; //(to a list)

?

I actually did that last friday, because I wanted to keep a steady amount of “free” item slots… and add dynamically a new slot every time the player would pickup a new item they did not have previously…

So instead of having 999 slots and having a super huge empty inventory, i can start the game with 5 slots, and increase by 1 every time a new item is picked up…

I made all the required changes from array to list, and so far everything seems to “hold on” and work out fine… :slight_smile: was just curious if there was any reason why array was picked or any potential problem i might run into :slight_smile:

Again, thanks for everything and have a nice day/week!

1 Like

Actually, there is no reason you can’t use a List. It’s a good idea to initialize the list in the declaration, just to be sure to never get a null reference.

List<InventorySlot> slots = new List<InventorySlot>();

I actually have this in my awake (to replace the Array declaration that was there)

slots = new List<InventorySlot> (new InventorySlot[inventorySize]);

That way the logic works and it is declared with the same “starting” size

also had to change all the slots.length with slots.count

and since its impossible to alter a list at index position, i changed the code to something like this :

                InventorySlot tempslot = slots[i];
                tempslot.item = InventoryItem.GetFromID(slotStrings[i].itemID);
                tempslot.number = slotStrings[i].number;
                slots[i] = tempslot;

As for “how I add a new slot”, I created a boolean to determine if the new item added was using a “new empty slot” (and not stackable)

            if (boolNewItem)
            {
                // ADD NEW SLOTS WHEN PICKING UP NEW ITEMS
                inventorySize++;
                var newslot = new InventorySlot();
                slots.Add(newslot);
            }

and this added the new slot at the end of the list…

As I wrote, so far, everything works… hehe… i was extremely impressed i managed to figure all of that and make it work! :slight_smile:

1 Like

Shhh, nobody tell @HawkX that he’s making the transition into genuine coder.
Well done on that!

1 Like

hello @HawkX, I’m pretty new to programming and this is exactly the feature that my inventory needs as well. I tried to follow what you did here but the only thing I managed to do was to change InventorySlot array to list and initialize it in awake, I really can’t figure some of the things like how you created slotStings[i], what does boolNewItem checks and where to put the rest of your codes. help me out please :smile:

The “boolNewItem” is to make sure a new inventory slot is only added if an item that is picked up is using a new slot (wont add when the item is stackable)

private int FindStack(InventoryItem item)
        {
            if (!item.IsStackable())
            {
                boolNewItem = true;
                return -1;
            }

            for (int i = 0; i < slots.Count; i++)
            {
                if (object.ReferenceEquals(slots[i].item, item))
                {
                    boolNewItem = false;
                    return i;
                }
            }
            boolNewItem = true;
            return -1;
        }

and this is where it gets added :

public bool AddToFirstEmptySlot(InventoryItem item, int number)
        {
            boolNewItem = false;

            int i = FindSlot(item);

            if (i < 0)
            {
                return false;
            }
            
            if (boolNewItem)
            {
                // ADD NEW SLOTS WHEN PICKING UP NEW ITEMS
                inventorySize++;
                var newslot = new InventorySlot();
                slots.Add(newslot);
            }

            InventorySlot tempslot = slots[i];
            tempslot.item = item;
            tempslot.number += number;
            slots[i] = tempslot;

            if (inventoryUpdated != null)
            {
                inventoryUpdated();
            }
            return true;
        }

Finally, my capture and restore state for the saving system (which i have not yet implemented at all in our game so i dont even know if it works)

object ISaveable.CaptureState()
        {
            var slotStrings = new InventorySlotRecord[inventorySize];
            for (int i = 0; i < inventorySize; i++)
            {
                if (slots[i].item != null)
                {
                    slotStrings[i].itemID = slots[i].item.GetItemID();
                    slotStrings[i].number = slots[i].number;
                }
            }
            return slotStrings;
        }

        void ISaveable.RestoreState(object state)
        {
            var slotStrings = (InventorySlotRecord[])state;
            for (int i = 0; i < inventorySize; i++)
            {
                InventorySlot tempslot = slots[i];
                tempslot.item = InventoryItem.GetFromID(slotStrings[i].itemID);
                tempslot.number = slotStrings[i].number;
                slots[i] = tempslot;
            }
            if (inventoryUpdated != null)
            {
                inventoryUpdated();
            }
        }

hope this helps! :slight_smile:

2 Likes

Thanks for all your help!! the game runs now without error, but when I try to add an item using this method that works before, I get a ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of collection.
looks like the issue is coming from AddItemToSlot() but everything looks okay in the inventory.cs

ui4

this is how I add an item

and these are the methods I modified using your codes to get the game working





I am not good enough to help out at this point… perhaps Bryan will be able to offer insight… but I would need to read the full error from that index out of range and find out at “what” line it causes that issue…

The thing between array and lists is that they dont calculate the total number of items the say way… array uses length which starts at 0, while lists uses count which starts at 1… so you always have to adapt when calculating the total number of items…

I tried to change it to 1 instead of 0 and still getting the same error
AddItemToSlot(1, inventoryitem[1], 1)

@Brian_Trotter help please :smile:
Here’s the full error(double clicking it doesn’t bring me to any line of script) :

ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index
System.ThrowHelper.ThrowArgumentOutOfRangeException (System.ExceptionArgument argument, System.ExceptionResource resource) (at <695d1cc93cca45069c528c15c9fdd749>:0)
System.ThrowHelper.ThrowArgumentOutOfRangeException () (at <695d1cc93cca45069c528c15c9fdd749>:0)
GameDevTV.Inventories.Inventory.AddItemToSlot (System.Int32 slot, GameDevTV.Inventories.InventoryItem item, System.Int32 number) (at Assets/Scripts/Inventory.cs:221)
Pickup.GetAnItem () (at Assets/Scripts/Drag and drop/Pickup.cs:29)
UnityEngine.Events.InvokableCall.Invoke () (at :0)
UnityEngine.Events.UnityEvent.Invoke () (at :0)
UnityEngine.UI.Button.Press () (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/UI/Core/Button.cs:68)
UnityEngine.UI.Button.OnPointerClick (UnityEngine.EventSystems.PointerEventData eventData) (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/UI/Core/Button.cs:110)
UnityEngine.EventSystems.ExecuteEvents.Execute (UnityEngine.EventSystems.IPointerClickHandler handler, UnityEngine.EventSystems.BaseEventData eventData) (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/EventSystem/ExecuteEvents.cs:50)
UnityEngine.EventSystems.ExecuteEvents.Execute[T] (UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.EventSystems.ExecuteEvents+EventFunction`1[T1] functor) (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/EventSystem/ExecuteEvents.cs:262)
UnityEngine.EventSystems.EventSystem:Update() (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/EventSystem/EventSystem.cs:385)

I pasted some codes that you created to AddItemToSlot() incorrectly but here’s my AddItemToSlot() now and I’m still getting the same error

and here’s the awake() of inventory.cs

same error when I tried adding an item using GetFromID just to check that it is not my array of items that is not working.

most likely the error is coming from the first param of AddItemToSlot() but I can’t seem to find out why.

from your error message, what line in inventory.cs is at (and around) “Inventory.cs:221”?

Those kind of errors are when a loop goes beyond list/array count/length…
The solution might not be to change 0 with 1… but to change the .length to .count - 1 if appropriate in certain situation…

it’s the end of awake

it looks like it doesn’t detect any inventoryslot when I try to add an item for some reason.
it instantiates the right number of slots at start tho
slots

Most likely this is a side effect of using a List for the Inventory.
While you can manipulate elements in a List by index, they do have to exist in the first place.

In your GetAnItem() method, I would use
AddToFirstEmptySlot() instead of AddItemToSlot() because AddItemToSlot depends on the slot already existing.

AddToFirstEmptySlot() works but it doesn’t save any item(im using the jsonsavingsystem) and it duplicates the item whenever I try to put it into a free slot. I’ll try to fix this on my own and if I can’t, I might just go back to arrays :sweat_smile:
I do appreciate all the help guys, I hope I could code like you guys someday @Brian_Trotter @HawkX

if you guys ever come here in the Philippines, message me and I’ll treat you guys :smile:

I dont think i’ve copy pasted my whole “inventory.cs” with all the changes I made…

I think you might only be missing a small part… I would try debugging line by line to see where it breaks and what happens (which value makes it return null or invalid)…

I noticed I had also changed HasItem and RemoveFromSlot which I never copy pasted…

Here is my full inventory.cs in case it might help…

using System;
using UnityEngine;
using GameDevTV.Saving;
using System.Collections.Generic;

namespace GameDevTV.Inventories
{
    /// <summary>
    /// Provides storage for the player inventory. A configurable number of
    /// slots are available.
    ///
    /// This component should be placed on the GameObject tagged "Player".
    /// </summary>
    public class Inventory : MonoBehaviour, ISaveable
    {
        // CONFIG DATA
        [Tooltip("Allowed size")]
        [SerializeField] int inventorySize = 16;

        // STATE
        List<InventorySlot> slots;
        bool boolNewItem = false;

        public struct InventorySlot
        {
            public InventoryItem item;
            public int number;
        }

        // PUBLIC

        /// <summary>
        /// Broadcasts when the items in the slots are added/removed.
        /// </summary>
        public event Action inventoryUpdated;

        /// <summary>
        /// Convenience for getting the player's inventory.
        /// </summary>
        public static Inventory GetPlayerInventory()
        {
            var player = GameObject.FindWithTag("MainUniqueController");
            return player.GetComponent<Inventory>();
        }

        /// <summary>
        /// Could this item fit anywhere in the inventory?
        /// </summary>
        public bool HasSpaceFor(InventoryItem item)
        {
            return FindSlot(item) >= 0;
        }

        /// <summary>
        /// How many slots are in the inventory?
        /// </summary>
        public int GetSize()
        {
            return slots.Count;
        }

        /// <summary>
        /// Attempt to add the items to the first available slot.
        /// </summary>
        /// <param name="item">The item to add.</param>
        /// <param name="number">The number to add.</param>
        /// <returns>Whether or not the item could be added.</returns>
        public bool AddToFirstEmptySlot(InventoryItem item, int number)
        {
            boolNewItem = false;

            int i = FindSlot(item);

            if (i < 0)
            {
                return false;
            }
            
            if (boolNewItem)
            {
                // ADD NEW SLOTS WHEN PICKING UP NEW ITEMS
                inventorySize++;
                var newslot = new InventorySlot();
                slots.Add(newslot);
            }

            InventorySlot tempslot = slots[i];
            tempslot.item = item;
            tempslot.number += number;
            slots[i] = tempslot;

            if (inventoryUpdated != null)
            {
                inventoryUpdated();
            }
            return true;
        }

        /// <summary>
        /// Is there an instance of the item in the inventory?
        /// </summary>
        public bool HasItem(InventoryItem item)
        {
            for (int i = 0; i < slots.Count; i++)
            {
                if (object.ReferenceEquals(slots[i].item, item))
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Return the item type in the given slot.
        /// </summary>
        public InventoryItem GetItemInSlot(int slot)
        {
            return slots[slot].item;
        }

        /// <summary>
        /// Get the number of items in the given slot.
        /// </summary>
        public int GetNumberInSlot(int slot)
        {
            return slots[slot].number;
        }

        /// <summary>
        /// Remove a number of items from the given slot. Will never remove more
        /// that there are.
        /// </summary>
        public void RemoveFromSlot(int slot, int number)
        {
            InventorySlot tempslot = slots[slot];
            tempslot.number -= number;
            slots[slot] = tempslot;

            if (slots[slot].number <= 0)
            {
                tempslot.number = 0;
                tempslot.item = null;
                slots[slot] = tempslot;
            }
            if (inventoryUpdated != null)
            {
                inventoryUpdated();
            }
        }

        /// <summary>
        /// Will add an item to the given slot if possible. If there is already
        /// a stack of this type, it will add to the existing stack. Otherwise,
        /// it will be added to the first empty slot.
        /// </summary>
        /// <param name="slot">The slot to attempt to add to.</param>
        /// <param name="item">The item type to add.</param>
        /// <param name="number">The number of items to add.</param>
        /// <returns>True if the item was added anywhere in the inventory.</returns>
        public bool AddItemToSlot(int slot, InventoryItem item, int number)
        {
            if (slots[slot].item != null)
            {
                return AddToFirstEmptySlot(item, number); ;
            }

            var i = FindStack(item);
            if (i >= 0)
            {
                slot = i;
            }

            InventorySlot tempslot = slots[slot];
            tempslot.item = item;
            tempslot.number += number;
            slots[slot] = tempslot;

            if (inventoryUpdated != null)
            {
                inventoryUpdated();
            }
            return true;
        }

        // PRIVATE

        private void Awake()
        {
            slots = new List<InventorySlot> (new InventorySlot[inventorySize]);
        }

        /// <summary>
        /// Find a slot that can accomodate the given item.
        /// </summary>
        /// <returns>-1 if no slot is found.</returns>
        private int FindSlot(InventoryItem item)
        {
            int i = FindStack(item);
            if (i < 0)
            {
                i = FindEmptySlot();
            }
            return i;
        }

        /// <summary>
        /// Find an empty slot.
        /// </summary>
        /// <returns>-1 if all slots are full.</returns>
        private int FindEmptySlot()
        {
            for (int i = 0; i < slots.Count; i++)
            {
                if (slots[i].item == null)
                {
                    return i;
                }
            }
            return -1;
        }

        /// <summary>
        /// Find an existing stack of this item type.
        /// </summary>
        /// <returns>-1 if no stack exists or if the item is not stackable.</returns>
        private int FindStack(InventoryItem item)
        {
            if (!item.IsStackable())
            {
                boolNewItem = true;
                return -1;
            }

            for (int i = 0; i < slots.Count; i++)
            {
                if (object.ReferenceEquals(slots[i].item, item))
                {
                    boolNewItem = false;
                    return i;
                }
            }
            boolNewItem = true;
            return -1;
        }

        [System.Serializable]
        private struct InventorySlotRecord
        {
            public string itemID;
            public int number;
        }
    
        object ISaveable.CaptureState()
        {
            var slotStrings = new InventorySlotRecord[inventorySize];
            for (int i = 0; i < inventorySize; i++)
            {
                if (slots[i].item != null)
                {
                    slotStrings[i].itemID = slots[i].item.GetItemID();
                    slotStrings[i].number = slots[i].number;
                }
            }
            return slotStrings;
        }

        void ISaveable.RestoreState(object state)
        {
            var slotStrings = (InventorySlotRecord[])state;
            for (int i = 0; i < inventorySize; i++)
            {
                InventorySlot tempslot = slots[i];
                tempslot.item = InventoryItem.GetFromID(slotStrings[i].itemID);
                tempslot.number = slotStrings[i].number;
                slots[i] = tempslot;
            }
            if (inventoryUpdated != null)
            {
                inventoryUpdated();
            }
        }
    }
}
1 Like

I am so sorry guys for wasting your time, I found the issue. it was my fault all along!! I wasn’t paying attention to the course and missed a single line of code.
(inventoryUI.cs)
inventory
this is why I’m getting and out of range error.
The idea to convert array to a list is brilliant and there’s nothing wrong with it.
Thank you so much @HawkX and @Brian_Trotter!!

Grats for figuring it out!! :slight_smile:

1 Like

Privacy & Terms