Working on Inventory System - help with Arrays?

Hi all,

I’m very VERY new at C#, and as a learning assignment, I am working on an inventory system similar to WoW or other MMOs. I realise there are several tutorials on these things, but I the idea is for me to build it entirely from scratch.

Anyway, I want to have the actual Item and all its parameters separate from its reference in the Inventory system. I’ve written a very simple Item script I will attach to each object. The parameters will either be set automatically by the name of the object (The Item script will know the Apple item’s name is Apple, etc.) or I will manually set and save in a prefab (item weight, max stack size, inventory icon, etc.)

Item Code:

using UnityEngine;
using System.Collections;

public class Item : MonoBehaviour {
	
	private string m_ItemName;
	public bool m_IsStackable;
	public Texture2D m_InventoryIcon;
	public int m_MaxStackSize;
	
	private void Awake () {
		m_ItemName = this.name;
	}
}

Yes, very bare bones for now I realise. I’m will expand on it as I go. The Inventory script is where all the item pickup stuff will go (unless you guys have a better suggestion. The issue I was running into sticking the “ive been picked up” portion on the item was OnTriggerEnter doesn’t trigger properly unless I give the player a second collider aside from his regular capsule collider).

So, when the player collides with an item, it will check to see if the item is something the player can pick up (I want to eventually distinguish between bad guys and items without having a script that just has a bool to check if the thing im checking is a baddie or an item…Any suggestions here would be helpful!) If it is, it will add the item to the inventory, and destroy the prefab in the scene.

So, my question really has to do with how Arrays work. I want the array to keep the name of the item, how many there are in each inventory spot, and the actual inventory spot it will (eventually) occupy. Is there any way to do this with a single array? I haven’t gotten into creating the Inventory GUI system, which will take the item icon designated to each item and place it in a grid depending on the order it was picked up (just like MMO bags).

I have had no luck getting my arrays to work. The error I get says “Type string[ ]' does not contain a definition for Add’ and no extension method Add' of type string[ ]’ could be found (are you missing a using directive or an assembly reference?)”

using UnityEngine;
using System.Collections;

public class Inventory : MonoBehaviour {
	
	public string[] m_Inventory = new string[20];

	// Use this for initialization
	void Start () {
	
	}
	
	// Update is called once per frame
	void Update () {
	
	}
	
	void OnTriggerEnter (Collider pCollider) {
		if(pCollider.name == "Apple"){
			m_Inventory.Add("Apple");
			Destroy(pCollider.gameObject);
		}
	}
	
}

So right now just as a test to get started, I just want to add the string “Apple” to my array when the player picks up the Apple, but it’s not working…

So, I dont really want the array to hold the ACTUAL object, just the essential data required to spawn the item on the ground if the player decides to drop it. I feel that the name of the object will be enough, since i will simple instantiate a new prefab of that item name in front of the player when prompted to do so.

Any direction would be fantastic. I’m very new to programming in general so I’m attempting to do this while learning!

A built in array is of a fixed size. There is no add method. You change the array via the index.

m_Inventory[4] = "Apple";

If you want a varying inventory size, as if your player has deep pockets, then you want to look at the List collection type. It has Add/Remove methods and you can grow the collection as you see fit.

The easiest way is by creating a struct or class, and storing instances of those within the array. Is there a reason you don’t want the array to hold objects? That’s the OO way of doing what you want to accomplish. Without them, you’re looking at a multidimensional array or multiple arrays to store the information.

I’m sorry, I’m a bit of a noob here. What’s a struct? How would I go about creating that or a class to store instances of them in the array?

The ONLY reason I’m trying to do it this way is because I’m trying to teach myself proper coding practices. I’ve always been told by co-workers to keep references and the actual data objects separate if possible. I’m very very new, and trying to just learn the basics. If there is a more efficient way to save this information in an array and retrieve it that can be scalable later and won’t cause me to tear my hair out wishing I had done it a different way, please teach me!

Also, if an array is of a fixed size, why isnt my

public string[ ] m_Inventory = new string[20];

creating an array with 20 spots in it? How do I properly create an array with X slots?

I should mention that eventually, I want the Inventory to work as similarly as the standard MMO inventory. You can move items between bag slots, drop items on top of other identical items to create stacks, break apart stacks, have empty spaces in your bag (inventory doesn’t auto-sort to fill gaps etc.)

I’m assuming a multidimensional array is what im going to need for this… am i correct?

There are two possible ways I’d approach this, depending on what you want to do with the Item class you defined above.

Either this class could hold information about an item type, or it could hold information about an individual apple. In the first case it would be something along the lines of “every apple weighs 0.1kg, is worth 1 gold piece, and restores 2hp when eaten”, whereas the second case would allow each apple to be different.

For the purpose of an inventory system, the first case is significantly simpler, as you only need to keep track of the number of each type of item, not the individual items. In this case I would use a Dictionary or Hashmap container to form your inventory. These are special types of arrays which allow you to reference items in the array using something other than an integer.

