Skip to content

06 — Спорядження

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

Носіння предметів у названих слотах: зброя в руці, шолом на голові, каблучка на пальці.


Слоти — це теги, а не перелічення

Які слоти існують, задається на компоненті в полі AvailableSlots. Плагін ніколи не вгадує, як влаштоване тіло вашого персонажа.

Середньовічний RPG:  Weapon.Main, Weapon.Offhand, Armor.Head, Armor.Chest, Accessory.Ring ×2
Мех-симулятор:       Arm.Left, Arm.Right, Torso, Legs, Reactor
Картярська гра:      (компонент не потрібен зовсім)

Предмет заявляє, куди він проситься, через свій фрагмент Equippable. Вдягнути його можна лише туди, де такий слот справді є.

Це найчастіша причина «чому не вдягається»: предмет просить слот, якого немає в AvailableSlots цього персонажа. Інспектор показує обидва боки — 13 — Відлагодження.


Налаштування компонента

Add Component → Equipment.

Поле Опис
AvailableSlots схема тіла: які слоти взагалі існують
LinkedInventory рюкзак, з якого беруть і куди повертають
bAutoLinkInventoryOnSameActor знайти інвентар на тому ж акторі (типово увімкнено)

LinkedInventory можна не чіпати: компонент сам знайде інвентар на своєму акторі. Пошук лінивий — він спрацює навіть тоді, коли інвентар додали пізніше.

Залишити зв'язок порожнім доречно, коли спорядження живе саме по собі: зброя NPC, яку не можна забрати, або манекен для попереднього перегляду.


Вдягання й зняття

Найзручніша форма — гравець обирає предмет, а не місце:

Equipment->TryEquipFromInventorySlot(InventorySlotIndex);

Предмет сам знає, у який слот він проситься. Це виклик за пунктом «Вдягнути» в контекстному меню.

Явна форма, коли слот відомий:

Equipment->TryEquipItem(Item, MainHandTag, /*bRemoveFromInventory*/ true);

Зняття:

Equipment->TryUnequipSlot(MainHandTag, /*bReturnToInventory*/ true);
Equipment->TryUnequipAll(true);

Заміна зброї — один виклик

Якщо слот зайнятий, попередній предмет знімається автоматично й повертається в рюкзак. Окремо знімати не потрібно.

Повний рюкзак не знищує предмет

Якщо в інвентарі немає місця, зняття не відбувається: предмет лишається вдягненим, а виклик повертає false. Нічого не втрачається через переповнений рюкзак.

Це стосується й випадку, коли вільних слотів немає, але є місце всередині стека: предмет туди зіллється. А якщо в останню мить відмовить гейт фрагмента — предмет повертається на себе, і виклик чесно повертає false.

Обмін між слотами

Equipment->TrySwapSlots(MainHandTag, OffHandTag);

Працює, якщо обидва предмети згодні жити в чужому слоті. Згода виражається в фрагменті Equippable: слот має бути названий або як EquipmentSlot, або в AlternativeSlots.

DA_Sword
└── Equippable
    ├── EquipmentSlot   : Equipment.Slot.Weapon.Main    ← куди потрапляє за замовчуванням
    └── AlternativeSlots: Equipment.Slot.Weapon.Offhand ← і де ще може бути

Меч, що вміє тільки головну руку, у другу не переміститься — і виклик поверне false. Це не обмеження реалізації, а те саме правило, що не дає вдягнути шолом на ногу; просто предметам, які справді носяться в двох місцях, треба про це сказати.

Один із слотів може бути порожнім — тоді це просто «перекласти в іншу руку».


Візуальний актор

Якщо фрагмент Equippable називає EquippedActorClass, актор створюється й прикріплюється на кожній машині — і на сервері, і на клієнтах.

Він не реплікується: кожен клієнт створює свій. Тому меч у руці персонажа коштує рівно стільки ж трафіку, скільки сам запис про слот.

Куди прикріплювати, вирішує GetAttachmentComponent. Типово це перший скелетний меш власника, а якщо його немає — корінь. Перевизначте в Blueprint, якщо ваш персонаж тримає сокети деінде: турель на транспорті, другорядний меш.

Доводка після прикріплення — OnEquipmentSpawned (матеріал за рідкістю, масштаб, передати актору посилання на екземпляр) або подія OnEquipmentActorSpawned.


Сумарні статистики

Число, яке аркуш персонажа показує як «загальна броня»:

const float TotalArmor = Equipment->GetTotalStatValue(MyTags::Stat_Armor);

Підсумовує стат по всьому вдягненому. Читає значення з екземплярів, тому покращений чи пошкоджений предмет вносить свій фактичний внесок, а не номінальний для типу.

Звідки значення беруться:

  1. Equippable.GrantedStats проставляє їх новій копії при створенні;
  2. далі ваша гра може змінювати їх на конкретній копії (Instance->SetStatValue(...)) — заточування, зачарування, знос.

Плагін нічого не застосовує до персонажа сам: жодні дві гри не моделюють броню однаково. Він лише дає число.


Використання вдягненого

Equipment->TryUseEquippedItem(MainHandTag, GetPawn());

Проганяє ті самі фрагменти, що й TryUseItem для інвентаря: зламаний інструмент відмовиться однаково — і в руці, і в рюкзаку.


Запити для UI

Метод Що дає
GetEquippedItem(Slot) предмет у слоті або null
GetOccupiedSlots() які слоти зайняті
GetAllEntries() усе вдягнене одним проходом
HasSlot(Slot) чи є такий слот у персонажа взагалі
CanEquipToSlot(Item, Slot) чи можна вдягнути сюди — для підсвітки цілі
GetDesiredSlotForItem(Item) куди предмет проситься
GetEquippedActor(Slot) візуальний актор слота

Для панелі «паперової ляльки» обходьте AvailableSlots, а не зайняті слоти — порожній слот теж потрібно намалювати:

for (const FGameplayTag& Slot : Equipment->AvailableSlots)
{
    DrawSlot(Slot, Equipment->GetEquippedItem(Slot));  // може бути null
}

Події

Подія Коли
OnItemEquipped слот став зайнятим — саме вдягання
OnItemUnequipped слот звільнився (предмет ще читабельний)
OnEquippedItemChanged предмет той самий, його дані змінилися: міцність, заряди, статистика
OnEquipmentActorSpawned візуальний актор створено й прикріплено

Перші три спрацьовують і на сервері, і на клієнтах.

Чому змінa — окрема подія. Меч, яким б'ють, втрачає одиницю міцності на кожному ударі, і кожна така втрата позначає слот до реплікації. Якби це вважалося «вдяганням», звук вдягання лунав би на кожен удар. Тому: смуга міцності на панелі персонажа — OnEquippedItemChanged; звук, спалах і перерахунок статистик — OnItemEquipped.

Це рівно та сама трійка, що й в інвентаря (OnItemAdded / OnItemRemoved / OnItemChanged), просто слот адресується тегом, а не номером.


Куди далі