I finished the lesson and I played my game when something odd happened. As soon as I started it, I got 80 or so error messages saying this:
“NullReferenceException: Object reference not set to an instance of an object
GameDevTV.Inventories.Inventory.FindStack (GameDevTV.Inventories.InventoryItem item) (at Assets/Asset Packs/GameDev.tv Assets/Scripts/Inventories/Inventory.cs:207)”
There are more lines that go back all the way to when the PlayerController updates, but those didn’t seem relevant.
I looked through that particular code, but I couldn’t find any errors. Here it is anyway, in case I missed something:
using System;
using UnityEngine;
using GameDevTV.Saving;
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
InventorySlot[] slots;
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("Player");
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.Length;
}
/// <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)
{
int i = FindSlot(item);
if (i < 0)
{
return false;
}
slots[i].item = item;
slots[i].number += number;
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.Length; 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)
{
slots[slot].number -= number;
if (slots[slot].number <= 0)
{
slots[slot].number = 0;
slots[slot].item = null;
}
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;
}
slots[slot].item = item;
slots[slot].number += number;
if (inventoryUpdated != null)
{
inventoryUpdated();
}
return true;
}
// PRIVATE
private void Awake()
{
slots = 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.Length; 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())
{
return -1;
}
for (int i = 0; i < slots.Length; i++)
{
if (object.ReferenceEquals(slots[i].item, item))
{
return i;
}
}
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++)
{
slots[i].item = InventoryItem.GetFromID(slotStrings[i].itemID);
slots[i].number = slotStrings[i].number;
}
if (inventoryUpdated != null)
{
inventoryUpdated();
}
}
}
}
In addition, it triggers as soon as I click on the pickup spawner and continues until I stop the game. When I click on the “Default Pickup” prefab I created, it does the same thing, but it doesn’t even let me pick it up.
Now what do I do?