Below I’ve put a fairly simple example of how you could do this to store information on an items type, quantity, and location in the inventory grid using a struct to store the information (structs are very similair to classes, but see this page for some important differences - Classes, structs, and records - C# | Microsoft Learn).

The m_Inventory Dictionary stores information on all the item piles held in the inventory, and can be referenced using the items name (a string). This makes it relatively simple to find items in the inventory and edit them. However, note that becuase I’ve included a reference back to Item_Info, which contains the name of the item, the use of a dictionary here is unnecessary, but significantly simplifies the usage of the code):

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Inventory : MonoBehaviour {

    // This struct holds the information about an object in the inventory:
    public struct InventoryItem
    {
        // This is a reference to the item info provided by your class
        // It should contain information on the item type, and also a picture for use in the inventory.
        public Item Item_Info;

        // Quantity of item held:
        public int Number_Held;

        // Location of the item in the inventory grid:
        public int X_Location;
        public int Y_Location;

        public InventoryItem(Item item,int number, int x_Loc, int y_Loc)
        {
            Item_Info = item;
            Number_Held = number;
            X_Location = x_Loc;
            Y_Location = y_Loc;
        }

        // Increase the number of objects held (or decrease, by passing a negative number):
        public void IncreaseNumber(int number)
        { Number_Held += number; }
    }

    public Dictionary<string, InventoryItem> m_Inventory = new Dictionary<string, InventoryItem>();

    int next_Free_X = 0;
    int next_Free_Y = 0;
    int width = 10;
	
    void OnTriggerEnter (Collider pCollider) 
    {
        // Check that the object can be picked up here
        if(true)  
        {
            // If we already have this object, increase the number we have
            if(m_Inventory.ContainsKey(pCollider.name))
            {
                m_Inventory[pCollider.name].IncreaseNumber(1);
            }
            else
            {
                // If we don't have this object, add a new entry to the inventory
                m_Inventory.Add(pCollider.name, new InventoryItem(GetItemInfo(pCollider), 1, next_Free_X, next_Free_Y));
            
                // Find the next free inventory slot for new items:
                next_Free_X++;
                if(next_Free_X >= width)
                {
                    next_Free_X = 0;
                    next_Free_Y++;
                }
            }
            // Destroy the object picked up:
		    Destroy(pCollider.gameObject);
	    }
	}

    private Item GetItemInfo(Collider pCollider)
    {
        // You will need to implement this, you could either have a central list (contained in a gameObject) 
        // holding references to all possible items, or you could retrieve the information from the object you hit
        // with:
        return pCollider.GetComponent<Item>();
    }
}

Thank you so much Seregon! After conferring with the programmers at work, I finally understand what your code is doing (I told you I was new to programming! haha).

My items currently have a boolean to check if the item is indeed stackable. Say I wanted my Apple object to NOT be stackable, and each Apple picked up to occupy its own spot in the inventory grid.

Would the best way to do this be to allow the item to still stack in the Dictionary, but simply use the “Next free inventory slot” function to display separate apples? Since it doesn’t really need to distinguish between the apples in the Dictionary, it only makes a difference in display and to which apple is being dragged around in the inventory (which I will handle with a separate InventoryGUI script)

Ideas?

so, I’ve been working on this in my spare time, and I’ve been able to get a pretty nice script working so far. However, I have a problem with incrementing numbers. When I pick up an Apple, it adds the InventoryItem struct to the Array. When I pick up a second Apple, it simply increments the number by 1. However, when I pick up a third Apple, it does not increment the number again… I’ve tried everything to figure out what the issue is to no avail. Can anyone help?

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class HGPInventory : MonoBehaviour {

	public struct InventoryItem
	{
		public Item itemInfo;
		public int quantity;
		
		public InventoryItem (Item item, int numHeld)
		{
			itemInfo = item;
			quantity = numHeld;
		}
		
		public void IncrementQuantity(int number)
		{
			quantity += number;
		}
	}
	
	public List<InventoryItem> m_Inventory = new List<InventoryItem>();
	
	void OnTriggerEnter (Collider pCollider)
	{
		InventoryItem invItem;
		print("pCollider is" + pCollider.name);
		
		int count = m_Inventory.Count;
		bool itemExists = false;
		
		if(true)
		{
			for(int i = 0; i < count; i++)
			{
				invItem = m_Inventory[i];
				
				if((invItem.itemInfo.itemName) == pCollider.name  invItem.itemInfo.isStackable)
				{
					itemExists = true;
					invItem.IncrementQuantity(1);
					print("invItem quantity is" + " " + invItem.quantity);
					print("This hit the 'if' statement" + " " + m_Inventory[i]);
					break;
				}
			}
			
			if(!itemExists)
			{
				invItem = new InventoryItem(GetItemInfo(pCollider), 1);
				m_Inventory.Add(invItem);
				print("invItem quantity is" + " " + invItem.quantity);
			}
			
			print("There are" + " " + m_Inventory.Count + " items in the inventory");
			Destroy(pCollider.gameObject);
		}
	}
		
	private Item GetItemInfo(Collider pCollider)
	{
		return pCollider.GetComponent<Item>();
	}
	
}

The first thing you should get rid of is using a struct. Use a class instead.
This may already fix your problem.
To understand why read through struct vs class in C#. Value vs Reference types.

Basically you use a class for whatever you can. Only use structs if you have really understood their implications.

I’m using a struct because I need a completely new copy of InventoryItem that isn’t a reference to anything inside this List, as defined by Seregon above.

Is that not necessary?