007
Of course, for any sort of progression the player needs to be able to obtain better items over time.
For this we need a good flexible item system that lets us make new items easily. I’ll be using a lot of OOP here so if that’s not your thing maybe look away.
Functionality⌗
The inventory and item system in this game will be similar to the one in Realm of the Mad God, or if you’ve never heard of that game, Terraria is probably the closest. The player has an inventory with slots. Each item takes up 1 slot. However, the player also has their equipment slots for their weapon, ability, armor and accessory. In these slots, only items of the respective type should be allowed, with some further restrictions depending on the character’s class.
Hierarchy⌗
Our base class Item
will be abstract and derive from ScriptableObject
. The abstract modifier means we can’t create an instance of Item
itself, but only classes that inherit from it and aren’t abstract. We’re deriving from ScriptableObject
so that we can easily create new items from within the inspector with no programming. 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 are a sprite for the item that will be used basically everywhere and so every item should have one, and a description that will appear in the item’s tooltip.
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 GameObject bullet;
}
Next class down is the Weapon
, which has a field for the bullet that it shoots. In the future it might change a bit to allow for multiple bullets, and other things like rate of fire tweaks. 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 weapons this will likely not hold any new data and is just here to allow for type restrictions on slots. However for abilities, there will be some fields for each ability type as each ability does different things.
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 we 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. We also can’t declare the field as type Sword
, as there’s no way to change that type after compilation.
Introducing, ItemSlot
s.⌗
[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 anythign derived from the slotType value, for example a slot with slotType Weapon can hold an axe or bow etc.
From there, we have an ItemContainer
class, that is derived from MonoBehaviour, and will hold ItemSlot
s 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 I will go over in the next post as this one is getting too long.