Posted October 14, 2025 by NimueS
Once the design parameters and basic functionality was defined I started to create a basic outline of how the system should work.
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.
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.
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:
Represent functional or thematic constraints, e.g., a “tool slot” or a “machine interface”
The system uses two main concepts:
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.
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:
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.
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:
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.
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.
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.
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.
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.
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.
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:
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.
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.
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.