Design Diary: Tile-Based Inventory System #2


Building a Dynamic Tile-Based Inventory System

Once the design parameters and basic functionality was defined I started to create a basic outline of how the system should work.

Core Structure: Grids, Slots and Items

The foundation of the inventory system is built around three main components: ItemGrid, InventoryManager, and ItemObject linked to ItemData. Each piece handles a specific aspect of the system, keeping the logic modular, flexible, and reusable.

  • ItemGrid: represents the 2D array of inventory slots and handles placement logic.
  • InventoryManager: oversees global state, current held items, and saving/loading.
  • ItemObject & ItemData: individual items, with data stored in ScriptableObjects for flexibility and reusability.

ItemGrid

The ItemGrid represents the 2D array of inventory slots and handles, placement logic for items of varying size and shape, highlighting valid and invalid positions, resizing to scale dynamically with the UI, optional restriction of allowed cells for certain gameplay interactions



Each ItemGrid tracks items in a 2D array, making it straightforward to check collisions, rotation placement, or grid availability.

Key Feature: Fixed or restricted grid cells

One of the design goals for the inventory was to support specialized slots—areas of the inventory that only accept certain items. This feature allows the game to:

  • Encourage strategic placement rather than free-for-all stacking
  • Trigger in-game events when an item is correctly placed
  • Represent functional or thematic constraints, e.g., a “tool slot” or a “machine interface”

The system uses two main concepts:

  1. UseRestrictedCells: a boolean on the ItemGrid indicating whether the grid enforces restrictions.
  2. AllowedCells: a List<Vector2Int> representing which cells are valid for restricted items.

When UseRestrictedCells is enabled, the grid logic checks the placement of an item against AllowedCells. Only items with a compatible shape can occupy the allowed positions. Attempting to place an item outside of these cells triggers feedback (e.g., red highlighting) and prevents placement.

This approach allows flexible slot shapes, not just single tiles, enabling objects that occupy multiple slots to still be checked against valid positions. Highlights help players understand which positions are valid, maintaining a clear visual language. Restriction logic is optional, so the same grid system works for freeform inventory sections.

Key Feature: Dynamic sizing to scale with UI

A major usability goal for the inventory was adaptability: it needed to look good and remain functional across different screen resolutions and aspect ratios. This meant the grid, slots, and items should scale dynamically with the UI, rather than relying on fixed pixel dimensions.

The ItemGrid uses Unity’s GridLayoutGroup combined with runtime calculations to determine the optimal cell size based on the available parent RectTransform. To implement this I did the following:

  1. Measure the total available width and height of the parent panel
  2. Subtract padding and spacing to determine usable space
  3. Calculate cell size that fits the grid without stretching items disproportionately
  4. Update GridLayoutGroup.cellSize and force a layout rebuild

This approach ensures readability and usability even on small screens or when the UI layout changes dynamically. It avoids hardcoding cell sizes, giving the inventory flexibility to adapt to future changes in layout or design.Works seamlessly with restricted cells and multi-tile items because rotatedShape calculations remain relative to the dynamic cell size.

Key Feature: highlighting for valid/invalid placements

A crucial part of making the inventory feel tactile and intuitive was giving the player clear visual feedback about where items could, or couldn’t, be placed. This ensures players understand constraints like restricted slots, item shapes, or occupied spaces without needing trial-and-error.

Each inventory slot has an attached OnHoverHighlight component. When an item is held:

  1. The ItemObject queries the ItemGrid for the proposed placement based on the mouse position and the item’s rotatedShape.
  2. The grid evaluates the placement:
    1. 1 (Valid): All relevant slots are free and within bounds.
    2. 0 (Invalid): Collides with existing items or restricted cells.
    3. 2 (Out of Bounds): Placement extends outside the grid
  3. The slots under the item are highlighted differently depending on the outcome

Immediate visual feedback reduces player frustration by clearly showing valid placements. Works seamlessly with rotated items and multi-tile objects since rotatedShape is applied dynamically. Integrates with restricted or interactable slots, ensuring feedback remains consistent even for complex grid rules. Highlights de-highlight when the item is dropped or moved away, keeping the UI clean.


