Filtering Inventory?

I am following the Inventory Course (from the RPG class) and am now at the point where I’m putting it in my game.

I want to be able to filter out the items type and display a separate inventory based on the menu from where it was accessed… (like only the wearables or only the actions bar compatible items)

Also, I know currently the inventory system only supports “wearable and actions” items… I would also like to add 1 more category type to that…
I already edited the enum for the scriptable objects for wearables to account for the actual equipments types we’re going to have in the game but i don’t recall where the “main” category were created to make a new “3rd” one…

To only display one of the 3 categories, I figured I’d probably need to update the InventorySlotsUI and add a serializable field to determine which of the “3 types” should be displayed… (then make 3 copy of that inventory prefab based on which inventory is being shown)…

EDIT : After digging around in the code a bit more, it might be easier to simply use item properties to filer out… Since I do not plan on using The Action bar at all… I’m using the “action” items as consumables… and the new (3rd category) i wanted to add could totally fit in the action items/consumables… it might be easier to simply filter out based on a new boolean property of the action items scriptable object… still no clue how i would go around displaying only 1 OR the other when building the inventory…

so any suggestion would be greatly appreciated!

I don’t think there’s a need to make three inventory prefabs… It can be done with one and some buttons…

I would put buttons at the top for each category in your Enum, then in your InventoryUI script, you’re going to want to have some methods to accomodate those switches…

I’m at the wrong computer at the moment, but I’ll work up a sample script… Please post your item Category enum and how it is referenced in the InventoryItem, and I’ll work from there.

Hi again and thank you for taking the time to reply and offer to help out! :slight_smile:
Prior to this class, I had never used “enums” before so my knowledge of those is still quite limited…

I did not edit/change the core functionality from the “inventory.unitypackage” i imported… instead of using the mouse on click to pickup, I am using on trigger enter since its not a top down point and click game.

The only enum I currently have is the one that already existed for the"wearables" so it is quite empty and really simple

public enum EquipLocation
    {
        Armor,
        Weapon,
    }

Obviously having one for the type (Wearables, Action, MISC) would be even better… but it would need to re-write most of the inventory system… since currently EquippableItem.cs is one of the category and the other is ActionItem.cs (both of which create different scriptable object “categories”) writing a new type that way is probably the way to go…

However, I’ll still need to figure out how to display only one of the types in a specific inventory page. This is hte most important part I’d think… by Using both ActionItem and EquippableItem as inspiration I could most likely create the 3rd new category… but displaying only one of the categories at a time is the part I have no idea how to do :slight_smile:

Again thank you for your time and for reading and have a nice day!

I didn’t get to this today, but I will do my best to get you a workable script tomorrow.

1 Like

To help you “better” help me :wink:

Here is something I have done/added inside the “public class ActionItem : InventoryItem”

[SerializeField] bool ASpirit = false;
...
public bool isASpirit()
        {
            return ASpirit;
        }

This will allow me to easily divide Regular Action Items vs Spirit “action items”… and the 3rd category I wish to display separately is the equipments (weapon/armor) that I displayed yesterday…

If i could figure out what to add in the file :
“public class InventoryUI : MonoBehaviour”
since this is where the inventory UI is actually drawn and built…
THEN again, on redraw, it does “itemUI.Setup” which is how it populate the inventory itself…

The goal is that it select/displays only the “one” type selected…

  1. Action Items NOT Spirit
  2. Action Items Spirits
  3. Equippables
    That would allow to have 3 different inventory display for all 3 types needed…

Again, Thanks so much for wanting to help!! :slight_smile:

So our selection query is going to have to work on two parameters, the 2nd parameter being different depending on the context…

It’s that second part that’s tricky (not impossible, mind you, just tricky).

I’ll give you the first part of this: We’re going to need a special return method from Inventory.cs.

        public struct InventoryEntry
        {
            public InventoryItem item;
            public int slot;
        }

        public IEnumerable<InventoryEntry> GetRawInventoryData()
        {
            for (int i = 0; i < inventorySize; i++)
            {
                InventoryEntry entry = new InventoryEntry
                                       {
                                           item = slots[i].item,
                                           slot = i
                                       };
                yield return entry;
            }
        }

This is what we’ll use in InventoryUI to filter the results.
I’ll get started on the actual filters later tonight.

