Skip to content

Від ассета до руки гравця

🇬🇧 English | 🇺🇦 Українська

Це не довідник — це одна коротка історія. Мета: за 10 хвилин побачити на власні очі, як предмет проходить весь шлях від Data Asset до меча в руці персонажа, не написавши самим жодного рядка коду інвентаря. Усі приклади нижче вже лежать у самому плагіні (Plugins/InventorySystemDemo/Content/Demo/Items/), тож можна відкрити редактор і повторити кожен крок буквально — без жодного власного ассета.

Якщо після цієї сторінки захочеться зрозуміти «а чому саме так» — розділи в кінці розкривають кожен крок докладно.


Дійові особи

Сім предметів уже створені в Plugins/InventorySystemDemo/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 — меч з'являється у світі

Кладемо в рівень актора AISDemoItemPickup і в полі Item Definition вказуємо DA_IronSword. Усе — окремий Blueprint для «меча, що лежить на землі» не потрібен: той самий клас Item Pickup обслуговує будь-який предмет у грі.

Меш, який побачить гравець, плагін бере з поля PickupMesh самого DA_IronSword (категорія Item|World у Details) — автоматично, при старті гри. Якщо PickupMesh не задано, актор усе одно працює (його можна підібрати), просто лишається невидимим на землі. Саме тому рядок pickup mesh: yes / no у дебаг-панелі (DemoInvOverlay) варто перевіряти для щойно створеного предмета ще до того, як класти його у світ — він прямо каже, побачить гравець щось на землі, чи ні.


Крок 2 — гравець підбирає меч

Взаємодія (клавіша, трейс під прицілом, тригер — це вже логіка вашого проєкту) викликає один метод — на інвентарі гравця, не на пікапі:

Inventory->TryCollect(Pickup);

Чому саме так, а не Pickup->TryCollect(...) напряму: у пікапа немає власного мережевого з'єднання (його ж ніхто не «підібрав» ще), тож переслати запит клієнта на сервер він не може. Інвентар гравця з'єднання має, тож запит їде через нього — той самий Try*, що й будь-де в плагіні, викликається звідки завгодно і сам розбирається, де він насправді має виконатись. Розробнику думати про це не треба.

Метод сам знаходить, куди класти, і кладе предмет у тому самому стані, у якому той лежав на землі (якщо меч підняли пошкодженим, а не щойно розміщеним, пошкодження збережеться). Пікап знищується (типова поведінка), меч тепер сидить у слоті інвентаря.

Перевірити без жодного рядка коду: відкрийте DemoInvOverlay (клавіша 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 без скелетного меша так і станеться, і для перевірки логіки це цілком нормально.

Той самий крок без коду: у DemoInvOverlay, на рядку меча в 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 на рядку щита в DemoInvOverlay — так само, як і для меча.


І шолом на голову — та сама Durability, інша логіка зносу

DA_KnightHelmet — третій одночасно вдягнений предмет (Equipment.Slot.Armor.Head, третій незалежний слот поруч із двома руками), і на перший погляд той самий рецепт: Equippable + Durability. Різниця — в одному числі.

У меча й щита DurabilityPerUse більше нуля: прочність тратиться, коли предмет явно використовують. У шолома DurabilityPerUse = 0 — прочність не тратиться сама по собі взагалі. Це не недогляд, а задокументована поведінка: броню ніхто не «використовує» кліком, вона зношується від того, що персонажа б'ють. Тому бойовий код проєкту сам списує її напряму зі самого екземпляра, а не через фрагмент:

HelmetInstance->ModifyStatValue(ISDemoTags::Stat_Durability, -DamageTaken);

(Durability->Repair() тут не підійде — він лише відновлює прочність, і від'ємне значення для нього означає «відремонтувати повністю», а не «зняти». Для стороннього пошкодження — саме ModifyStatValue на екземплярі, з від'ємною дельтою.)

...коли персонаж отримує удар — а не покладається на TryUseItem, який для брони взагалі не має сенсу. MaxDurability = 60 тут така сама цифра, як і в меча чи щита, — рахуються лише «скільки ударів витримає», а не «скільки разів клікнули».

Без коду: кнопка Equip на рядку шолома в DemoInvOverlay, як і для решти. Кнопки Use там не буде — фрагмент Consumable шолому не потрібен, і без нього IsUsable() для броні так само повертає false, як і для мідної руди.


А тепер факел — усі три фрагменти одразу

DA_Torch має той самий Equippable, що й меч (з'являється в руці так само), плюс Durability (запас «палива») і Consumable (витрачається під час використання). Жодного окремого коду для цього не знадобилося — просто третій запис у масиві фрагментів.

Inventory->TryUseItem(SlotIndex, Instigator);

Той самий виклик, яким користується будь-який предмет із кнопкою «Use»: фрагменти самі вирішують, що це означає для конкретно факела (списати заряд, зменшити запас пального). Сам інвентар про факел спеціально нічого не знає.

Без коду: кнопка Use на рядку факела в DemoInvOverlay.


І зілля — без вдягання взагалі

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 — Лут і предмети у світі
Усе про DemoInvOverlay і консольні команди 13 — Відлагодження