Один код для браузера, Telegram, iOS і Android
· 4 хв читання · Команда Тямки
Чому Тямка працює на Expo і React Native, а не на Flutter чи трьох нативних застосунках, що ми виміряли перед вибором і чого нам коштує вебверсія.
Тямка має працювати в більшій кількості місць, ніж більшість маленьких ігор. Вона має відкриватися в браузері й у Telegram, працювати на iOS і Android, а згодом у Steam і на Steam Deck. 25 вересня ми зупинилися на одній кодовій базі на TypeScript з Expo і React Native для всього цього. Репозиторій досі називається puzzle-game-flutter, бо в першому брифі був Flutter. Цей запис про те, як це змінилося.
Що ми порівнювали
Ми зібрали той самий прототип, таблицю Шульте 7×7, на кількох стеках. Спершу Flutter для вебу проти звичайного TypeScript у браузері, пізніше Expo з React Native Web. Окремо розглянули нативну розробку під кожну платформу на Swift і Kotlin з окремим стеком для Steam.
Вирішальним було число секунд до моменту, коли в гру можна грати на повільному зʼєднанні. Міряли в Chrome з холодним кешем і процесором, сповільненим учетверо, брали медіану з трьох прогонів. Повільний профіль мав затримку 562,5 мс і 1,44 Мбіт/с, швидкий 150 мс і 8,1 Мбіт/с. Обидва ми задали вручну, тож вони близькі до пресетів DevTools, але не такі самі.
| Збірка | Повільний профіль | Швидкий профіль |
|---|---|---|
| Звичайний TypeScript | 2,2 с | 0,6 с |
| Expo, React Native Web | 3,7 с | 1,0 с |
| Flutter web | 13,5–14,4 с | 3,0–3,5 с |
Poki, один із великих порталів вебігор, пише, що гравці йдуть, якщо гра вантажиться довше за десять секунд. Flutter перевищував цю межу з однією грою на сторінці, а кожна з пʼятнадцяти ігор додала б ще коду. У WebKit навіть Wasm-збірка Flutter відкочувалася на найважчий варіант на JavaScript, а документація самого Flutter каже, що Wasm-збірка не працює в жодному браузері на iOS. Отже, Telegram на iPhone отримав би найповільнішу версію. Для гри, яку відкривають із чату, це все вирішило.
Чому не натив
Нативна розробка під кожну платформу виглядала варіантом із найкращою якістю. Зупинила нас найдорожча частина проєкту, ігрові поля і скіни. Більшість скінів змінює розкладку HUD, клітинок чи екрана результатів, а не лише кольори, і натив означав би робити все це тричі. До того ж Swift нічим не допомагає зі Steam на Windows чи Steam Deck. На папері натив виграв в одному місці. Анімації на JavaScript у вебвʼю на iPhone, а саме там вебзбірка працює всередині Telegram, тримаються близько 60 Гц, і публічного способу підняти частоту немає.
Expo в першому колі не було. Його запропонував рецензент, і ми виміряли його на тій самій таблиці Шульте. На повільному профілі 3,7 с, тобто повільніше за звичайний TypeScript, але далеко до десяти секунд, і через мережу йде близько 300 КБ. Той самий прототип запустився і на симуляторі iOS, і на емуляторі Android як справжній нативний застосунок.
На телефонах ми не міряли. Тестових пристроїв не було, тому вибір зроблено за числами з десктопного браузера зі штучним сповільненням.
Що ми отримали
- Одна кодова база на TypeScript. З неї нативно збираються iOS і Android, а вебзбірка йде в браузер, у Telegram і на ігрові портали.
- Steam і десктоп заплановані як та сама вебзбірка всередині Electron. Невеликий прототип на Electron запустив досягнення і хмарні збереження Steamworks на macOS. Оверлей і Windows ще не перевіряли.
- Правила кожної гри лежать у звичайних модулях на TypeScript без React. Випадковість і час передаються як аргументи, тож той самий seed дає той самий рівень на будь-якому пристрої. Пізніше це дало змогу серверу перевіряти результат, переграючи його тим самим кодом, про це окремий запис.
Чого це коштує
Нові версії Expo часто щось ламають, а нові версії iOS доходять до нього із затримкою. Для Game Center, Google Play Games і якісної вібровіддачі знадобляться невеликі власні нативні модулі.
Вебзбірка — це окрема платформа, а не побічний продукт, що дістається задарма. У прототипі на Expo було шість багів. Два були тільки у вебі, ще два у вебі проявлялися помітніше, а останні два жили кожен на одній мобільній платформі. Три вебособливості тепер записані в правила проєкту.
- React Native Web ігнорує натискання коротші за 50 мс, якщо не поставити затримку натискання на нуль. У грі на реакцію це непомітно зʼїдає справжні тапи, тому спільний компонент натискання у вебі ставить її на нуль.
- Вебекспорту, який запускається з підпапки порталу, потрібні відносні шляхи. Нативні збірки не мають отримати це налаштування, інакше Android перестає вантажити вбудовані зображення, тому воно вмикається лише для вебекспорту.
- Якщо забути провайдер безпечної зони в корені, вебсторінка просто порожня.
Кожну зміну у вебі перевіряємо в Chrome і в WebKit, ніколи лише в одному браузері.
Де ми зараз
Усі пʼятнадцять ігор працюють у вебзбірці з однієї оболонки. Ми почали автоматичне проходження на iOS, і на симуляторі iPhone 16 Pro з iOS 18.6 перший рівень восьми ігор пройшов без жодного бага в коді ігор. 26 вересня нативну роботу поставили на паузу. Перша справжня перевірка буде у вебі і в Telegram, а iOS і Android повернуться після неї. Доти в коді не може бути нічого тільки для вебу.
Таблиця Шульте, на якій ми все міряли, є на сторінці ігор разом з іншими чотирнадцятьма.