Finished Inventory system

Functionality#

I want the inventory system in this game to work as follows:

The player has an amount of inventory slots (Let’s say 8 to begin with). Each of these slots can hold one of any item in the game. On top of that, the player has 4 gear slots; weapon, ability, armor and accesory. In these slots, only items of the respective type should be allowed, with some further restrictions depending on the character’s class.

This system is basically identical to that of Realm of the Mad God, which looks like this:

RotMG Inventory

To get this functionality, I will be using a lot of inheritance as it lets me have categories within categories of items.

Hierarchy#

The base class Item will be abstract and derive from ScriptableObject. The abstract modifier means I can’t create an instance of Item itself (which wouldn’t make sense), but only classes that inherit from it which aren’t also abstract. I’m deriving from ScriptableObject for easy creation and serialization, and it also lets us easily have fields of types such as GameObject or Sprite, which can be assigned through the inspector.

public abstract class Item : ScriptableObject
{
    public Sprite sprite;
    public string description;
}

The two fields here, sprite and description are something every item should have and so they can be put in the Item class itself.

public abstract class Equipable : Item
{
    // public StatBoost[] statBoosts;
}

Anything that can be equipped in one of the player’s equipment slots (Weapon, Ability, Armor, Accessory) will also derive from Equipment, which allows it to have any number of stat boosts on equip (eg. +2 speed, -5 defense). I haven’t actually implemented this yet (hence the comment) but plan to do so next.

public abstract class Weapon : Equipable
{
    public ShotPattern shotPattern;
    public float fireRate;
}

Next class down is the Weapon. Notice how this is still an abstract class, and thats because theres different types of weapons.

[CreateAssetMenu(menuName="Item/Equipable/Weapon/Sword")]
public class Sword : Weapon
{
}

The final level, the type of weapon. For most things this will likely not hold any new data and is just here to allow for type restrictions on slots.

The reason for all of this is so that we can restrict an item slot to only be able to hold a particular type of item. For example, the player shouldn’t be able to equip an ability in their weapon slot.

In the player’s inventory I could declare weapon as a field of type Weapon, however there are multiple character classes, each with a weapon type which is the only one it can use. Therefore that approach would not work. I also can’t declare the field as type Sword, as there’s no way to change that type after compilation.

The solution is a wrapper around the item, which I’ll call ItemSlot.

ItemSlots#

[System.Serializable]
public class ItemSlot
{
    public Type slotType = typeof(Item);
    public Item item = null;

    public ItemSlot(Type _slotType)
    {
        slotType = new(_slotType);
    }

    public ItemSlot()
    {
    }

    public ItemSlot(Type _slotType, Item _item)
    {
        slotType = new(_slotType);
        item = _item;
    }
}

ItemSlot has 2 fields - the Item that it holds (null for none), and a Type that restricts the type of item it can hold. Note that it can hold anything derived from the slotType value, for example a slot with slotType Weapon can hold an axe or bow etc.

From there, I made an ItemContainer class that is derived from MonoBehaviour, and will hold ItemSlots aswell as manage adding, removing and swapping items between them.

public class ItemContainer : MonoBehaviour
{
    public event EventHandler OnContainerChanged;

    public int size = 8;
    public List<ItemSlot> contents = new();

    public bool TryAddItem(Item item)
    // ...

    public bool TrySwapSlots(int s1, int s2)
    // ...

    public void RemoveItem(int index)
    // ...

    public int GetFreeSlotOfType(Type itemType)
    // ...
}

ItemContainer will be anything like a Loot bag, chest or inventory. However the inventory has a few special things that I want it to do, so it’s actually a class derived from ItemContainer.

public class Inventory : ItemContainer
{
    public ItemSlot weapon { get { return contents[0]; } }
    public ItemSlot ability { get { return contents[1]; } }
    public ItemSlot armor { get { return contents[2]; } }
    public ItemSlot accessory { get { return contents[3]; } }

    public void OnValidate()
    // ...
}

I’ve added fields for weapon, ability, armor and accessory to make code related to those more clear. Also added an OnValidate method as I want to make sure some things are verified and the OnContainerChanged event is invoked when editing through the inspector.

Speaking about the inspector, our ItemSlot’s slotType field isn’t actually shown in the editor, because Type is not serializable. This was something that I wasn’t aware of when making this system, however there is a way to overcome this which go over in here