Unity Project Guide: Build Pickups, Inventory Slots, and a Hotbar
Inventory systems become confusing when beginners try to build everything at once: drag-and-drop, equipment, crafting, shops, storage chests, item rarity, icons, tooltips, and saving.
This guide builds a smaller but useful version:
Items are defined with ScriptableObjects.
The player can pick up items.
Inventory slots can stack items.
Number keys select a hotbar slot.
The selected slot can be consumed.
This gives you a foundation you can extend without rewriting everything.
Step 1: Create the Item Definition
Create a folder:
TEXT
1Assets/Items
Create ItemDefinition.cs.
CSHARP
1using UnityEngine;23[CreateAssetMenu(menuName ="Game/Item Definition")]4public class ItemDefinition : ScriptableObject5{6 public string itemId;7 public string displayName;8 public Sprite icon;9 public int maxStack =1;10 public bool consumable;11}
In Unity:
Right-click in Assets/Items.
Choose Create > Game > Item Definition.
Name it Apple.
Set:
Item Id: apple
Display Name: Apple
Max Stack: 10
Consumable: enabled
Why ScriptableObjects? They let designers create item data in the editor without hardcoding every item in a script.
Step 2: Create an Inventory Slot Model
Create InventorySlot.cs.
CSHARP
1[System.Serializable]2public class InventorySlot3{4 public ItemDefinition item;5 public int quantity;67 public bool IsEmpty => item ==null || quantity <=0;89 public void Clear()10{11 item =null;12 quantity =0;13}14}
This is not a MonoBehaviour. It is plain data.
Step 3: Create the Inventory Component
Create PlayerInventorySlots.cs.
CSHARP
1using System;2using UnityEngine;34public class PlayerInventorySlots : MonoBehaviour5{6[SerializeField] private int slotCount =8;78 public InventorySlot[] Slots { get; private set;}9 public int SelectedIndex { get; private set;}1011 public event Action InventoryChanged;12 public event Action<int> SelectionChanged;1314 private void Awake()15{16 Slots =new InventorySlot[slotCount];17for(int i =0; i < Slots.Length; i++)18{19 Slots[i]=new InventorySlot();20}21}2223 public bool TryAdd(ItemDefinition item, int amount)24{25if(item ==null || amount <=0)returnfalse;2627 amount = AddToExistingStacks(item, amount);28 amount = AddToEmptySlots(item, amount);2930 InventoryChanged?.Invoke();31return amount ==0;32}3334 private int AddToExistingStacks(ItemDefinition item, int amount)35{36for(int i =0; i < Slots.Length; i++)37{38 InventorySlot slot = Slots[i];39if(slot.IsEmpty) continue;40if(slot.item != item) continue;41if(slot.quantity >= item.maxStack) continue;4243 int room = item.maxStack - slot.quantity;44 int toAdd = Mathf.Min(room, amount);45 slot.quantity += toAdd;46 amount -= toAdd;4748if(amount ==0)return0;49}5051return amount;52}5354 private int AddToEmptySlots(ItemDefinition item, int amount)55{56for(int i =0; i < Slots.Length; i++)57{58 InventorySlot slot = Slots[i];59if(!slot.IsEmpty) continue;6061 int toAdd = Mathf.Min(item.maxStack, amount);62 slot.item = item;63 slot.quantity = toAdd;64 amount -= toAdd;6566if(amount ==0)return0;67}6869return amount;70}7172 public void SelectSlot(int index)73{74if(index <0 || index >= Slots.Length)return;75 SelectedIndex = index;76 SelectionChanged?.Invoke(SelectedIndex);77}7879 public void UseSelected()80{81 InventorySlot slot = Slots[SelectedIndex];82if(slot.IsEmpty)return;83if(!slot.item.consumable)return;8485 Debug.Log($"Used {slot.item.displayName}");8687 slot.quantity -=1;88if(slot.quantity <=0)89{90 slot.Clear();91}9293 InventoryChanged?.Invoke();94}95}
Important: the unsubscribe shown above with _ => Render() creates a new delegate, so in production you should use named methods. For a cleaner version:
Then subscribe and unsubscribe with OnSelectionChanged.
Step 7: Test the System
Use this checklist:
Picking up one apple creates a stack.
Picking up several apples increases quantity.
Quantity stops at max stack and moves to another slot.
If all slots are full, pickup remains in the world.
Pressing number keys changes selection.
Pressing E consumes the selected apple.
Empty slots render as empty UI.
Step 8: Common Beginner Mistakes
Do not compare items by display name. Use the ScriptableObject reference or a stable item id.
Do not put all inventory code inside UI buttons.
Do not destroy pickups unless the item was actually added.
Do not save Sprite references directly in save files. Save item ids instead.
Where To Go Next
Next upgrades:
Add non-consumable items like keys.
Add equipment slots.
Save inventory to JSON.
Add drag-and-drop later, after the data model works.
Add item tooltips.
The main lesson is separation: item definitions describe what an item is, inventory slots describe what the player owns, and UI only renders the current state.