Thank you for that! :slight_smile:

I assumed you were from Europe since you always answered during the evening/night… hehe… guess not! – It’s crazy, I followed the whole tutorial, and understood “most” of it… yet when it comes to expanding the functionality, this is so advanced compared how i’ve been making code myself that I dont know where to start…

Tiny detail however, I think the code you posted will go into Inventory.cs and not InventoryUI.cs since there is no reference to InventorySize or Slots at all in the InventoryUI script…

I added it to Inventory.cs (with using systems.generic) and it compiled fine :slight_smile:

I will keep writing it every time, but THANK you so much for helping!! :slight_smile:

Yes, the code goes in Inventory, but InventoryUI will be calling that method to get the slots with the numbers. The filters will be in InventoryUI, this is just getting all of the data in a different way.

1 Like

Nope, California. I usually answer questions before I leave for work or in the evening at dinner, along with questions that don’t require me to have the code in front of me during lunch. My day job is building circuit boards.

wow… I am even more impressed… you have a full time day job and on top of that you work here to help people out? this is dedication! You have no idea how much I appreciate the assist! :slight_smile:

Hi again! :slight_smile:
I did try a lot to figure out how to add actual filtering to the inventory display, but it seems just out of reach for my current knowledge…
So I’ll definitely be needing some extra help to finish making this work…

Oops, I let this one slide.

Ironically, it turns out I already solved this for another student, and it doesn’t require changes to Inventory.cs, just InventoryUI.cs

  private ItemCategory categoryFilter;  
        private EquipLocation locationFilter;
        // For all categories except Equipment, your butttons should call this method
        public void SetCategoryFilter(ItemCategory category)
        {
            categoryFilter = category;
            Redraw();
        }
        //For Armour, your button should call this method.
        public void SetLocation(EquipLocation location)
        {
            categoryFilter = ItemCategory.Armour;
            locationFilter = location;
            Redraw();
        }


        
        IEnumerable<int> GetFilteredItems()
        {
            List<int> result = new List<int>();
            List<int> nulls = new List<int>();
            for (int i = 0; i < playerInventory.GetSize(); i++)
            {
                InventoryItem item = playerInventory.GetItemInSlot(i);
                if (item != null)
                {
                    if (categoryFilter == ItemCategory.None)
                    {
                        result.Add(i);
                    }
                    else if (categoryFilter == ItemCategory.Armour &&
                             item is EquipableItem equipableItem)
                    {
                        if (equipableItem.GetAllowedEquipLocation() == locationFilter) result.Add(i);
                    }
                    else if (item.GetCategory()==categoryFilter) result.Add(i);
                } else
                {
                    nulls.Add(i);
                }
            }
            result.AddRange(nulls);
            return result;
        }

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

            foreach (int slot in GetFilteredItems())
            {
                var itemUI = Instantiate(InventoryItemPrefab, transform);
                itemUI.Setup(playerInventory, slot);
            }
        }

This really seems like it will work out!!! :slight_smile:

There is however a tiny part that is missing…

I do not have the ItemCategory “class” or the GetCategory() method anywhere…
were those 2 supposed to be added in inventory.cs along with InventoryEntry that we added last week? – after a bit of digging, it seems like the GetCategory should be located in InventoryItem.cs

Not quite sure where to make/create ItemCategory itself however… or what should be located in it…

Thank you so much once more for your time!!

It’s actually part of the Shops and Dialogues course, where we add an ItemCategory to the Equipment.cs

public enum ItemCategory
{
      All,
      Weapons,
      Armor,
      Potions,
      Abilities,
      Special
}

(It may vary a bit from that, going from memory). Then the InventoryItem needs an

[SerializeField] ItemCategory category;

public ItemCategory GetCategory() => category;

O M G!! I finally got it working!!! :grinning_face_with_smiling_eyes:

I actually simplified your code quite a bit…

I added this at the top of InventoryUI

        public ItemCategory categoryFilter = new ItemCategory();

Which allowed me to select using a drop down list in the inspector which inventory I wanted to display in each menu…
I updated your ItemCategory enum to fit our game need obviously…

then Cut down and simplified your code from InventoryUI.cs a lot to this :