ItemObject & ItemData

At the heart of the inventory system is the separation of data and presentation. Each held or placed item is represented by an ItemObject component in the scene, while its core data (name, sprite, description, shape, rotation) is stored in a ScriptableObject called ItemData.

  • ItemObject is responsible for visuals, input, and placement logic. 
  • ItemData stores persistent, reusable item properties.

This separation allows multiple items to share the same data, and makes the system easy to extend or tweak. 


Keeps gameplay logic decoupled from visuals, allowing easier testing and iteration. Supports rotation and multi-tile items cleanly. Simplifies saving/loading inventory state, as ItemData is serializable.

Key Feature: ScriptableObject and ItemObject Separation

The main basis for the items its the ItemData ScriptableObject which stores persistent properties like name, sprite, description, size rotation, and shape tiles.


Keeps data decoupled from scene logic, enabling clean reuse of items across grids and scenes. Supports runtime modifications (e.g., rotating items) without altering original data. Simplifies persistent inventory saving because the system only needs to reference which ItemData is at each slot and its rotation. Makes future expansions (like crafting, upgrades, or interactive events) much easier, since ItemData can carry additional metadata without changing the visual logic.

Key Feature: List<Vector2Int>

To represent the physical footprint of each item on the inventory grid, I used a Vector2Int[] array (or List<Vector2Int>) stored in ItemData. Each Vector2Int corresponds to a tile offset relative to the item’s origin point, which is the top-left corner by default.

  • Each item has a starting grid position (x, y) corresponding to the top-left of its bounding box.
  • The shape tiles define all the cells the item occupies relative to that origin. 
  • During placement or rotation, the system iterates through the list to check for available slots:


Using a list of offsets makes arbitrary shapes easy to define (L-shaped, 2x3 blocks, etc.). Keeps the placement system scalable, allowing multi-tile items without adding extra complexity to the grid logic. Integrates seamlessly with restricted cells and highlighting, because the system can check each offset independently for validity. Ensures rotation is data-driven, so the visual rotation always matches the underlying tile occupancy.

InventoryManager

The InventoryManager serves as the brain of the inventory system, managing global state, persistence, and the active grid. It keeps track of what the player is holding, what grid is currently active, and all saved items.

Key Feature: Current Held Item

The current Held Item is a reference to the item that the player is currently moving and is present on the dragcanvas. When an item is picked up:

  • It is removed from its current grid.
  • Parent is set to a dedicated Drag Canvas to remove collisions with the grid. 
  • The system highlights valid placement slots in real time.
  • When the item is placed or dropped, the manager clears the held reference and updates the appropriate grid.

By having a singleton behaviour on the InventoryManager this means that the reference is centralised, ensuring only one item is manipulated at a time, preventing conflicts or overlapping interactions across multiple grids.

Key Feature: Current Grid

The current grid reference tracks the active inventory grid, allowing seamless grid switching for multiple inventories (e.g. player inventory to chest inventory or vice versa). It also ensures that all placement, highlighting and availability checks refence the currentGrid, ensuring there is visual continuity. The current grid is set via a mouse over action:


By centralizing the current grid, the system avoids duplicating logic across different inventories, and makes extending the game with new grids or puzzles straightforward.

Key Feature: SavedItems dictionary

The saveditem dictionary was a later addition that was created to ensure that the player's inventory remained constant when switching between scenes in unity. It stores the state of all items in a grid, keyed by Vector2Int positions, with each SavedItemData including an itemData, gridPosition, and rotation. This enables persistence across the different scenes by restoring inventory after scene loads and tracking which items are placed and their orientations.


This dictionary decouples data from visual objects, making it easy to reload inventories without relying on scene objects being present. It also supports future features like saving/loading, undoing moves, or interacting with multiple grids simultaneously.

Leave a comment

Log in with itch.io to leave a comment.