0
\$\begingroup\$

I'm in the process of implementing an inventory system in my game.

The script which is most important to this question is InventorySlotManager.

public class InventorySlotManager : MonoBehaviour 
{
    ItemData itemData;

    const int numSlots = 20;
    Item[] itemsInInventory = new Item[numSlots];
    [SerializeField] GameObject[] slots = new GameObject[numSlots];
    [SerializeField] GameObject itemImagePrefab;
    int[] stackSize = new int[numSlots];

    void Awake()
    {
        itemData = GameObject.Find("Item Manager").GetComponent<ItemData>();
    }

    void Start()
    {
        AddItem(itemData.coin);
        AddItem(itemData.silverRing);
        AddItem(itemData.coin);
    }

    void Update()
    {
        if (Input.GetKey(KeyCode.Alpha1))
        {
            AddItem(itemData.coin);
        }
        if (Input.GetKey(KeyCode.Alpha2))
        {
            AddItem(itemData.silverRing);
        }
    }

    void AddItem(Item item)
    {
        int indexOfDuplicateItem = System.Array.IndexOf(itemsInInventory, item);

        if (indexOfDuplicateItem > -1)
        {
            Text quantityTextOfDuplicateItem = slots[indexOfDuplicateItem].transform.Find("Item Image(Clone)").gameObject.GetComponentInChildren<Text>();
            quantityTextOfDuplicateItem.text = (++stackSize[indexOfDuplicateItem]).ToString();
        }
        else
        {
            for (int i = 0; i < numSlots; i++)
            {
                if (itemsInInventory[i] == null)
                {
                    itemsInInventory[i] = item;
                    GameObject itemImage = Instantiate(itemImagePrefab, slots[i].transform);
                    itemImage.GetComponent<Image>().sprite = item.itemSprite;
                    Text quantityTextOfNewItem = slots[i].GetComponentInChildren<Text>();
                    stackSize[i] = item.itemQuantity;
                    quantityTextOfNewItem.text = stackSize[i].ToString();
                    return;
                }
            }
        }
    }
}

This does what you'd expect. (The bit in Update is just there temporarily for testing.) When an item gets added, the function checks to see if one already exists in the items array. If not, then the for loop is executed and the item is added to the items array, an item image game object is instantiated in the corresponding index of the slots array, etc. So far so good, everything behaves properly until I start dragging items around in my DragHandler script.

So I press play, my inventory has two kinds of objects in it (coin, and silver ring). They each display their correct quantities (stack sizes) as I keep adding items. Then, I move the coin from slot 0 to slot 5 and attempt to add more coins to the inventory. It stops working because the line Text quantityTextOfDuplicateItem = slots[indexOfDuplicateItem].transform.Find("Item Image(Clone)").gameObject.GetComponentInChildren<Text>(); returns null at that point.

What is the best way to fix this such that this reference won't become null if I move the item to a different slot after it gets added to my inventory? I think I either have to map the items array to the slots array or store all of these values, e.g., items, slots, quantitytext, in one array. Is this correct?

\$\endgroup\$
1
\$\begingroup\$

The first thing is to break down your lines when you have Find() queries, array accesses, and other things that could be null so you can know which part causes the null reference:

Transform slotTransform = slots[indexOfDuplicateItem].transform;
Transform foundTransform = slotTransform.Find("Item Image(Clone)");
Text quantityTextOfDuplicateItem = foundTransform.gameObject.GetComponentInChildren<Text>();
  • slots[indexOfDuplicateItem] could be null
  • slotTransform.Find("Item Image(Clone)") could return null

I suspect some of your objects are not named exactly "Item Image(Clone)".


It's also better to avoid using Find() when you can.

To do this save itemImage in a class or struct, as well as slot, stackSize and any other information you may want to manage per inventory slot.

public struct InventorySlot
{
   public Item item = ??? enum or class ???;
   public int stackSize = 0;
   public GameObject gameObject = new GameObject();
   public Image image = null;
   public Text text = null;
};

InventorySlot[] inventorySlots = new InventorySlot[numSlots];

This will make it a lot easier to manage your arrays.

You probably also want to use a combined stack+item struct instead so you can manager stacks better.

For example you can create a function to split a stack:

public struct ItemStack {
    public Item item = ???;
    public int stackSize = 0;

    // return a new stack which is 1/x th of this stack. (eg: with 4: return 1/4, this stack keeps 3/4) default is two halves.
    public ItemStack SplitIn(int divider=2)
    { 
      ItemStack r = new ItemStack();
      r.item = item;
      r.stackSize = (stackSize + divider - 1) / divider; // round up
      stackSize -= r.stackSize; // remove form current stack
      return r;
    }
};
\$\endgroup\$
  • \$\begingroup\$ Hey thanks for the response. All of my gameObjects are in fact named Item Image(Clone) so the slots[indexOfDuplicateItem] part should be null. I've never used structs before. How would I fit them into my current code? In other words, how might I change my current code that handles adding items to the inventory if I create struct that contains the item, stacksize, image, etc.? \$\endgroup\$ – embracethefuture Oct 13 '17 at 3:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.