# Hiro Inventory Sample Project

**URL:** https://heroiclabs.com/docs/sample-projects/unity/hiro-inventory/
**Summary:** Player inventory system sample project for Unity games
**Keywords:** hiro inventory, unity sdk, hiro inventory coordinator, hiro coordinator, nakama, inventory system, player progression, systems-based architecture, sample project
**Categories:** unity, sample-projects, hiro-inventory

---


## Code overview

The project uses Hiro’s systems-based architecture, which provides an opinionated structure for building games with pre-configured systems.

### Coordinator (`HiroInventoryCoordinator.cs`)

Extends the `HiroCoordinator` class to set up the Nakama connection and initialize Hiro systems. It handles system lifecycle and authentication before your game logic runs. Learn more about [Hiro's deterministic startup](../../../hiro/unity/getting-started/nakama-system/#hiro-deterministic-startup).

```csharp
protected override Task<Systems> CreateSystemsAsync()   {
    var logger = new Hiro.Unity.Logger();
    var monitor = NetworkMonitor.Default;
    var client = localHost
        ? new Client("http", "127.0.0.1", 7350, "defaultkey")
        : new Client(scheme, host, port, serverKey);
    var nakamaSystem = new NakamaSystem(logger, client, NakamaAuthorizerFunc());
    var storage = MemoryStorage.Default;

    // Register necessary Hiro Systems
    var systems = new Systems(nameof(HiroInventoryCoordinator), monitor, storage, logger);
    systems.Add(nakamaSystem);
    var inventorySystem = new InventorySystem(logger, nakamaSystem);
    systems.Add(inventorySystem);
    var economySystem = new EconomySystem(logger, nakamaSystem, EconomyStoreType.Unspecified);
    systems.Add(economySystem);
    return Task.FromResult(systems);
}
```

API reference: [Hiro Initialization](../../../hiro/unity/getting-started/nakama-system)

### Controller (`InventoryController.cs`)

Bridges the UI and Inventory system, handling codex loading, item selection, and item actions (such as granting, consuming, and removing) with validation.

**Load Item Codex**

{{< note "important" "What is the Item Codex?" >}}Hiro’s codex is the server-defined catalog of item templates—names, descriptions, categories, stack rules, max counts, properties, and icons—that the client loads to know what items exist and how they behave before granting, consuming, or displaying them.{{< /note >}}


```csharp
// clears the current codex cache, fetches all codex items from the inventory system, 
// and repopulates both the list and ID lookup dictionary with the results
  public async Task LoadItemCodex()
  {
      CodexItems.Clear();
      CodexLookup.Clear();
      var items = await _inventorySystem.GetItemCodexAsync();
      CodexItems.AddRange(items);
      foreach (var item in items)
      {
          CodexLookup[item.Id] = item;
      }
  }
```

**Grant item**

Grants a codex item by quantity after validating selection, nonzero quantity, max count, and slot availability.

```csharp
public async Task GrantItem(int codexIndex, int quantity)
  {
      if (codexIndex < 0 || codexIndex >= CodexItems.Count)
          throw new Exception("Please select a valid item.");

      if (quantity == 0)
          throw new Exception("Quantity cannot be 0.");

      var selectedItem = CodexItems[codexIndex];

      // Only validate limits when adding items (positive quantity)
      if (quantity > 0)
      {
          ValidateItemMaxCount(selectedItem, quantity);
          ValidateInventorySlots(selectedItem, quantity);
      }

      var items = new Dictionary<string, long>
      {
          { selectedItem.Id, quantity }
      };

      await _inventorySystem.GrantItemsAsync(items);
  }
```
**Consume item**

Consumes the selected consumable item by quantity (with optional overconsume), then refreshes the economy state.

```csharp
public async Task ConsumeItem(int quantity, bool overconsume)
{
    if (_selectedItem == null) return;

    if (quantity <= 0)
        throw new Exception("Quantity must be greater than 0.");

    var items = new Dictionary<string, long>
    {
        { _selectedItem.Id, quantity }
    };
    var instances = new Dictionary<string, long> {};

    await _inventorySystem.ConsumeItemsAsync(items, instances, overconsume);
    await _economySystem.RefreshAsync();

    _selectedItem = null;
}
```

**Validate item max count**

Ensures adding a quantity won’t exceed an item’s MaxCount, throwing "MAX_COUNT_REACHED" when it would.

**Validate inventory slots**

Checks inventory capacity, throwing "INVENTORY_FULL" when stack rules or slot count can’t fit the addition.

**Refresh inventory**

Refreshes inventory and wallet state, repopulates the local item list, and returns the updated items.

API reference: [Inventory](../../../hiro/unity/inventory/)