Быстрый старт
Это руководство создаёт два обычных инвентаря, между которыми можно перетаскивать предметы.
Подобное уже реализовано в первом примере и финальный результат можно посмотреть там.
Даже в базовом сценарии вам обычно нужно написать немного интеграционного кода под свои данные. В этом руководстве это будут:
ItemSOкак ваши данные предметаItemSOAdapterкак представление для системы инвентаряSimpleBindingкак мост между UI и вашим списком данных
Шаг 1. Подготовьте сцену
Инвентари — это обычный uGUI, поэтому они живут на UI-канвасе.
-
Создайте
Canvasдля инвентарей, если его ещё нет.В окне
Hierarchyкликните правой кнопкой по пустому месту и выберитеUI → Canvas.Вместе с
CanvasUnity создаст объектEventSystem. Для быстрого старта оставьте уCanvasзначениеRender Mode=Screen Space - Overlay. -
Добавьте на сцену
DragAndDropManager.Найдите в
ProjectпрефабPrefabs/DragCanvas.prefabи перетащите его вHierarchyна пустое место — он должен стать корневым объектом сцены, рядом с вашимCanvas.У
DragCanvasсвойCanvasсо значениемSorting Order = 100— за счёт этого перетаскиваемый предмет рисуется поверх остального интерфейса. КромеDragAndDropManagerна префабе лежатInputEventRouterиDragVisualPresenter. На сцене нужен ровно один такой объект: он управляет всеми операциями переноса и отвечает за визуал предмета «в руке». -
Проверьте
EventSystem.Если в
Hierarchyего нет, кликните правой кнопкой по пустому месту и выберитеUI → Event System.Выделите
EventSystemи посмотрите вInspector, какой модуль ввода на нём стоит:- проект на legacy input — нужен
StandaloneInputModule; - проект на New Input System — нужен
InputSystemUIInputModule.
Ассет поддерживает оба варианта. В
DragAndDropManagerпо умолчанию указан минимальный рабочий конфиг действий: можете поправить его под себя или создать свою копию, не забыв заменить её в сцене. - проект на legacy input — нужен
Шаг 2. Создайте два инвентаря
-
В
Hierarchyкликните правой кнопкой по объектуCanvasи выберитеCreate Empty. Назовите новый объектBackpack.Так как родитель —
Canvas, Unity сама даст объектуRectTransform. Если сразу нужен фон, вместоCreate EmptyвыберитеUI → Panel. -
Задайте
Backpackразмер и положение.У UI-объекта вместо
TransformстоитRectTransform, и послеCreate EmptyегоWidthиHeightравны нулю. Размер задаётся здесь, а не масштабом:- выделите
Backpackи вInspectorв компонентеRectTransformукажитеWidth=540,Height=240; - положение удобно задать кнопкой пресетов якорей — это квадрат с крестом в левом
верхнем углу
RectTransform. Нажмите его и для быстрого старта выберитеmiddle center, затем сдвиньте объект полямиPos XиPos Y.
Если вы делали
UI → Panel, размер уже растянут на весь экран — тогда просто поставьте нужныеWidthиHeightтем же способом. - выделите
-
Кликните правой кнопкой по
Backpack, выберитеCreate Emptyи назовите объектSlotContainer.Растяните его на весь
Backpack: нажмите кнопку пресетов якорей и с зажатымиAltиShiftвыберите правый нижний вариантstretch / stretch.Затем нажмите
Add Component → Layout → Grid Layout Groupи заполните его поля:Поле Grid Layout GroupЗначение Cell Size100×100— размер слота изPrefabs/Slot.prefabSpacing5по X и YConstraintFixed Column CountConstraint Count5— тогда 10 слотов лягут сеткой 5 × 2Именно этот компонент раскладывает слоты сеткой.
-
Добавьте на
BackpackкомпонентUniversalInventoryи заполните поля.В инспекторе они разложены по сворачиваемым группам:
Группа
Slot Setup:Поле Значение Slot Containerобъект SlotContainerиз шага 3 — родитель, внутри которого создаются слотыSlot Prefabпрефаб слота, для старта подойдёт Prefabs/Slot.prefabInitial Slot Countнапример 10. При запуске столько слотов будет создано вSlot Container. Если вы создали их сами, их можно закэшировать кнопкойCache SlotsГруппа
Strategy— здесь два поля без подписи, каждое выбирается выпадающим списком:Что выбрать Значение верхний список (стратегия инвентаря) UniqueItemStrategyдля самого простого старта, чтобы каждый предмет лежал в отдельном слотенижний список (управление слотами) FixedSlotManagementSettingsдля фиксированного количества слотовОстальные группы (
Rules,Drop Policy,Placement) для быстрого старта можно не трогать. -
Скопируйте
Backpackи сделайте второй инвентарь, напримерChest. Разведите их по экрану полемPos X, чтобы оба были видны одновременно.
Info
Инвентарь на старте пробует найти уже созданные слоты, дочерние к Slot Container, и
закэшировать их для управления. Если их оказалось недостаточно, он создаст слоты из
Slot Prefab до количества Initial Slot Count. Вы также можете создать все слоты вручную
в режиме редактирования и закэшировать их кнопкой Cache Slots, чтобы этой операции не
происходило во время игры.
Шаг 3. Опишите тип предмета
Допустим у вас есть тип предметов
[CreateAssetMenu(menuName = "Game/Item")]
public class ItemSO : ScriptableObject
{
[field: SerializeField] public string ItemName { get; private set; }
[field: SerializeField] public Sprite Icon { get; private set; }
}
IItemAdapter, например:
using UDND.Core;
using UnityEngine;
public class ItemSOAdapter : IItemAdapter
{
// ссылка на данные для вашего типа
public readonly ItemSO Data;
// конструктор
public ItemSOAdapter(ItemSO data) => Data = data;
// обязательные поля из интерфейса
public string ItemId => Data.GetInstanceID().ToString();
public Sprite Icon => Data.Icon;
public string DisplayName => Data.ItemName;
}
Значения обязательных полей:
ItemId- Нужен чтобы различать, можно ли объединить предметы в один слот, для стратегийStackableиSeparableStacks.Icon- Нужно чтобы отображать картинку предмета в слотеDisplayName- Используется в основном в логах и некоторых дополнительных системах вроде примера подсказок. Можно оставить равным пустой строке
Шаг 4. Добавьте простой binding
Чтобы отобразить ваши данные в слотах нужно системе показать эти данные и дать возможности взаимодействия с ними. Обычно это будут наборы данных хранящиеся где то в ваших скриптах игрока, персонажей и объектов. Тут в примере будет просто список в самом этом компоненте
using UDND.Core;
using UDND.DataBinding;
using System.Collections.Generic;
using UnityEngine;
public class SimpleBinding : ListInventoryDataBinding<ItemSO, ItemSOAdapter>
{
// Ваш список данных
[SerializeField] private List<ItemSO> _items;
// обязательные функции для переопределения
protected override IReadOnlyList<ItemSO> GetItems() => _items;
protected override ItemSOAdapter CreateAdapter(ItemSO item) => new(item);
protected override void AddToData(ItemSOAdapter adapter) => _items.Add(adapter.Data);
protected override void RemoveFromData(ItemSOAdapter adapter) => _items.Remove(adapter.Data);
}
ListInventoryDataBinding это специальный шаблон DataBinding для работы с данными, хранящимися в виде списка. У него при наследовании указывается тип вашего предмета и адаптера для него.
Значения обязательных методов:
GetItems- Используется для первичного рендера данныхCreateAdapter- Нужен для создания адаптера и передачи ему необходимых параметров о ваших данныхAddToData- вызывается при переносе предмета в этот инвентарьRemoveFromData- вызывается при выносе предмета из этого инвентаря
Повесьте такой binding на оба инвентаря или другие объекты в сцене.
Укажите ссылки на соответствующие инвентари.
У каждого будет свой список _items.
Info
В ваших проектах вы скорее всего будете вместо простого списка _items указывать взаимодействие с вашими скриптами в которых хранятся ваши данные. Подробнее как подобное реализовано можно посмотреть в примерах.
Шаг 5. Заполните стартовые данные
- Создайте несколько
ItemSO. - Добавьте их в списки
_itemsу вашихSimpleBinding. - Запустите сцену.
Если всё настроено правильно:
- оба инвентаря покажут предметы
- предмет можно перетащить из одного инвентаря в другой
- списки данных обновятся автоматически при переносе
Что происходит под капотом
flowchart LR
A["Игрок тащит предмет"] --> B["<b>UniversalInventory</b> обрабатывает перенос"]
B --> C["В <b>DataBinding</b> вызываются методы"]
C --> D["Ваш List<ItemSO> обновляется"]
Частые ошибки в первом проекте
- инвентарь создан как 2D- или обычный объект сцены, а не как UI внутри
Canvas: в инспекторе у негоTransformвместоRectTransform - у
BackpackилиSlotContainerвRectTransformостались нулевыеWidthиHeight, поэтому инвентарь не видно или он стоит не там, где ожидалось - размер пытались задать через
ScaleвместоWidthиHeight DragCanvasположили внутрь своегоCanvas, и перетаскиваемый предмет пропадает под интерфейсомSlot Containerуказывает на сам инвентарь, а не на дочерний объект сGrid Layout GroupItemIdне соответствует вашей логике stacking, и предметы начинают merge-иться или не merge-иться неожиданно- на сцене нет
DragAndDropManager - на сцене нет
EventSystem - проект использует legacy input, но на
EventSystemнетStandaloneInputModule - binding привязан не к тому
UniversalInventory - у
InventoryDataBindingне назначена ссылка на inventory - данные меняются вне pipeline, но
ReloadUI()не вызывается. Добавьте в вашем DataBinding вызов ReloadUI по событию изменения данных
См. также:
- Примеры — если хотите выбрать из всех 6 демо
- Привязка данных — если нужно понять lifecycle и точки расширения
- Стратегии размещения — если хотите добавить свою стратегию или свой режим slot management
- Troubleshooting — если базовая сцена не завелась с первого раза