TutorialSeptember 12, 202614 min read

Building an Inventory System in UE5 (With UnrealPilot)

From data structures to drag-and-drop UI. A complete guide to building flexible inventory systems in Blueprint, with AI-assisted shortcuts.

Why Inventory Systems Are Deceptively Complex

Every game needs some form of inventory. Sounds simple: store items, display them, let players use them. In practice, inventory systems touch nearly every other system in your game. Items affect combat stats, trigger quests, enable crafting, persist across saves, replicate in multiplayer.

This guide builds a production-ready inventory system step by step. We'll cover the data architecture that makes future features easy to add, the UI patterns that scale, and how UnrealPilot accelerates the tedious parts.

Data Architecture: The Foundation

The most important decision is how you represent items. Get this wrong, and you'll be refactoring constantly. Get it right, and adding new item types becomes trivial.

Item Data Table Structure

Start with a Data Table for static item definitions. Create a struct (S_ItemData) with these fields:

S_ItemData
├── ItemID (Name) - Unique identifier
├── DisplayName (Text) - Localized name
├── Description (Text) - Localized description
├── Icon (Texture2D) - UI representation
├── ItemType (E_ItemType) - Weapon, Consumable, Material, etc.
├── MaxStackSize (Integer) - 1 for unique items, higher for stackables
├── Weight (Float) - For weight-limited inventories
├── BaseValue (Integer) - For trading/selling
└── ItemClass (Class Reference) - The actual actor class to spawn

This separation between data (what an item is) and instances (items in inventory) is crucial. The Data Table is your source of truth for all item definitions.

Inventory Slot Structure

For actual inventory storage, create a simpler struct:

S_InventorySlot
├── ItemID (Name) - Reference to Data Table row
├── Quantity (Integer) - Stack count
└── InstanceData (Map) - Runtime modifications (durability, enchantments)

The InstanceData map handles items that can be modified. A sword might have different durability values, or a potion might have bonus effects. This map stores any per-instance variations without bloating the base struct.

The Inventory Component

Create an Actor Component (AC_Inventory) to manage inventory logic:

AC_Inventory
├── Variables
│   ├── Slots (Array of S_InventorySlot)
│   ├── MaxSlots (Integer)
│   ├── MaxWeight (Float)
│   └── CurrentWeight (Float)
├── Functions
│   ├── AddItem(ItemID, Quantity) → Boolean, Remainder
│   ├── RemoveItem(ItemID, Quantity) → Boolean
│   ├── HasItem(ItemID, Quantity) → Boolean
│   ├── GetItemCount(ItemID) → Integer
│   ├── TransferItem(SlotIndex, TargetInventory, TargetSlot)
│   └── SortInventory(SortMethod)
└── Events
    ├── OnItemAdded(ItemID, Quantity)
    ├── OnItemRemoved(ItemID, Quantity)
    └── OnInventoryChanged()

Implementing Core Functions

AddItem Logic

Adding items needs to handle stacking intelligently:

AddItem(ItemID, Quantity):
  1. Look up item in Data Table
  2. If stackable (MaxStackSize > 1):
     a. Find existing stacks of this item
     b. Fill existing stacks up to MaxStackSize
     c. Create new stacks for remainder
  3. If not stackable:
     a. Find empty slots
     b. Create one slot per item
  4. Check weight limits before each addition
  5. Return success and any quantity that didn't fit

The key insight: handle partial additions gracefully. If a player picks up 50 potions but only has room for 30, add what fits and leave the rest.

Using UnrealPilot for Boilerplate

Here's where UnrealPilot shines. Instead of manually creating all these functions:

"Create an inventory component with add, remove, transfer, and sort functions. Support stackable items with max stack size from a data table. Include weight limits and events for UI updates."

UnrealPilot generates the component with all functions, proper validation, and event dispatchers. The generated code follows UE5 best practices because the AI understands Unreal Engine's conventions.

What would take 2-3 hours of Blueprint wiring takes 2 minutes. You review the generated logic, make any project-specific tweaks, and move on to the interesting parts.

UI Implementation

Inventory Grid Widget

Create a WBP_InventoryGrid widget with:

WBP_InventoryGrid
├── Uniform Grid Panel (for slot layout)
├── Slots Per Row (Integer, editable)
└── Slot Widget Class (reference to WBP_InventorySlot)

On construct, spawn slot widgets based on the inventory component's MaxSlots. Bind to OnInventoryChanged to refresh when contents change.

Individual Slot Widget

Each slot needs:

WBP_InventorySlot
├── Image (for item icon)
├── Text (for stack count)
├── Border (for selection/hover states)
└── SlotIndex (Integer) - Which inventory slot this represents

The slot widget doesn't store item data—it reads from the inventory component using its SlotIndex. This keeps the UI stateless and prevents sync issues.

Drag and Drop

UMG's built-in drag-drop system works well for inventory:

On Mouse Button Down:
  → Detect Drag if Pressed → Return Drag Drop Operation

