Разработка системы инвентаря и менеджмента ресурсов в играх
Система инвентаря — один из тех компонентов, которые на ранних этапах кажутся простыми («просто список предметов»), а к середине проекта превращаются в узел, с которым завязано всё: экономика, прогрессия, крафт, торговля, сохранение. Переделывать архитектуру инвентаря на этапе контент-продакшена — одно из самых болезненных занятий в геймдеве.
Типичные архитектурные ошибки, которые закладываются с самого начала
Предмет как MonoBehaviour на сцене. Распространённая ошибка у новичков: ItemComponent на GameObject, Inventory как List<ItemComponent>. Это означает, что каждый предмет в инвентаре существует как объект Unity — нельзя сериализовать без хаков, сложно клонировать, невозможно передать по сети. Правильный путь: предмет в инвентаре — это данные, а не объект сцены.
Отсутствие разделения между ItemDefinition и ItemInstance. ItemDefinition — это ScriptableObject, описывающий тип предмета: название, иконка, базовые статы, stackable или нет, максимальный стек. ItemInstance — структура или класс с runtime-данными: количество, durability, случайные аффиксы, enchantment-ы. Один ItemDefinition может породить тысячи разных ItemInstance. Без этого разделения невозможно нормально реализовать ни модификаторы предметов, ни их генерацию.
Жёсткая привязка UI к данным инвентаря. Если InventorySlot напрямую читает Item.name и обновляет Text.text, то при любом рефакторинге данных ломается UI. Инвентарь должен работать без UI вообще — события (OnItemAdded, OnItemRemoved, OnItemChanged) публикуются через C# events или UnityEvent, UI подписывается на них снаружи.
Как строится надёжная архитектура
Ядро — InventoryContainer: класс с List<ItemSlot> где ItemSlot хранит ItemDefinition reference + ItemInstance data + int quantity. Контейнер не знает, чей он — игрока, сундука, магазина. Это позволяет использовать одну систему для всего.
ItemDatabase — ScriptableObject или адресируемый ассет с Dictionary<string, ItemDefinition> по GUID. GUID предмета — строка, не int-ID: это упрощает мёрдж при командной работе и не ломается при добавлении новых предметов.
Операции над инвентарём — методы контейнера: TryAdd(ItemDefinition, quantity), TryRemove(ItemDefinition, quantity), TryMove(fromSlot, toContainer, toSlot). Каждый метод возвращает bool или InventoryOperationResult с кодом ошибки (не хватает места, предмет не найден, слот заблокирован). Никаких void-методов для операций с данными.
Стекирование и уникальные предметы
Стекируемые ресурсы (дерево, золото, патроны) и уникальные предметы с аффиксами требуют разной логики добавления. TryAdd проверяет itemDef.isStackable — если да, ищет существующий слот с тем же ItemDefinition и дополняет до maxStackSize, остаток кладёт в новый слот. Если !isStackable — каждый экземпляр занимает отдельный слот с собственным ItemInstance.
Сортировка инвентаря — алгоритм, который переставляет слоты по категориям и внутри категории по имени. Реализуется через List.Sort() с кастомным IComparer<ItemSlot> и анимируется через coroutine с поочерёдным свапом позиций — иначе визуально неразличимо, что произошло.
Сохранение состояния инвентаря
Сериализация — отдельная задача. ItemInstance должен сериализоваться в JSON-friendly структуру: { "defGuid": "abc123", "quantity": 5, "durability": 87, "affixes": [...] }. Unity JsonUtility плохо работает с полиморфизмом — для сложных ItemInstance с наследованием лучше Newtonsoft.Json (через com.unity.nuget.newtonsoft-json) с кастомными конвертерами.
При загрузке сохранения: десериализуем список слотов, по GUID ищем ItemDefinition в ItemDatabase, восстанавливаем ItemInstance. Если ItemDefinition с таким GUID не найден (контент удалён) — слот помечается как orphaned и не вызывает краш.
Таблица ориентировочных сроков
| Масштаб | Состав | Срок |
|---|---|---|
| Минимальный | Список предметов, добавить/убрать, UI-слоты | 3–7 дней |
| Базовый | Стекирование, ItemDefinition/Instance, сохранение | 1–2 недели |
| Средний | Крафт, экипировка, drag & drop UI, фильтры | 3–5 недель |
| Полный | Генерация аффиксов, торговля, сетевая синхронизация | 2–3 месяца |
Менеджмент ресурсов: когда инвентарь — это не только предметы
В стратегиях и survival-играх ресурсы (дерево, еда, электричество) живут не в слотах инвентаря, а в ResourceSystem — глобальном или привязанном к структуре реестре с Dictionary<ResourceType, float>. Обновление происходит через Tick каждые N секунд игрового времени, а не в Update() каждый кадр.
Производители и потребители ресурсов регистрируются в ResourceSystem через интерфейс IResourceProducer / IResourceConsumer. Это позволяет добавлять новые здания без изменения ядра системы. Баланс ресурсных потоков проверяется в редакторском инструменте ещё до запуска — таблица с текущим производством и потреблением по типам обновляется в custom Editor Window через EditorApplication.update.
Процесс работы
Проектирование начинается с таблицы всех типов предметов и их свойств — до написания кода. Сколько уникальных предметов планируется? Есть ли генерируемые? Нужен ли крафт? Это определяет глубину архитектуры. Прототип InventoryContainer без UI пишется первым и покрывается юнит-тестами в Unity Test Runner — добавление, удаление, переполнение, сохранение/загрузка. UI подключается последним.