//USING THIS TO REFRESH THE INVENTORY
        public void OpenInventory()
        {
            playerInventory = Inventory.GetPlayerInventory();
            Redraw();
        }

        // PRIVATE

        // NEW FILTERING SYSTEM
        IEnumerable<int> GetFilteredItems()
        {
            List<int> result = new List<int>();
            List<int> nulls = new List<int>();
            for (int i = 0; i < playerInventory.GetSize(); i++)
            {
                InventoryItem item = playerInventory.GetItemInSlot(i);
                if (item != null)
                {
                    if (item.GetCategory() == categoryFilter) result.Add(i);
                }
                else
                {
                    nulls.Add(i);
                }
            }
            result.AddRange(nulls);
            return result;
        }
        

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

            foreach (int slot in GetFilteredItems())
            {
                var itemUI = Instantiate(InventoryItemPrefab, transform);
                itemUI.Setup(playerInventory, slot);
            }
        }

Somehow the “playerinventory” was null and not being assigned in awake like it should have been… not 100% sure why…

BUT at least now its working!! all my inventories display only what is needed of them! :grinning_face_with_smiling_eyes:

Thank you so much for your help and patience!

1 Like

Well done!!

Hey Brian,
I am having difficulties trying to adapt this to the pooling option that you provided for the shop UI inventory redraw.

rivate void Redraw()
        {
            int rowIdx = 0;
            int slotIndex = GetFilteredItems().Count();
            // foreach (int slot in GetFilteredItems())
            for (int i = 0; i < slotIndex; i++)
            {
                InventorySlotUI row;
        
                if (rowIdx < existingRows.Count) //Activate an existing row
                {
                    row = existingRows[rowIdx];
                    row.gameObject.SetActive(true);
                }
                else //not enough rows, add one to the hierarchy and cache it in existingRows
                {
                    row = Instantiate(InventoryItemPrefab, transform);
                    existingRows.Add(row);
                }
        
                rowIdx++;
                row.Setup(selectedInventory, i);
            }
        
            for (int i = rowIdx; i < existingRows.Count; i++) //disable any rows we don't need.
            {
                existingRows[i].gameObject.SetActive(false);
            }
        }

If I use it with a slotIndex, the inventory does not get filtered correctly. If I use a foreach, the inventory does get filtered, but a weird bug occurs where I can’t move items in the slots freely, and If I move on item in the slot of another, it somehow duplicates that item.
I created another script for the inventory filter buttons, like we have for shops, and that is because I am using a dropdown that gets populated with them and I select what filter I want from it.

You’ll need to link me to the pooling script I wrote for the ShopUI (I write a lot of scripts on the fly!)… I suspect it was heavily taylored to the ShopUI. Unfortunately, the filtering tends to fight with the drag and drop system, so I’ll probably have to come up with a separate pooling solution. (While Unity 2021 actuallyy has a new generic pooling solution, I still find that every pooling solution needs to be custom taylored to the unique application).

Am I glad our game is in the old school traditional rpg menu (chrono trigger, final fantasy) and we dont have diablo style drag/drop interactions :wink:
Good luck to you dramolxe to figure it out!!

1 Like

Sure,
This was the pooling system that you came up with for the shopUI. I’ve been using it everywhere and it’s working without any problems.

private List<InventorySlotUI> existingRows = new List<InventorySlotUI>();

private void Redraw()
        {
            int rowIdx = 0;
            int slotIndex = selectedInventory.GetSize();
            for (int i = 0; i < questIndex; i++)
            {
                InventorySlotUI row;
        
                if (rowIdx < existingRows.Count) //Activate an existing row
                {
                    row = existingRows[rowIdx];
                    row.gameObject.SetActive(true);
                }
                else //not enough rows, add one to the hierarchy and cache it in existingRows
                {
                    row = Instantiate(InventoryItemPrefab, transform);
                    existingRows.Add(row);
                }
        
                rowIdx++;
                row.Setup(selectedInventory, i);
            }
        
            for (int i = rowIdx; i < existingRows.Count; i++) //disable any rows we don't need.
            {
                existingRows[i].gameObject.SetActive(false);
            }
        }

I use the selectedInventory because I am using your multiple containers methods. Since I have bags, and other lootable containers.
So you defined the list of existing prefabs, and in the redraw you took care of the activation / deactivation for prefabs that are needed or not needed.
I think the GetfilteredItems method is not going hand in hand with this pooling method?

Privacy & Terms