Null Bolt
Studio
guides

Grid inventories

InventoryController owns a 2D grid of cells, places multi-cell items into it, raises events, and exposes virtual gates for your game rules.

Inspector setup

FieldTypeDescription
Grid Width / HeightintGrid dimensions in cells (default 20×8). A performance warning appears past 1000 cells.
Grid UIGridUIRequired reference to the visual grid. The controller refuses to initialize without it — the custom inspector offers a one-click auto-fix.
Inventory TypeenumPlayer, Shop, or Stash. Drives trade behavior and the save subfolder.
Inventory IdstringStable identity for transfers/tooling; defaults to "{GameObject name}_{type}" when blank.
Allow Direct Item Placement / RemovalboolMaster switches for putting items in or taking them out (a shop display case would disable removal).
Auto Save / Intervalbool / floatTimed background saves to a dedicated autosave file (default off / every 30s).
Starter ItemsList<ItemData>Placed once, on first initialize. Items that don’t fit are skipped with a warning.
Dragging a multi-cell item across a grid with a green validity preview
Dragging shows a live green/red placement preview; invalid drops snap back with a red flash.

Core operations

The calls you'll actually use
C#
using InventorySystem;

// Add — auto-placed, or at an exact cell
inventory.AddItem(potion);
inventory.AddItem(sword, new Vector2Int(3, 0), rotated: true);

// Query
ItemInstance hit = inventory.GetItemAt(new Vector2Int(3, 0));
bool fits = inventory.CanPlaceItem(shield, new Vector2Int(6, 2));

// Move / rotate / remove by instance ID
inventory.MoveItem(hit.InstanceID, new Vector2Int(0, 4));
inventory.RotateItem(hit.InstanceID);
inventory.RemoveItem(hit.InstanceID);

// Split 3 units off a stack onto a specific cell
inventory.SplitStack(stack.InstanceID, 3, new Vector2Int(9, 1));

// Auto-sort the whole grid into a corner
inventory.OrganizeTopLeft();          // also TopRight / BottomLeft / BottomRight

// Resize (destructive — existing placements are cleared)
inventory.ResizeGrid(12, 6);
// note
Methods taking ItemData throw ArgumentNullException on null rather than returning false — an early crash beats a silent no-op while you're integrating.

Events

EventTypeDescription
OnItemAddedAction<ItemInstance>An item landed in this inventory (placement, load, transfer in).
OnItemRemovedAction<ItemInstance>An item left this inventory.
OnInventoryChangedActionAnything changed — add, remove, move, merge, resize, load. The cheap "refresh my UI" hook.

Per-instance changes (stack count, rotation, condition, renames) raise ItemInstance.OnInstanceChanged. Cross-inventory moves raise the static InventoryTransfer events covered in Shops, stash & economy.

Permission gates — your game rules

Every mutating operation funnels through three virtual methods. Out of the box nothing is gated (beyond the two Allow Direct… toggles); override them for weight limits, quest locks, category restrictions, or level requirements:

WeightLimitedInventory.cs
C#
using InventorySystem;
using UnityEngine;

public class WeightLimitedInventory : InventoryController
{
    [SerializeField] private float maxWeight = 50f;
    private float currentWeight;   // maintain via OnItemAdded/OnItemRemoved

    public override bool CanAddItem(ItemData data, Vector2Int? pos = null)
    {
        if (currentWeight + WeightOf(data) > maxWeight) return false;
        return base.CanAddItem(data, pos);   // keep space/permission checks
    }

    public override bool CanPerformAction(InventoryAction act, ItemData data = null)
    {
        // e.g. lock rotation during combat:
        if (act == InventoryAction.RotateItem && Combat.Active) return false;
        return base.CanPerformAction(act, data);
    }

    private float WeightOf(ItemData d) => d.Width * d.Height * 0.5f;
}

The drag-and-drop UI consults the same gates, so a rule you write once applies to player drags and code calls alike. AddItem, MoveItem, RemoveItem, and RotateItem are also virtual if you need to intercept the operations themselves.

Slot inventories — the non-grid sibling

SlotInventoryController + SlotInventoryUI give you a Minecraft-style fixed slot array instead of a spatial grid: one item per slot, addressed by index, no footprints or rotation. Stackables auto-merge into the lowest matching stack. It adds one event the grid doesn't have — OnItemMoved(item, oldIndex, newIndex) — and is the base class of the hotbar.

Slot API in one glance
C#
slotInventory.AddItem(potion);            // first free / merging slot
slotInventory.AddItemAt(potion, 4, 3);    // slot 4, stack of 3
slotInventory.MoveItem(id, 7);            // move | merge | swap
ItemInstance i = slotInventory.GetItemAt(2);

Both controllers implement IInventory, so transfers, drag-and-drop, and save/load treat them interchangeably. Use grids where geometry is the gameplay; use slots for hotbars, crafting inputs, and chest-style storage.

One-line global access

For prototypes, the static InventoryAPI wraps the first controller in the scene: InventoryAPI.AddItem(data), RemoveItem, SplitStack, Save(), Load(). Call InventoryAPI.ClearCache() if the scene's controller changes. For anything real, hold a serialized reference instead.