Drag Drop Operation:
  → Payload: Slot Index + Source Inventory
  → Visual: Item icon following cursor

On Drop:
  → Get payload
  → Call TransferItem on source inventory
  → Refresh both inventories

For a polished feel, add:

  • Ghost image at original position during drag
  • Highlight valid drop targets
  • Snap-back animation on invalid drops
  • Stack splitting with modifier key (shift-drag for half stack)

Advanced Patterns

Item Categories and Filters

Add an E_ItemCategory enum and filter function:

GetItemsByCategory(Category) → Array of SlotIndices

// In UI:
Category Buttons → Filter displayed slots
Show All | Weapons | Armor | Consumables | Materials

Equipment Slots

Equipment is a separate system that references inventory:

AC_Equipment
├── EquipmentSlots (Map: E_EquipSlot → S_InventorySlot)
├── EquipItem(InventorySlotIndex, EquipSlot)
├── UnequipItem(EquipSlot)
└── GetEquippedStats() → Combined stat bonuses

When equipping, move the item from inventory to equipment slots. The original inventory slot becomes empty, and the equipment component holds the item data.

Item Actions (Context Menu)

Right-click on items should show relevant actions:

GetItemActions(SlotIndex) → Array of Actions

Actions based on ItemType:
  Consumable: Use, Drop
  Equipment: Equip, Drop, Compare
  Material: Drop
  Quest Item: Examine (no drop)

Each action is a struct with Name, Icon, and a function reference to execute.

Quick Win: UnrealPilot for UI

Creating inventory UI is repetitive. With UnrealPilot:

"Create an inventory grid widget with 6 columns. Each slot shows item icon and stack count. Support drag and drop between slots. Add hover tooltip showing item name and description."

UnrealPilot creates the widget hierarchy, sets up the grid layout, implements drag-drop operations, and wires up the tooltip. The generated widgets use proper UMG patterns—size boxes for consistent slot sizes, overlays for layered elements, proper anchoring.

You still need to style it for your game's aesthetic, but the functional foundation is complete.

Persistence: Saving and Loading

Inventory needs to persist across sessions. Two approaches:

SaveGame Object

Create a USaveGame subclass with an array of S_InventorySlot. On save, copy the inventory array. On load, restore it.

SaveInventory():
  SaveGame.InventoryData = Inventory.Slots
  Async Save Game to Slot

LoadInventory():
  Async Load Game from Slot
  Inventory.Slots = SaveGame.InventoryData
  Broadcast OnInventoryChanged

JSON Serialization

For cloud saves or server-authoritative inventory, serialize to JSON:

{
  "slots": [
    {"itemId": "Sword_Iron", "quantity": 1, "instance": {"durability": 87}},
    {"itemId": "Potion_Health", "quantity": 5, "instance": {}}
  ]
}

This format is human-readable, debuggable, and works with any backend.

Multiplayer Considerations

For networked games, inventory must be server-authoritative:

  • Server owns the data: Clients request changes, server validates and applies
  • Replicate minimal data: Only current slot contents, not full item definitions
  • Client prediction: Show immediate feedback, roll back if server rejects
  • Relevant actors: Only replicate inventory for actors the client can see

The AC_Inventory component works in multiplayer if you mark Slots as Replicated and make modification functions Server RPCs.

Performance Tips

Avoid Rebuilding UI Every Frame

The most common inventory performance issue: rebuilding the entire grid on every change. Instead:

  • Only update slots that changed
  • Pass slot index with OnInventoryChanged event
  • Cache Data Table lookups in the slot widget

Lazy Loading Icons

Don't load all item textures at startup. Load icons when slots become visible, release when scrolled away. For large inventories (100+ slots), this significantly reduces memory.

Use Object Pooling for Pickups

World items that can be picked up should use an object pool. Spawning and destroying actors constantly is expensive. Pool them, reset state on "spawn," return to pool on pickup.

Putting It Together

A complete inventory system has these pieces working together:

Player Character
├── AC_Inventory (Component)
│   └── Manages item storage
├── AC_Equipment (Component)
│   └── Manages equipped items
└── References WBP_InventoryUI

WBP_InventoryUI
├── WBP_InventoryGrid
│   └── Array of WBP_InventorySlot
├── WBP_EquipmentPanel
│   └── Specialized slots for each equip type
└── WBP_ItemTooltip
    └── Displays on hover

Each component has a single responsibility. The inventory doesn't know about UI. The UI doesn't know about game logic. They communicate through events and function calls on well-defined interfaces.

Try It With UnrealPilot

Building an inventory system from scratch takes days. The data structures, the UI widgets, the drag-drop implementation, the persistence—it adds up.

UnrealPilot handles the repetitive parts. Describe what you need, get working Blueprint logic, then customize for your specific game. The AI understands Unreal Engine's patterns, so the generated code integrates cleanly with your existing project.

Get early access to UnrealPilot →