Як приручити трансформера з лазером? Про нюанси роботи з джойконами Nintendo Switch 2

Всім привіт. Мене звати Сергій Донський, я Unreal Engine C++ розробник. Зараз працюю в українській компанії Pingle Studio. Моя робота зосереджена переважно на геймплейній логіці, портуванні на консолі PlayStation, Xbox, Switch та оптимізації ігор.

Як багато хто знає, торік вийшла нова консоль Nintendo SwitchTM 2. Наша компанія — одна з перших у світі — портувала гру Lynked: Banner of the Spark для неї. Ми зіткнулися з багатьма викликами на цьому проєкті. Про них я розповідав у своїй доповіді на DOU Day 2026 і саме її я адаптував для цього блогу. В ньому я розповім про імплементацію основної фічі Switch 2 — mouse controls.

Що змінилось у Switch 2

Switch — це планшет, який можна під’єднати до док-станції для домашнього користування, або використовувати як портативний пристрій, що робить його гібридною консоллю. У нього є бездротові контролери Joy-Con, які функціонують як дві половини стандартного контролера і можуть бути під’єднані/від’єднані від консолі.

Одна з ключових особливостей Switch 2 — це поява mouse controls. Тобто тепер користувач може використовувати контролер не тільки як геймпад, а як мишку.

Unreal Engine — потужний рушій для розробки ігор від Epic Games. Приблизно 25–30% сучасних ігор робляться на ньому. Для AAA проектів цей показник 50%. Решта — Unity, або інші рушії. UE вже має базову абстракцію платформ через класи типу: GenericPlatformMisc, GenericApplicattion, GenericApplicationMessageHandler, IInputDevice.

Зокрема, FGenericPlatformMisc — це набір статичних платформних helper-функцій. Наприклад:

  • RequestExit() — закрити гру;
  • PlatformHandleSplashScreen() — сплеш скрін;
  • GetDeviceId() — унікальний ID пристрою.

Тобто, багато платформо-специфічної логіки вже реалізовано «з коробки».

Проблема: Switch 2 — нова платформа

Для Switch існує власна реалізація — SwitchPlatformMis. Та це нова платформа і частина функціоналу була відсутня. Тому нам довелось модифікувати рушій і власними силами зробити підтримку mouse controls.

Трохи теорії про те, як працює інпут. В Unreal Engine кожен тік викликається функція PollGameDeviceState, яка відповідає за опитування стану пристрою вводу. Для Switch є своя специфікація:

void FSwitchApplication::PollGameDeviceState(const float TimeDelta)
{
	// Poll game device state and send new events
	InputInterface.Tick(TimeDelta);
	InputInterface.SendControllerEvents();
}

void FNpadController::SendControllerEvents() аналізує стан контролера і порівнює його зі станом один тік тому. Наприклад, якщо тік тому кнопка A була натиснута, а зараз — ні, це означає, що користувач відпустив її, і ми надсилаємо відповідний івент.

Очевидно, що залежно від того, увімкнений режим миші чи вимкнений, обробка інпуту має відрізнятися. Але рушій не знав про цей режим, адже це нова фіча. Тому ми самі додали булеву змінну bIsActiveMouseMode. І тут постало питання: а які умови перемикання цього режиму?

Switch дає три тригери, які ми потенційно могли використати:

  • від’єднання джойконів;
  • чи торкається лазер поверхні;
  • чи перебувають джойкони у вертикальному положенні.

Після тестів зручності для користувача ми вирішили орієнтуватися лише на перший тригер — «від’єднання джойконів». Тобто, коли вони приєднані, працює звичайний режим, а коли від’єднані — mouse mode. Ми додали делегат для інформування, коли змінюється стан.








Ми також додали можливість:

  • вмикати/вимикати режим миші;
  • відстежувати його стан.

Детальніше про «лазер»

Лише після від’єднання джойконів ми робимо курсор видимим і починаємо аналізувати лазер. Як працюють лазерні мишки? Вони підсвічують поверхню лазером і за допомогою маленької камери роблять тисячі знімків щосекунди. Система аналізує мікрорельєф поверхні та визначає, як саме він змістився між кадрами. На основі цього обчислюється рух мишки у вигляді зміщення по X та Y.


В обробці void FNpadController::FlushMouseEvents() ми якраз отримуємо Event із цими зміщеними координатами мишки. Щоб Unreal Engine коректно сприймав мишку, ми додали просту зміну позиції курсору:

FVector2D CurrentCursorPos = FSlateApplication::Get().GetCursorPos();
const FVector2D MouseCoordinate {CurrentCursorPos.X + Event.X, CurrentCursorPos.Y + Event.Y };
MessageHandler->SetCursorPos(MouseCoordinate);

Коли ми працюємо в режимі миші, Switch автоматично визначає, що кнопка R відповідає за ліву кнопку миші, а кнопка ZR — за праву. Але як працюють інпути в грі? Є UInputMappingComponent і в ньому є різні UInputMappingContext:

  • GlobalInput;
  • GameInput;
  • UIInput;
  • BuildModeInput.

Що таке UInputMappingContext? Це звичайний дата асет, який містить в собі масив мапінгів.

FEnhancedActionKeyMapping — це структура в Unreal Engine (система Enhanced Input), що описує зв’язок між фізичною клавішею/кнопкою та ігровою дією.

Тут зберігається початкова інформація про всі інпути залежно від контексту. Наприклад, у BuildModeInput містяться всі інпути, що стосуються будівництва. У різних ModeInput клавіші можуть дублюватися, оскільки гра сама відстежує, у якому режимі ми перебуваємо. Залежно від цього застосовується відповідний інпут. Під час гри інпути певним чином можуть змінюватися, тому всі вони записуються у відповідні Runtime:

Я зосереджуся на BuildModeInput та BuildModeInputRuntime, оскільки для інших логіка буде аналогічною. Отже, після імплементації на рівні рушія гра отримує івент зміни режиму. Що саме змінюється:

  • з’являється курсор;
  • інпут починає працювати як мишка;
  • частина кнопок «втрачається», оскільки тепер вони інтерпретуються як кнопки миші;
  • ми інакше тримаємо джойкони, тому частина дій може опинитися на незручних кнопках.

Ми також зробили автоматичний ремапінг кнопок:

  • дії переносяться на інші кнопки;
  • є схема інпутів для звичайного режиму;
  • є схема інпутів для режиму миші.

Між ними відбувається перемикання. У нашому UInputMappingComponent ми підписуємося на зміну режиму, і коли це відбувається, запускається ремапінг:

Оскільки тільки деякі інпути треба змінювати, то в UInputMappingContext ми додали:

При зміні режиму ми проходимось циклом по наших мапінгах і змінюємо кнопку активації екшену на JoyConKey у звичайному режимі. Або на MouseKey в режимі маус мода:


Найцікавіше — проблема з ремапінгом

Як я вже зазначав, під час гри інпути певним чином можуть змінюватися. Річ у тім, що в грі реалізований користувацький ремапінг. І якщо користувачу був не зручний базовий мапінг, то він міг змінити кнопки для дій в InputRuntime.


В результаті нашої робити mouse controls працює коректно, ми отримали два режими інпуту і при цьому зберегли користувацькі налаштуванням. А у підсумку отримали стабільну інтеграцію фічв у гру.

Всім дякую за увагу!

Підписуйтеся на Telegram-канал @gamedev_dou, щоб не пропустити найважливіші статті і новини

👍ПодобаєтьсяСподобалось4
До обраногоВ обраному1
LinkedIn


Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Підписатись на коментарі