Від ассета до руки гравця¶
🇬🇧 English | 🇺🇦 Українська
Це не довідник — це одна коротка історія. Мета: за 10 хвилин побачити на власні очі,
як предмет проходить весь шлях від Data Asset до меча в руці персонажа, не написавши
самим жодного рядка коду інвентаря. Усі приклади нижче вже лежать у самому плагіні
(Plugins/InventorySystem/Content/Demo/Items/), тож можна відкрити редактор і
повторити кожен крок буквально — без жодного власного ассета.
Якщо після цієї сторінки захочеться зрозуміти «а чому саме так» — розділи в кінці розкривають кожен крок докладно.
Дійові особи¶
Сім предметів уже створені в Plugins/InventorySystem/Content/Demo/Items/ — разом
вони показують кожне поєднання готових фрагментів (що таке фрагмент і чому предмет —
це набір фрагментів, а не клас — 01 — Основні поняття):
| Ассет | Що це | З чого зроблений |
|---|---|---|
DA_IronSword |
Залізний меч | Equippable + Durability |
DA_WoodenShield |
Дерев'яний щит | Equippable (інша рука) + Durability |
DA_KnightHelmet |
Лицарський шолом | Equippable (голова) + Durability — зношується не від використання |
DA_Torch |
Смолоскип | Equippable + Durability + Consumable — усі три одразу |
DA_HealthPotion |
Зілля здоров'я | Stackable + Consumable |
DA_CopperOre |
Мідна руда | Stackable + Weight |
DA_AncientRelic |
Стародавня реліквія | без фрагментів узагалі |
Жоден із них не має власного класу — ні C++, ні Blueprint. Це просто дані на ассеті. Саме тому всю різницю в поведінці між мечем, щитом, шоломом, факелом, зіллям, рудою і реліквією видно нижче в кількох рядках на предмет, а не в семи окремих класах.
Крок 0 — персонажу потрібен рюкзак¶
Перш ніж щось підбирати, у гравця має існувати компонент Inventory (і, якщо
плануєте щось вдягати — Equipment). Найшвидший спосіб для першого разу не вимагає
жодного коду й жодної правки Blueprint персонажа:
Project Settings → Game → Inventory System:
| Налаштування | Значення для цієї історії |
|---|---|
Auto Create Player Inventory |
✅ |
Auto Create Player Equipment |
✅ |
Default Equipment Slots |
Equipment.Slot.Weapon.Main |
Готово. Плагін сам створить обидва компоненти на PlayerState кожного гравця, щойно
той з'явиться в грі. Для постійного проєкту компонент зазвичай додають вручну на
конкретного персонажа — 02 — Підключення пояснює, чому автостворення
вимкнене за замовчуванням і коли вмикати його свідомо.
Крок 1 — меч з'являється у світі¶
Кладемо в рівень актора AISItemPickup і в полі Item Definition вказуємо
DA_IronSword. Усе — окремий Blueprint для «меча, що лежить на землі» не потрібен:
той самий клас Item Pickup обслуговує будь-який предмет у грі.
Меш, який побачить гравець, плагін бере з поля PickupMesh самого DA_IronSword
(категорія Item|World у Details) — автоматично, при старті гри. Якщо PickupMesh
не задано, актор усе одно працює (його можна підібрати), просто лишається невидимим
на землі. Саме тому рядок pickup mesh: yes / no у дебаг-панелі (InvOverlay)
варто перевіряти для щойно створеного предмета ще до того, як класти його у світ —
він прямо каже, побачить гравець щось на землі, чи ні.
Крок 2 — гравець підбирає меч¶
Взаємодія (клавіша, трейс під прицілом, тригер — це вже логіка вашого проєкту) викликає один метод — на інвентарі гравця, не на пікапі:
Inventory->TryCollect(Pickup);
Чому саме так, а не Pickup->TryCollect(...) напряму: у пікапа немає власного
мережевого з'єднання (його ж ніхто не «підібрав» ще), тож переслати запит клієнта
на сервер він не може. Інвентар гравця з'єднання має, тож запит їде через нього —
той самий Try*, що й будь-де в плагіні, викликається звідки завгодно і сам
розбирається, де він насправді має виконатись. Розробнику думати про це не треба.
Метод сам знаходить, куди класти, і кладе предмет у тому самому стані, у якому той лежав на землі (якщо меч підняли пошкодженим, а не щойно розміщеним, пошкодження збережеться). Пікап знищується (типова поведінка), меч тепер сидить у слоті інвентаря.
Перевірити без жодного рядка коду: відкрийте InvOverlay (клавіша I або
однойменна консольна команда), у полі GIVE ITEM введіть iron, натисніть Give —
і той самий DA_IronSword опиниться в слоті 0. Це рівно та сама дія, яку в грі
виконує TryCollect: панель просто дає натиснути її мишкою, а не бігти через увесь
рівень заради перевірки.
Крок 3 — меч опиняється в руці¶
Ось і вся магія — і в ній рівно нуль рядків коду з вашого боку. У DA_IronSword, на
фрагменті Equippable, заповнені два поля (категорія Equippable|Visuals):
| Поле | Значення в прикладі |
|---|---|
EquippedActorClass |
клас актора-меча (Blueprint зі статичним мешем) |
AttachSocketName |
hand_rSocket (чи будь-який сокет вашого скелета) |
Викликаєте — з кнопки UI, з гарячої клавіші, звідки завгодно:
Equipment->TryEquipFromInventorySlot(0);
...і компонент спорядження сам створює EquippedActorClass та кріпить його до
названого сокета персонажа — окремо на кожній машині. Це не реплікується, тож меч у
руці не коштує жодного мережевого трафіка понад сам факт «цей слот зайнятий».
Якщо сокет не задано — актор кріпиться до кореня персонажа, а не зникає; на голому
DefaultPawn без скелетного меша так і станеться, і для перевірки логіки це цілком
нормально.
Той самий крок без коду: у InvOverlay, на рядку меча в CONTENTS — кнопка
Equip. Клік — і той самий TryEquipFromInventorySlot спрацьовує від натискання
мишкою; на панелі EQUIPMENT одразу з'являється зайнятий слот Weapon.Main.
Якщо
EquippedActorClassлишити порожнім, предмет усе одно вдягається (слот зайнятий,GetTotalStatValueрахує його бонуси) — просто нічого не з'являється в руці. Корисно для персня, що лише дає статистику, або для броні, вже запеченої в меш персонажа.
І щит в іншу руку — два спорядженних предмети одразу¶
DA_WoodenShield — той самий рецепт, що й меч (Equippable + Durability), тільки
EquipmentSlot вказує на Equipment.Slot.Weapon.Offhand замість Weapon.Main. Той
самий виклик, інший слот:
Equipment->TryEquipFromInventorySlot(1); // якщо щит лежить у слоті 1
Тепер персонаж тримає обидва предмети одночасно — по одному в кожній руці, кожен
кріпиться до свого сокета незалежно. Жодної особливої обробки «пари зброя-щит» у
плагіні немає: Weapon.Main і Weapon.Offhand — просто два різні записи в
AvailableSlots компонента спорядження, і кожен обробляється так само, як єдиний
слот у прикладі з мечем.
Без коду: кнопка Equip на рядку щита в InvOverlay — так само, як і для
меча.
І шолом на голову — та сама Durability, інша логіка зносу¶
DA_KnightHelmet — третій одночасно вдягнений предмет (Equipment.Slot.Armor.Head,
третій незалежний слот поруч із двома руками), і на перший погляд той самий рецепт:
Equippable + Durability. Різниця — в одному числі.
У меча й щита DurabilityPerUse більше нуля: прочність тратиться, коли предмет
явно використовують. У шолома DurabilityPerUse = 0 — прочність не тратиться
сама по собі взагалі. Це не недогляд, а задокументована поведінка: броню ніхто не
«використовує» кліком, вона зношується від того, що персонажа б'ють. Тому бойовий
код проєкту сам списує її напряму зі самого екземпляра, а не через фрагмент:
HelmetInstance->ModifyStatValue(ISTags::Stat_Durability, -DamageTaken);
(Durability->Repair() тут не підійде — він лише відновлює прочність, і
від'ємне значення для нього означає «відремонтувати повністю», а не «зняти». Для
стороннього пошкодження — саме ModifyStatValue на екземплярі, з від'ємною дельтою.)
...коли персонаж отримує удар — а не покладається на TryUseItem, який для брони
взагалі не має сенсу. MaxDurability = 60 тут така сама цифра, як і в меча чи
щита, — рахуються лише «скільки ударів витримає», а не «скільки разів клікнули».
Без коду: кнопка Equip на рядку шолома в InvOverlay, як і для решти.
Кнопки Use там не буде — фрагмент Consumable шолому не потрібен, і без нього
IsUsable() для броні так само повертає false, як і для мідної руди.
А тепер факел — усі три фрагменти одразу¶
DA_Torch має той самий Equippable, що й меч (з'являється в руці так само), плюс
Durability (запас «палива») і Consumable (витрачається під час використання).
Жодного окремого коду для цього не знадобилося — просто третій запис у масиві
фрагментів.
Inventory->TryUseItem(SlotIndex, Instigator);
Той самий виклик, яким користується будь-який предмет із кнопкою «Use»: фрагменти самі вирішують, що це означає для конкретно факела (списати заряд, зменшити запас пального). Сам інвентар про факел спеціально нічого не знає.
Без коду: кнопка Use на рядку факела в InvOverlay.
І зілля — без вдягання взагалі¶
DA_HealthPotion (Stackable + Consumable) ніколи не потрапляє в EQUIPMENT — у
нього немає фрагмента Equippable, тож дебаг-панель навіть не покаже для нього
кнопку Equip (перевірка «чи є фрагмент» ховає її автоматично, для будь-якого
предмета). Використання — той самий TryUseItem, тільки цього разу стек
зменшується на одиницю замість очікування на знос.
І мідна руда — вантаж без жодної дії¶
DA_CopperOre (Stackable + Weight) взагалі не має кнопки Use — у неї немає
Consumable, тож IsUsable() повертає false, і дебаг-панель ховає кнопку так само,
як ховала Equip для зілля. Єдине, що ця руда робить — займає місце в стеку
(до 99 копій в одному слоті) і додає вагу до GetCurrentWeight() інвентаря.
Це навмисно нудний приклад: не кожен предмет мусить щось робити. Крафтовий матеріал, валюта, сировина — усе це законні предмети з порожньою поведінкою, і плагін не змушує вигадувати їм фальшиве «використання».
І стародавня реліквія — предмет узагалі без жодного фрагмента¶
DA_AncientRelic — це та сама «Квестовий лист» із таблиці в 01 — Основні
поняття, тільки з іншою назвою. У масиві Fragments — нуль
записів. Практично це означає:
- не стакується —
GetMaxStackSize()без фрагментаStackableзавжди повертає 1; - не вдягається — кнопка Equip не з'явиться, як і для руди чи зілля;
- нічого не робить при Use — кнопки Use також не буде.
У дебаг-панелі це видно прямим текстом: рядок Fragments для цього предмета
покаже none, а не порожнє місце. Це не зламаний предмет — це найпростіший
можливий предмет, і система для нього не робить винятку: квестовий предмет,
ключ, прапорець сюжету — усе це ISItemDefinition без жодного фрагмента, і
більше нічого не потрібно.
Що ми щойно побачили¶
Жоден із семи прикладів не додав жодного рядка в код інвентаря чи спорядження.
Уся різниця між мечем, щитом, шоломом, факелом, зіллям, рудою і реліквією — це
просто різний набір фрагментів (і різні значення на них — шолом і меч мають
однакові фрагменти, але різну поведінку через одне число, DurabilityPerUse)
на одному й тому самому класі ISItemDefinition, від трьох фрагментів одразу до
нуля. Це і є центральна ідея плагіна: 01 — Основні поняття
розкриває її докладно, з тією самою таблицею прикладів.
Куди далі¶
| Хочете | Читайте |
|---|---|
| Створити власний предмет | 03 — Створення предметів |
| Зрозуміти кожен фрагмент і хуки | 04 — Фрагменти |
| Побудувати UI підбирання й спорядження | 08 — Побудова інтерфейсу |
| Класти предмети й скрині у рівень | 07 — Лут і предмети у світі |
Усе про InvOverlay і консольні команди |
13 — Відлагодження |