Бой в MMORPG для Telegram шёл на пяти кадрах в секунду: экран перерисовывался шестьдесят раз в секунду, и телефон не успевал
Я делаю EclipseFall — MMORPG внутри Telegram, которую не нужно устанавливать. В прошлом лонге я разбирал, как экран создания героя перестал листаться на Android. Эта история тоже началась с отзыва живого игрока и тоже про слабый телефон: третьего сентября под одной из статей здесь, на DTF, читатель написал, что бой у него идёт примерно на пяти кадрах в секунду, и попросил выключатель эффектов.
Бой в Telegram-RPG считает сервер, а телефон его только показывает
Чтобы было понятно, где искать тормоза, коротко про устройство. Бой в игре целиком считает сервер: он прогоняет схватку по тикам в 100 миллисекунд и отдаёт клиенту готовый журнал событий. Кто ударил, сколько снял, когда наложился эффект. Телефону остаётся проиграть этот журнал как запись, с анимациями и летящими числами урона.
Звучит так, будто клиенту и тормозить нечем. Первая мысль и была такой: у человека старый телефон, тут ничего не сделаешь. Прежде чем отвечать, я полез в код экрана боя.
Что нашлось в коде
Часы реплея были устроены самым прямым способом. Таймер на каждом кадре браузера сдвигал текущее время боя и записывал его в состояние экрана. Каждое такое обновление заставляет React перерисовать экран, то есть экран боя пересчитывался шестьдесят раз в секунду, даже когда между двумя кадрами в бою ничего не происходило.
Хуже было то, что делалось при каждой перерисовке. Экран трижды проходил фильтром по всему журналу боя, чтобы понять, какие события уже случились и какие из них свежие. В среднем в журнале 195 событий, в самом длинном из записанных боёв — 2707. Три прохода по такому списку шестьдесят раз в секунду на мощном компьютере незаметны. На бюджетном Android это и есть пять кадров.
Отдельно всплыло, что системная настройка «уменьшить движение» игрой не учитывалась вовсе. Своего выключателя эффектов в настройках тоже не было.
Сначала замер, потом правка
Чинить вслепую я не стал: без замера «стало лучше» ничего не значит. Написал пробу, которая запускает настоящий бой новичка на локальном сервере и через протокол отладки Chrome замедляет процессор в шесть раз, чтобы изобразить слабый телефон. За восемь секунд боя проба считает кадры и долгие задачи, которые блокируют экран.
До правки вышло 16,4 кадра в секунду и 69 долгих задач. Это не те пять кадров, что видел игрок: замер шёл на отладочной сборке и на эмуляции, поэтому сравнивать эти числа можно только между собой.
Как починили
Время боя переехало из состояния экрана в ссылку, которую React не отслеживает. Таймер по-прежнему тикает каждый кадр, но экран обновляется только в двух случаях: когда в журнале наступило новое событие и раз в 100 миллисекунд, чтобы полоски здоровья и каста шли плавно. Какие события уже видны, теперь находится двоичным поиском по отсортированному журналу, без проходов по всему списку.
Корень приложения стал уважать системное «уменьшить движение»: если телефон просит меньше анимаций, их становится меньше.
После правки та же проба на тех же условиях показала 33,0 кадра в секунду и 56 долгих задач. Выпуск с починкой ушёл в прод четвёртого сентября, на следующий день после отзыва.
Чтобы починка не откатилась тихо, её держат два теста. Первый проверяет правила часов на всех записанных эталонных боях: если журнал однажды придёт несортированным, двоичный поиск начнёт врать, и тест это поймает. Второй проверяет, что экран боя подключён именно к новым часам.
Выключатель всё-таки понадобился
Вдвое больше кадров не значит «плавно на любом телефоне». Похожая жалоба приходила и от другого игрока, с бюджетного Poco. Поэтому одиннадцатого сентября в настройках появился переключатель «меньше эффектов»: он гасит анимации во всём приложении, а числа урона в бою рисует на месте, без полёта. Выбор хранится на устройстве, как выбор языка.
Пятнадцатого сентября игрок тридцатого уровня написал в чате канала, что включил эту настройку и бой в группе пошёл плавнее. Групповой бой — самый тяжёлый случай: пять участников дают вчетверо больше событий в секунду, чем бой один на один.
Что я из этого вынес
Серверный бой снимает с клиента расчёты, но показ записи тоже стоит денег, и платит за него самый слабый телефон в аудитории. Проверять такие вещи на своём устройстве бесполезно, нужен замер с искусственно замедленным процессором до и после правки.
И отзыв из комментариев оказался точнее любого моего теста: я сам бой на слабом телефоне не запускал ни разу.
Игра живёт внутри Telegram, ставить ничего не нужно: открыть EclipseFall. Про такие разборы я пишу в девлоге: t.me/eclipsefall_game.