03 — Взаємодія та фокус¶
🇬🇧 English | 🇺🇦 Українська
Джерело¶
Простими словами. Джерело — це «рука з лазерною указкою». Указка постійно показує, що перед вами (фокус), а рука бере предмет, коли ви натискаєте клавішу. У desktop-гри одна така рука прив'язана до камери, у VR — по одній на кожен контролер.
UInteractorComponent — scene component на pawn. Прив'яжіть його до камери або motion controller. При bUsePawnViewpoint=true він сам оновлює позицію з поточної камери. При false його transform визначається батьківським компонентом або кодом проєкту.
| Параметр | Призначення |
|---|---|
TraceDistance |
Довжина променя, початково 300 см |
TraceRadius |
0 — line trace; більше 0 — sphere sweep |
FocusUpdateHz |
Частота пошуку фокуса для цієї руки; початково 30 Гц. Значення 20 Гц зменшує кількість трейсів і навантаження на CPU |
PoseReplicationHz |
Частота спільного пакета поз усіх активних рук pawn; початково 30 Гц. Значення 20 Гц зменшує мережевий трафік, але збільшує інтервал між оновленнями на сервері. Використовується значення першого активного interactor |
bNearInteraction / NearRadius |
Пошук предмета поруч із долонею через overlap, із перевіркою видимості; зручний для VR |
TraceChannel |
Канал блокування, початково Visibility |
Filter.RequiredTags |
Усі потрібні Gameplay Tags, з ієрархічним зіставленням |
Filter.BlockedTags |
Будь-який із цих Gameplay Tags забороняє ціль |
Filter.RequiredActorTags |
Усі точні імена з Actor.Tags |
GrabOffset |
Зсув захопленого фізичного предмета від джерела; для VR зазвичай нуль |
Ціль має IInteractableInterface для звичайної дії або UInteractionGrabTargetComponent для захоплення. Готовий UInteractionManipulationComponent реалізує контракт захоплення; його можна додати до власного актора без успадкування від AInteractionPhysicsActor. Якщо поведінку утримання потрібно описати самостійно без C++, використайте Blueprint-нащадок UInteractionBlueprintGrabTargetComponent (11). Якщо інтерфейсу немає, фільтр читає TargetTags компонента. Локальна поза оновлюється кожен кадр незалежно від частот фокуса й мережі.
Відфільтрована або заблокована ціль залишається перешкодою для предметів за нею. При TraceRadius > 0 компонент викликає SweepSingleByChannel зі сферою; точку контакту додатково перевіряє LineTraceSingleByChannel, щоб не вибрати предмет за кутом. Для такого пошуку ціль має блокувати TraceChannel.
Порядок вибору цілі¶
RefreshFocus спершу шукає ціль для захоплення, а якщо її немає — ціль для звичайної дії. Актор підходить, якщо:
- реалізує
IInteractableInterfaceабо маєUInteractionGrabTargetComponent; - його теги проходять
Filter: теги інтерфейсу (GetInteractionTags) або, без інтерфейсу,TargetTagsкомпонента, а такожActor.Tags; CanInteractповертаєtrue, якщо інтерфейс є;- для захоплення — компонент цілі дозволяє хват (
CanBeginGrip): вільне місце для руки, валідний профіль, доступна точка хвату.
Ближній пошук (bNearInteraction) знаходить усі об'єкти в сфері NearRadius навколо джерела, відкидає непридатні, для решти бере найближчу точку колізії й перевіряє до неї видимість. Перемагає найближчий кандидат. Без ближнього пошуку перемагає перший блокувальний об'єкт променя.
Актор лише з компонентом цілі, без інтерфейсу, отримує фокус тільки тоді, коли його зараз можна взяти: звичайної дії в нього немає. Тому, наприклад, зайнятий предмет іншого гравця не показує підказки.
Сервер повторює той самий пошук від своєї копії пози руки, тому ціль, яку клієнт бачив у фокусі, на сервері може не підтвердитися. Тоді OnActionResult повідомить причину відмови (13).
Фільтрація тегами¶
Простими словами. Теги — це наліпки на предметах, а фільтр — правило на руці: «бери лише з наліпкою Item і ніколи з наліпкою Locked». Так одна рука може брати лише квестові предмети, а інша — будь-що.
Фільтр належить джерелу (Interactor.Filter), а теги — цілі. GetInteractionTags() повертає Gameplay Tags цілі; готовий AInteractionPhysicsActor повертає свій InteractionTags. Окремо кожен актор Unreal має звичайний масив Tags із точних імен FName. Для вибору цілі потрібні всі RequiredTags, жодного з BlockedTags і всі RequiredActorTags. Порожні списки нічого не обмежують.
Наприклад, рука має брати лише предмети для завдання. На руці встановіть RequiredTags = {Interaction.Item}, BlockedTags = {Interaction.Locked} і RequiredActorTags = {QuestPickup}. На списі задайте InteractionTags = {Interaction.Item} та додайте QuestPickup до Actor → Tags. Спис пройде фільтр. Якщо додати йому Interaction.Locked, він перестане вибиратися; якщо прибрати QuestPickup, результат буде таким самим. Вбудовані теги Interaction.Item і Interaction.Locked оголошені в InteractionGameplayTags.h.
Еквівалентне налаштування в C++ після створення компонентів:
Interactor->Filter.RequiredTags.AddTag(InteractionGameplayTags::Item);
Interactor->Filter.BlockedTags.AddTag(InteractionGameplayTags::Locked);
Interactor->Filter.RequiredActorTags.Add(TEXT("QuestPickup"));
Spear->InteractionTags.AddTag(InteractionGameplayTags::Item);
Spear->Tags.Add(TEXT("QuestPickup"));
Gameplay Tags мають ієрархію: потрібний Interaction.Item також збігається з дочірнім тегом на кшталт Interaction.Item.Spear, якщо ви зареєстрували його в проєкті. Actor.Tags — звичайні імена: QuestPickup і QuestPickup.Special різні. Фільтр перевіряється і локально для фокуса, і повторно на сервері перед дією. Відхилений предмет усе одно перекриває предмети за ним, якщо його collision блокує TraceChannel.
Контракт цілі¶
| Метод | Де виконується | Відповідальність |
|---|---|---|
CanInteract |
Локальний запит і повторно сервер | Дозволити дію; реалізуйте без побічних ефектів |
GetInteractionTags |
Локально й на сервері | Повернути семантичні теги |
Interaction |
Сервер | Відкрити двері, натиснути кнопку або інша ігрова дія |
OnFocusStart / OnFocusEnd |
Локальний спостерігач | Показати або прибрати візуальний відгук |
OnGrab(Interactor) |
Сервер, коли першу руку прийнято | Повідомити gameplay про підбір; рухом керує компонент цілі |
OnRelease(Interactor, Reason, LinearVelocity, AngularVelocity) |
Сервер, коли відпустила остання рука | Повідомити gameplay про відпускання; Reason = Released означає навмисне відпускання, швидкості вже обмежені сервером |
OnGrab і OnRelease викликає ядро для будь-якої цілі з інтерфейсом, а не лише для готових акторів. Події інтерфейсу — Interaction, OnFocusStart, OnFocusEnd, OnGrab, OnRelease — можна лише реалізувати, але не викликати з Blueprint: так клієнт не обійде серверну перевірку. Перевіряйте допустимість у CanInteract та CanBeginGrip. Для власного компонента цілі реалізуйте BeginGrip/EndGrip; для готового manipulation використовуйте OnGripStarted, OnGripEnded та OnUsed, залишаючи одного виконавця руху.
GetFocusedActor повертає одну поточну ціль на джерело. Перехід A → B надсилає кінець A перед початком B. OnFocusChanged підходить для HUD; OnActionResult повідомляє результат серверного запиту разом із причиною відмови (EInteractionRejectReason). Локальний фокус не означає, що сервер уже дозволив дію; як пояснити гравцеві відмову — у уроці 3.
Один UInteractorComponent керує однією рукою: під час захоплення або очікування відповіді сервера він прибирає фокус з інших предметів. CanFocusNewTarget() показує, чи може ця рука зараз вибрати нову ціль. У VR друга, вільна рука має власний компонент і може вибирати незалежно. Після відпускання перша рука знову шукає цілі.
Візуальний відгук¶
AInteractionMasterItem додає proximity sphere, текстові маркери та опціональні billboard-текстури. Встановіть NearbyIcon.Sprite і FocusIcon.Sprite, щоб замінити текст. Billboard сам повертається до камери, зокрема у стереорежимі.
Для підсвітки призначте FocusOverlayMaterial. За потреби увімкніть bUseCustomDepthFocus; видимий outline вимагає post-process матеріалу проєкту. Без нього custom depth сам по собі нічого не малює. Попередні overlay і stencil відновлюються після завершення фокуса.
Фокус двох рук підраховується окремо: завершення фокуса однієї руки не прибирає підсвітку іншої. Вбудоване представлення спільне для локальних гравців одного процесу. Для незалежного split-screen UI використайте OnFocusChanged та власні widgets кожного LocalPlayer.
Proximity sphere використовує overlap із locally controlled pawn. Увімкніть Generate Overlap Events на capsule персонажа. Маркер близькості з'являється, тільки якщо хоча б одна місцева рука вільна; він не дає права взаємодіяти крізь стіни.