В апреле War Robots отмечает свой 10-летний юбилей! И по сей день мы продолжаем его развивать и поддерживать, не только выпуская новые возможности для наших игроков, но и совершенствуя его технически.
В этой статье мы обсудим наш многолетний опыт технической разработки этого крупного проекта. Но сначала вот снимок проекта в его нынешнем виде:
Чтобы сохранить функциональность такого рода проекта и обеспечить дальнейшее качественное развитие, работы только над непосредственными продуктовыми задачами недостаточно; также важно улучшить его техническое состояние, упростить разработку и автоматизировать процессы, в том числе связанные с созданием нового контента. Кроме того, мы должны постоянно адаптироваться к меняющемуся рынку доступных пользовательских устройств.
Текст основан на интервью с Павлом Зиновым, руководителем отдела разработки Pixonic (MY.GAMES), и Дмитрием Четвериковым, ведущим разработчиком War Robots.
Вернемся в 2014 год: проект War Robots изначально создавался небольшой командой, и все технические решения укладывались в парадигму быстрой разработки и вывода на рынок новых функций. В то время у компании не было больших ресурсов для размещения выделенных серверов, и поэтому War Robots вышла на рынок с сетевым мультиплеером, основанным на логике P2P.
Photon Cloud использовался для передачи данных между клиентами: каждая команда игрока, будь то движение, стрельба или любая другая команда, отправлялась на сервер Photon Cloud с помощью отдельных RPC. Поскольку выделенного сервера не было, в игре был главный клиент, который отвечал за достоверность состояния матча для всех игроков. При этом остальные игроки полностью обрабатывали состояние всей игры на своих локальных клиентах.
Как пример, не было проверки на движение — локальный клиент перемещал своего робота так, как считал нужным, это состояние отправлялось мастер-клиенту, а мастер-клиент безоговорочно верил этому состоянию и просто пересылал его другим клиентам в матче. Каждый клиент самостоятельно вел лог боя и отправлял его на сервер по окончании матча, затем сервер обрабатывал логи всех игроков, назначал награду и отправлял результаты матча всем игрокам.
Мы использовали специальный сервер профилей для хранения информации о профилях игроков. Он состоял из множества серверов с базой данных Cassandra. Каждый сервер представлял собой простой сервер приложений на Tomcat, и клиенты взаимодействовали с ним через HTTP.
Подход, использованный в игровом процессе, имел ряд недостатков. Команда, конечно, знала об этом, но из-за скорости разработки и доставки конечного продукта на рынок пришлось пойти на ряд компромиссов.
Среди этих недостатков, прежде всего, было качество связи с мастер-клиентом. Если у клиента была плохая сеть, то у всех игроков в матче наблюдались зависания. Причем, если мастер-клиент работал не на очень мощном смартфоне, то из-за высокой нагрузки на него в игре тоже наблюдалась задержка передачи данных. Так вот, в данном случае, помимо мастер-клиента, пострадали и другие игроки.
Вторым недостатком было то, что эта архитектура была склонна к проблемам с читерами. Поскольку итоговое состояние передавалось с главного клиента, этот клиент мог менять многие параметры матча по своему усмотрению, например, подсчитывать количество захваченных зон в матче.
В-третьих, существует проблема с функцией подбора игроков в Photon Cloud: самый старый игрок в комнате подбора игроков Photon Cloud становится главным клиентом, и если с ним что-то происходит во время подбора игроков (например, разрыв соединения), это отрицательно влияет на создание группы. Интересный факт: чтобы система подбора игроков в Photon Cloud не закрывалась после отправки группы на матч, мы в течение некоторого времени даже устанавливали простой компьютер в нашем офисе, и он всегда поддерживал подбор игроков, а это означает, что подбор игроков с помощью Photon Cloud не мог потерпеть неудачу. .
В какой-то момент количество проблем и запросов в поддержку достигло критической массы, и мы начали всерьез задумываться о развитии технической архитектуры игры — эта новая архитектура получила название Инфраструктура 2.0.
Основной идеей этой архитектуры было создание выделенных игровых серверов. На тот момент в команде клиента не хватало программистов с большим опытом серверной разработки. Однако одновременно компания работала над серверным проектом высоконагруженной аналитики, который мы назвали AppMetr. Команда создала продукт, обрабатывающий миллиарды сообщений в день, и имела обширный опыт в разработке, настройке и правильной архитектуре серверных решений. Итак, некоторые члены этой команды присоединились к работе над Инфраструктурой 2.0.
В 2015 году за достаточно короткий промежуток времени был создан .NET-сервер, завернутый в фреймворк Photon Server SDK, работающий на Windows Server. Мы решили отказаться от Photon Cloud, сохранив в клиентском проекте только сетевую структуру Photon Server SDK, и создали Мастер-сервер для подключения клиентов и соответствующих серверов для поиска партнеров. Часть логики была перенесена с клиента на выделенный игровой сервер. В частности, была введена базовая проверка повреждений, а также размещение расчетов результатов матча на сервере.
После успешного создания Инфраструктуры 2.0 мы поняли, что нужно продолжать двигаться в сторону разделения обязанностей сервисов: результатом этого стало создание микросервисной архитектуры. Первым микросервисом, созданным на этой архитектуре, были кланы.
Оттуда появился отдельный сервис Communication, который отвечал за передачу данных между микросервисами. Клиента научили устанавливать соединение с «ангаром», отвечающим за метамеханику на игровом сервере, и делать запросы к API (единой точке входа для взаимодействия с сервисами) по UDP over Photon Cloud. Постепенно количество сервисов росло и в результате мы провели рефакторинг микросервиса подбора игроков. Благодаря этому был создан новостной функционал Inbox.
Когда мы создавали кланы, было понятно, что игроки хотят общаться друг с другом в игре — им нужен какой-то чат. Изначально он был создан на основе Photon Chat, но вся история чата стиралась, как только отключался последний клиент. Поэтому мы преобразовали это решение в отдельный микросервис на базе базы данных Cassandra.
Новая микросервисная архитектура позволила нам удобно управлять большим количеством независимых друг от друга сервисов, что означало горизонтальное масштабирование всей системы и избежание простоев.
В нашей игре обновления происходили без необходимости приостанавливать сервера. Профиль сервера был совместим с несколькими версиями клиентов, а что касается игровых серверов, то у нас всегда есть пул серверов для старой и новой версий клиента, этот пул постепенно смещался после выхода новой версии в магазинах приложений, и игровые серверы со временем исчезли из старой версии. Общая схема нынешней инфраструктуры называлась Инфраструктура 4.0 и выглядела следующим образом:
Помимо изменения архитектуры, мы также столкнулись с дилеммой, где разместить наши серверы, поскольку они должны были охватывать игроков со всего мира. Изначально многие микросервисы размещались в Amazon AWS по ряду причин, в частности, из-за гибкости, которую предлагает эта система с точки зрения масштабирования, поскольку это было очень важно в моменты всплеска игрового трафика (например, когда она была представлена в магазинах и для повышения UA). Кроме того, тогда в Азии было сложно найти хорошего хостинг-провайдера, который бы обеспечивал хорошее качество сети и связь с внешним миром.
Единственным недостатком Amazon AWS была его высокая стоимость. Поэтому со временем многие наши серверы переехали на наше собственное оборудование — серверы, которые мы арендуем в дата-центрах по всему миру. Тем не менее, Amazon AWS оставался важной частью архитектуры, поскольку позволял разрабатывать постоянно меняющийся код (в частности, код сервисов Лиг, Кланов, Чатов и Новостей) и недостаточно обеспеченный стабильностью. тесты. Но, как только мы поняли, что микросервис стабилен, мы перенесли его на свои мощности. В настоящее время все наши микросервисы работают на наших аппаратных серверах.
В 2016 году в рендеринг игры были внесены важные изменения: появилась концепция убершейдеров с единым атласом текстур для всех мехов в бою, что значительно сократило количество вызовов отрисовки и улучшило производительность игры.
Стало понятно, что в нашу игру играют на десятках тысяч различных устройств, и мы хотели предоставить каждому игроку наилучшие игровые условия. Таким образом, мы создали Менеджер качества, который по сути представляет собой файл, который анализирует устройство игрока и включает (или отключает) определенные функции игры или рендеринга. Более того, эта функция позволила нам управлять этими функциями для конкретной модели устройства, чтобы мы могли быстро устранять проблемы, с которыми сталкивались наши игроки.
Также есть возможность скачать настройки Quality Manager с сервера и динамически выбирать качество на устройстве в зависимости от текущей производительности. Логика довольно проста: весь корпус менеджера по качеству разделен на блоки, каждый из которых отвечает за какой-то аспект производительности. (Например, качество теней или сглаживание.) Если производительность пользователя страдает, система пытается изменить значения внутри блока и выбрать вариант, который приведет к увеличению производительности. К эволюции Менеджера Качества мы вернемся чуть позже, но на данном этапе его внедрение прошло достаточно быстро и обеспечило необходимый уровень контроля.
Работа над менеджером качества также была необходима, поскольку разработка графики продолжалась. В игре теперь есть каскадные динамические тени, полностью реализованные нашими графическими программистами.
Постепенно в коде проекта появилось довольно много сущностей, поэтому мы решили, что неплохо бы начать управлять их временем жизни, а также разграничивать доступ к разному функционалу. Это было особенно важно для разделения ангара и боевого кода, поскольку многие ресурсы не очищались при изменении игрового контекста. Мы начали изучать различные контейнеры управления зависимостями IoC. В частности, мы рассмотрели решение StrangeIoC. На тот момент решение показалось нам довольно громоздким, и поэтому мы реализовали в проекте собственный простой DI. Мы также представили ангарный и боевой контекст.
Кроме того, мы начали работу над контролем качества проекта; FirebaseCrashlytics был интегрирован в игру для выявления сбоев, ошибок ANR и исключений в производственной среде.
Используя наше внутреннее аналитическое решение AppMetr, мы создали необходимые дашборды для регулярного мониторинга статуса клиентов и сравнения изменений, вносимых во время релизов. Далее, чтобы улучшить качество проекта и повысить доверие к вносимым в код изменениям, мы начали исследование автотестов.
Увеличение количества ассетов и уникальные ошибки в контенте заставили нас задуматься об организации и унификации ассетов. Это особенно касалось мехов, потому что их конструкция была уникальной для каждого из них; для этого мы создали инструменты в Unity и с их помощью сделали макеты мехов с основными необходимыми компонентами. Это означало, что отдел игрового дизайна мог легко их редактировать — и именно так мы впервые начали объединять нашу работу с мехами.
Проект продолжал расти, и команда начала подумывать о его выпуске на других платформах. Кроме того, в то время для некоторых мобильных платформ было важно, чтобы игра поддерживала как можно больше собственных функций платформы, поскольку это позволяло продвигать игру. Итак, мы начали работу над экспериментальной версией War Robots для Apple TV и сопутствующим приложением для Apple Watch.
Кроме того, в 2016 году мы запустили игру на новой для нас платформе — Amazon AppStore. По техническим характеристикам эта платформа уникальна, поскольку имеет единую линейку устройств, как у Apple, но мощность этой линейки находится на уровне бюджетных Андроидов. Учитывая это, при запуске на этой платформе была проделана значительная работа по оптимизации использования памяти и производительности, например, работали с атласами, сжатием текстур. В клиент была интегрирована платежная система Amazon, аналог Game Center для входа и достижений, в связи с чем был переделан поток работы с логином игрока и запущены первые достижения.
Также стоит отметить, что одновременно с этим команда клиента впервые разработала набор инструментов для Unity Editor, которые позволили снимать внутриигровые видеоролики в движке. Это облегчило нашему отделу маркетинга работу с ресурсами, управление боем и использование камеры для создания видеороликов, которые так понравились нашей аудитории.
Из-за отсутствия Unity и, в частности, физического движка на сервере, большинство матчей продолжало эмулироваться на устройствах игроков. Из-за этого проблемы с читерами сохранились. Периодически в нашу службу поддержки поступали отзывы от наших пользователей с видео читеров: они ускорялись в удобное время, летали по карте, убивали других игроков из-за угла, становились бессмертными и так далее.
В идеале мы бы всю механику перенесли на сервер; однако, помимо того, что игра находилась в постоянной разработке и уже накопила изрядное количество устаревшего кода, подход с авторитетным сервером мог иметь и другие недостатки. Например, увеличение нагрузки на инфраструктуру и требование лучшего подключения к Интернету. Учитывая, что большинство наших пользователей играли в сетях 3G/4G, такой подход хоть и очевиден, но не является эффективным решением проблемы. В качестве альтернативного подхода к борьбе с читерами внутри команды мы придумали новую идею – создание «кворума».
Кворум — механизм, позволяющий сравнивать несколько симуляций разных игроков при подтверждении нанесенного ущерба; получение урона – одна из главных особенностей игры, от которой зависит практически все остальное ее состояние. Например, если вы уничтожите своих противников, они не смогут захватить маяки.
Идея этого решения заключалась в следующем: каждый игрок по-прежнему моделировал весь мир (в том числе отстреливал других игроков) и отправлял результаты на сервер. Сервер проанализировал результаты всех игроков и принял решение о том, был ли игрок в конечном итоге поврежден и в какой степени. В наших матчах участвуют 12 человек, поэтому, чтобы сервер посчитал, что ущерб нанесен, достаточно, чтобы этот факт был зарегистрирован в рамках локальных симуляций 7 игроков. Эти результаты затем будут отправлены на сервер для дальнейшей проверки. Схематически этот алгоритм можно представить следующим образом:
Эта схема позволила нам существенно сократить количество читеров, сделавших себя неубиваемыми в бою. На следующем графике показано количество жалоб на этих пользователей, а также этапы включения функций расчета ущерба на сервере и включения кворума ущерба:
Этот механизм нанесения урона долгое время решал проблемы с читерством, поскольку, несмотря на отсутствие физики на сервере (а значит, и невозможность учитывать такие вещи, как топография поверхности и реальное положение роботов в пространстве), результаты моделирование для всех клиентов и их консенсус давали однозначное понимание, кто кому и при каких условиях нанес ущерб.
Со временем, помимо динамических теней, мы добавили в игру пост-эффекты с помощью стека постобработки Unity и пакетного рендеринга. Примеры улучшенной графики в результате применения эффектов постобработки можно посмотреть на картинке ниже:
Чтобы лучше понимать, что происходит в отладочных сборках, мы создали систему логирования и развернули во внутренней инфраструктуре локальный сервис, который мог собирать логи от внутренних тестировщиков и облегчать поиск причин ошибок. Этот инструмент до сих пор используется QA для плейтестов, а результаты его работы прикрепляются к тикетам в Jira.
Мы также добавили в проект самописный менеджер пакетов. Изначально мы хотели улучшить работу с различными пакетами и внешними плагинами (коих в проекте уже накопилось достаточное количество). Однако функционала Unity на тот момент было недостаточно, поэтому наша внутренняя команда Платформы разработала собственное решение с управлением версиями пакетов, хранением с помощью NPM и возможностью подключения пакетов к проекту, просто добавив ссылку на GitHub. Поскольку мы все еще хотели использовать собственное решение для движка, мы проконсультировались с коллегами из Unity, и ряд наших идей были включены в окончательное решение Unity, когда они выпустили свой менеджер пакетов в 2018 году.
Мы продолжили расширять количество платформ, на которых была доступна наша игра. Со временем появилась Facebook Gameroom — платформа, поддерживающая клавиатуру и мышь. Это решение от Facebook (Meta), которое позволяет пользователям запускать приложения на ПК; по сути, это аналог магазина Steam. Конечно, аудитория Facebook достаточно разнообразна, и Facebook Gameroom был запущен на большом количестве устройств, в основном неигровых. Итак, мы решили оставить в игре мобильную графику, чтобы не нагружать ПК основной аудитории. С технической точки зрения единственными существенными изменениями, которые потребовала игра, были интеграция SDK, платежной системы и поддержка клавиатуры и мыши с использованием родной системы ввода Unity.
Технически она мало отличалась от сборки игры для Steam, но качество устройств было существенно ниже, поскольку платформа позиционировалась как место для казуальных игроков с достаточно простыми устройствами, на которых можно было запускать не что иное, как браузер, при этом в разум Facebook надеялся выйти на довольно большой рынок. Ресурсы этих устройств были ограничены, а у платформы, в частности, были постоянные проблемы с памятью и производительностью.
Позже платформа закрылась, и мы перевели наших игроков в Steam, где это было возможно. Для этого мы разработали специальную систему с кодами для переноса аккаунтов между платформами.
Еще одно примечательное событие 2017 года: выпуск iPhone X, первого смартфона с вырезом. В то время Unity не поддерживала устройства с вырезами, поэтому нам пришлось создать собственное решение на основе параметров UnityEngine.Screen.safeArea, которое заставляет пользовательский интерфейс масштабироваться и избегать различных безопасных зон на экране телефона.
Кроме того, в 2017 году мы решили попробовать ещё кое-что — выпустить VR-версию нашей игры в Steam. Разработка длилась около полугода и включала тестирование на всех доступных на тот момент шлемах: Oculus, HTC Vive и Windows Mixed Reality. Эта работа проводилась с использованием Oculus API и Steam API. Кроме того, готова демо-версия для гарнитур PS VR, а также поддержка MacOS.
С технической точки зрения необходимо было поддерживать стабильный FPS: 60 FPS для PS VR и 90 FPS для HTC Vive. Для этого в дополнение к стандартным Frustum и Occlusion использовалась самописная Zone Culling, а также репроекции (когда некоторые кадры генерируются на основе предыдущих).
Для решения проблемы укачивания было принято интересное творческое и техническое решение: наш игрок стал пилотом-роботом и сел в кабину. Кабина была статичным элементом, поэтому мозг воспринимал ее как некую константу в мире, а значит, движение в пространстве работало очень хорошо.
В игре также был сценарий, который требовал разработки нескольких отдельных триггеров, сценариев и самодельных таймлайнов, поскольку Cinemachine еще не был доступен в той версии Unity.
Для каждого оружия логика наведения, захвата, сопровождения и автоприцеливания была написана с нуля.
Версия была выпущена в Steam и была хорошо принята нашими игроками. Однако после кампании на Kickstarter мы решили не продолжать разработку проекта, поскольку разработка полноценного PvP-шутера для VR была сопряжена с техническими рисками и неготовностью рынка к такому проекту.
Пока мы работали над улучшением технической составляющей игры, а также созданием новых и развитием существующих механик и платформ, мы столкнулись с первым серьёзным багом в игре, который негативно отразился на доходах при запуске новой акции. Проблема была в том, что кнопка «Цена» была неправильно локализована как «Приз». Большинство игроков были этим смущены, совершали покупки за реальную валюту, а затем просили вернуть деньги.
Техническая проблема заключалась в том, что тогда все наши локализации были встроены в клиент игры. Когда возникла эта проблема, мы поняли, что больше не можем откладывать работу над решением, позволяющим обновлять локализации. Так было создано решение на базе CDN и сопутствующих инструментов, таких как проекты в TeamCity, которые автоматизировали загрузку файлов локализации в CDN.
Это позволило нам загружать локализации из используемых нами сервисов (POEditor) и загружать необработанные данные в CDN.
После этого в профиле сервера началась установка ссылок для каждой версии клиента, соответствующих данным локализации. При запуске приложения сервер профиля начинал отправлять эту ссылку клиенту, и эти данные загружались и кэшировались.
Перенесемся в 2018 год. По мере роста проекта увеличивался и объем данных, передаваемых с сервера клиенту. В момент подключения к серверу необходимо было загрузить довольно много данных о балансе, текущих настройках и т.д.
Текущие данные были представлены в формате XML и сериализованы стандартным сериализатором Unity.
Помимо передачи достаточно большого объема данных при запуске клиента, а также при общении с сервером профиля и игровым сервером, устройства игроков тратили много памяти на хранение текущего баланса и его сериализацию/десериализацию.
Стало понятно, что для улучшения производительности и времени запуска приложения необходимо будет провести НИОКР по поиску альтернативных протоколов.
Результаты исследования на наших тестовых данных отображены в этой таблице:
Протокол | размер | выделять | время |
---|---|---|---|
XML | 2,2 МБ | 18,4 МБ | 518,4 мс |
MessagePack (без контракта) | 1,7 МБ | 2 МБ | 32,35 мс |
MessagePack (ключи str) | 1,2 МБ | 1,9 МБ | 25,8 мс |
MessagePack (целые ключи) | 0,53 МБ | 1,9 | 16,5 |
Плоские буферы | 0,5 МБ | 216 Б | 0 мс/12 мс/450 КБ |
В результате мы выбрали формат MessagePack, поскольку миграция обходилась дешевле, чем переход на FlatBuffers с аналогичными результатами вывода, особенно при использовании MessagePack с целочисленными ключами.
Для FlatBuffers (и с протокольными буферами) необходимо описать формат сообщения на отдельном языке, сгенерировать код C# и Java и использовать сгенерированный код в приложении. Поскольку мы не хотели нести дополнительные затраты на рефакторинг клиента и сервера, мы перешли на MessagePack.
Переход был практически плавным, и в первых релизах мы поддерживали возможность отката к XML до тех пор, пока не убедились, что с новой системой проблем нет. Новое решение охватило все наши задачи — существенно сократило время загрузки клиента, а также повысило производительность при отправке запросов к серверам.
Каждая функция или новое техническое решение в нашем проекте выпускается под «флагом». Этот флаг хранится в профиле игрока и приходит клиенту при запуске игры вместе с балансом. Как правило, при выходе нового функционала, особенно технического, несколько клиентских релизов содержат оба функционала – старый и новый. Активация нового функционала происходит строго в соответствии с состоянием описанного выше флага, что позволяет отслеживать и корректировать техническую успешность того или иного решения в режиме реального времени.
Постепенно пришло время обновить функционал Dependency Injection в проекте. Текущий уже не справлялся с большим количеством зависимостей и в некоторых случаях вводил неочевидные связи, которые очень сложно разорвать и провести рефакторинг. (Особенно это касалось нововведений, так или иначе связанных с пользовательским интерфейсом.)
При выборе нового решения выбор был прост: всем известный Zenject, зарекомендовавший себя на других наших проектах. Этот проект уже давно находится в разработке, активно поддерживается, добавляются новые возможности, и многие разработчики в команде были с ним так или иначе знакомы.
Постепенно мы начали переписывать War Robots с помощью Zenject. С его помощью разрабатывались все новые модули, а старые постепенно подвергались рефакторингу. С момента использования Zenject мы получили более четкую последовательность загрузки сервисов в тех контекстах, которые мы обсуждали ранее (боевой и ангар), и это позволило разработчикам легче погружаться в разработку проекта, а также более уверенно разрабатывать новые функции. в этих контекстах.
В относительно новых версиях Unity появилась возможность работать с асинхронным кодом через async/await. В нашем коде уже использовался асинхронный код, но единого стандарта для всей кодовой базы не было, и поскольку серверная часть сценариев Unity начала поддерживать async/await, мы решили провести исследования и разработки и стандартизировать наши подходы к асинхронному коду.
Еще одной мотивацией было убрать из кода ад обратных вызовов — это когда идет последовательная цепочка асинхронных вызовов, и каждый вызов ждет результатов следующего.
В то время было несколько популярных решений, таких как RSG или UniRx; мы сравнили их все и собрали в одну таблицу:
| RSG.Обещание | ВГЗ | TPL с ожиданием | Юнитаск | UniTask с асинхронностью |
---|---|---|---|---|---|
Время до завершения, с | 0,15843 | 0,1305305 | 0,1165172 | 0,1330536 | 0,1208553 |
Первый кадр Время/Я, мс | 105,25/82,63 | 13.51/11.86 | 21.89/18.40 | 28.80/24.89 | 19.27/15.94 |
Распределение первых кадров | 40,8 МБ | 2,1 МБ | 5,0 МБ | 8,5 МБ | 5,4 МБ |
Второй кадр Время/Я, мс | 55,39/23,48 | 0,38/0,04 | 0,28/0,02 | 0,32/0,03 | 0,69/0,01 |
Распределение второго кадра | 3,1 МБ | 10,2 КБ | 10,3 КБ | 10,3 КБ | 10,4 КБ |
В конечном итоге мы остановились на использовании встроенного async/await в качестве стандарта для работы с асинхронным кодом C#, с которым знакомо большинство разработчиков. Мы решили не использовать UniRx.Async, так как преимущества плагина не перекрывают необходимость полагаться на стороннее решение.
Мы решили пойти по пути минимально необходимого функционала. Мы отказались от использования RSG.Promise или холдеров, поскольку, во-первых, нужно было обучить новых разработчиков работе с этими, как правило, незнакомыми инструментами, а во-вторых, нужно было создать обертку для стороннего кода, которая использует асинхронные задачи в RSG.Promise или держателях. Выбор async/await также помог стандартизировать подход к разработке проектов компании. Этот переход решил наши проблемы — мы получили четкий процесс работы с асинхронным кодом и убрали из проекта сложный в поддержке ад обратных вызовов.
Пользовательский интерфейс постепенно улучшался. Поскольку в нашей команде функционал новых фич разрабатывается клиентскими программистами, а версткой и интерфейсом занимается UI/UX-отдел, нам нужно было решение, которое позволило бы распараллелить работу над одной и той же фичей – чтобы Верстальщик может создавать и тестировать интерфейс, пока программист пишет логику.
Решение этой проблемы было найдено при переходе на MVVM-модель работы с интерфейсом. Эта модель позволяет разработчику интерфейса не только спроектировать интерфейс без привлечения программиста, но и увидеть, как интерфейс будет реагировать на определенные данные, когда фактические данные еще не подключены. После некоторого исследования готовых решений, а также быстрого прототипирования нашего собственного решения под названием Pixonic ReactiveBindings, мы составили сравнительную таблицу со следующими результатами:
| процессорное время | Память Аллок | Память Применение |
---|---|---|---|
Привязка данных перечной мяты | 367 мс | 73 КБ | 17,5 МБ |
ДисплейFab | 223 мс | 147 КБ | 8,5 МБ |
Единство Сварки | 267 мс | 90 КБ | 15,5 МБ |
Pixonic ReactiveBindings | 152 мс | 23 КБ | 3 МБ |
Как только новая система зарекомендовала себя, в первую очередь в плане упрощения производства новых интерфейсов, мы начали использовать ее для всех новых проектов, а также всех новых функций, появляющихся в проекте.
К 2019 году было выпущено несколько устройств от Apple и Samsung, достаточно мощных в плане графики, поэтому в нашей компании возникла идея значительного обновления War Robots. Мы хотели использовать возможности всех этих новых устройств и обновить визуальный образ игры.
Помимо обновленного изображения, у нас также было несколько требований к нашему обновленному продукту: мы хотели поддерживать игровой режим 60 FPS, а также разное качество ресурсов для разных устройств.
Текущий менеджер качества также требовал переделки, так как росло количество качеств внутри блоков, которые переключались на лету, снижая производительность, а также создавая огромное количество перестановок, каждую из которых приходилось тестировать, что накладывало дополнительные затраты на команду контроля качества при каждом выпуске.
Первое, с чего мы начали при переделке клиента, — это рефакторинг стрельбы. Исходная реализация зависела от скорости отрисовки кадров, поскольку игровая сущность и ее визуальное представление в клиенте были одним и тем же объектом. Мы решили разделить эти сущности, чтобы игровая сущность кадра больше не зависела от ее визуальных эффектов, и это обеспечило более справедливую игру между устройствами с разной частотой кадров.
В игре происходит большое количество выстрелов, но алгоритмы обработки данных от них достаточно простые – перемещение, определение попадания во врага и т.д. Концепция Entity Component System идеально подошла для организации логики движения снаряда. в игре, поэтому мы решили придерживаться этого.
Кроме того, во всех наших новых проектах мы уже использовали ECS для работы с логикой, и пришло время применить эту парадигму к нашему основному проекту для унификации. В результате проделанной работы мы реализовали ключевое требование — обеспечение возможности запуска изображений со скоростью 60 FPS. Однако не весь боевой код был перенесен в ECS, поэтому потенциал такого подхода не удалось реализовать в полной мере. В конечном итоге мы не улучшили производительность так сильно, как хотелось бы, но и не ухудшили ее, что по-прежнему является важным показателем при таком сильном сдвиге парадигмы и архитектуры.
В то же время мы начали работать над нашей версией для ПК, чтобы посмотреть, какого уровня графики мы сможем достичь с помощью нашего нового графического стека. Он начал использовать Unity SRP, которая на тот момент еще находилась в предварительной версии и постоянно менялась. Картина, которая вышла для Steam-версии, была весьма впечатляющей:
Кроме того, некоторые графические функции, показанные на изображении выше, можно перенести на мощные мобильные устройства. Особенно это касалось устройств Apple, которые исторически имеют хорошие характеристики производительности, отличные драйверы и плотное сочетание аппаратного и программного обеспечения; это позволяет им производить картинку очень высокого качества без каких-либо временных решений с нашей стороны.
Мы быстро поняли, что изменение графического стека само по себе не изменит изображение. Также стало очевидно, что помимо мощных устройств нам понадобится контент для более слабых устройств. Итак, мы начали планировать разработку контента разного уровня качества.
Это означало, что механизмы, оружие, карты, эффекты и их текстуры должны были быть разделены по качеству, что означает разные объекты для разных качеств. То есть физические модели, текстуры и набор текстур мехов, которые будут работать в HD-качестве, будут отличаться от мехов, которые будут работать в других качествах.
Кроме того, игра отводит картам большое количество ресурсов, и их тоже необходимо переделывать, чтобы они соответствовали новым качествам. Также стало очевидно, что нынешний менеджер по качеству не отвечает нашим потребностям, поскольку не контролирует качество активов.
Итак, сначала нам нужно было решить, какие качества будут доступны в нашей игре. Учитывая опыт нашего нынешнего менеджера по качеству, мы поняли, что в новой версии мы хотим несколько фиксированных качеств, которые пользователь сможет самостоятельно переключать в настройках в зависимости от максимально доступного качества для данного устройства.
Новая система качества определяет, каким устройством владеет пользователь, и на основе модели устройства (в случае iOS) и модели графического процессора (в случае Android) позволяет нам установить максимально доступное качество для конкретного игрока. В этом случае это качество, как и все предыдущие качества, имеется.
Также для каждого качества есть настройка переключения максимального FPS между 30 и 60. Изначально мы планировали иметь около пяти качеств (ULD, LD, MD, HD, UHD). Однако в процессе разработки стало понятно, что такое количество качеств будет развиваться очень долго и не позволит снизить нагрузку на QA. Учитывая это, в конечном итоге мы получили в игре следующие качества: HD, LD и ULD. (Для справки, текущее распределение аудитории, играющей с этими качествами, следующее: HD - 7%, LD - 72%, ULD - 21%).
Как только мы поняли, что нам нужно реализовать больше качеств, мы начали думать, как сортировать активы по этим качествам. Чтобы упростить эту работу, мы договорились о следующем алгоритме: художники создают активы максимального качества (HD), а затем с помощью скриптов и других инструментов автоматизации части этих активов упрощаются и используются как активы для других качеств.
В процессе работы над данной системой автоматизации мы разработали следующие решения:
Была проделана большая работа по рефакторингу существующих ресурсов механизмов и карт. Оригинальные мехи не были разработаны с учетом различных качеств и, следовательно, хранились в отдельных префабах. После рефакторинга из них были извлечены базовые части, в основном содержащие их логику, и с помощью Prefab Variants созданы варианты с текстурами для определенного качества. Плюс мы добавили инструменты, которые могли бы автоматически разделять меха на разные качества с учетом иерархии папок хранения текстур в зависимости от качеств.
Чтобы доставить конкретные активы на конкретные устройства, необходимо было разделить весь контент в игре на бандлы и пакеты. Основной пакет содержит ресурсы, необходимые для запуска игры, ресурсы, которые используются во всех качествах, а также пакеты качества для каждой платформы: Android_LD, Android_HD, Android_ULD, iOS_LD, iOS_HD, iOS_ULD и так далее.
Разделение активов на пакеты стало возможным благодаря инструменту ResourceSystem, который был создан командой нашей Платформы. Эта система позволяла нам собирать ресурсы в отдельные пакеты и затем встраивать их в клиент или отдельно брать для загрузки на внешние ресурсы, например CDN.
Для доставки контента мы использовали новую систему, также созданную командой платформы, так называемую DeliverySystem. Эта система позволяет получать манифест, созданный ResourceSystem, и загружать ресурсы из указанного источника. Этим источником может быть монолитная сборка, отдельные APK-файлы или удаленный CDN.
Изначально мы планировали использовать возможности Google Play (Play Asset Delivery) и AppStore (On-Demand Resources) для хранения пакетов ресурсов, но на платформе Android было много проблем, связанных с автоматическим обновлением клиента, а также ограничениями на количество хранимых ресурсов.
Далее наши внутренние тесты показали, что лучше всего и стабильнее работает система доставки контента с CDN, поэтому мы отказались от хранения ресурсов в магазинах и начали хранить их в нашем облаке.
Когда основная работа над ремастером была завершена, пришло время релиза. Однако мы быстро поняли, что первоначальный запланированный размер в 3 ГБ был плохим для загрузки, несмотря на то, что ряд популярных игр на рынке требуют от пользователей загрузки 5-10 ГБ данных. Наша теория заключалась в том, что пользователи привыкнут к этой новой реальности к тому времени, когда мы выпустим на рынок обновленную игру, но, увы.
Другими словами, игроки не привыкли к таким большим файлам для мобильных игр. Нам нужно было быстро найти решение этой проблемы. Через несколько итераций мы решили выпустить версию без HD-качества, но все равно доставили ее нашим пользователям позже.
Параллельно с разработкой ремастера мы активно работали над автоматизацией тестирования проекта. Команда контроля качества проделала большую работу, гарантируя автоматическое отслеживание статуса проекта. На данный момент у нас есть сервера с виртуальными машинами, на которых запускается игра. Используя самописный фреймворк тестирования, мы можем нажать любую кнопку в проекте со скриптами — и одни и те же скрипты используются для запуска различных сценариев при тестировании непосредственно на устройствах.
Мы продолжаем развивать эту систему: уже написаны сотни постоянно работающих тестов для проверки стабильности, производительности и корректности выполнения логики. Результаты отображаются на специальном дашборде, где наши QA-специалисты вместе с разработчиками могут детально изучить наиболее проблемные места (в том числе реальные скриншоты из тестового запуска).
До ввода текущей системы в эксплуатацию регрессионное тестирование занимало много времени (около недели на старой версии проекта), которое к тому же было значительно меньше текущего по объему и количеству контента. Но благодаря автоматическому тестированию текущая версия игры тестируется всего две ночи; это можно улучшить, но в настоящее время мы ограничены количеством устройств, подключенных к системе.
Ближе к завершению ремастера с нами связалась команда Apple и предоставила возможность принять участие в презентации нового продукта, чтобы продемонстрировать возможности нового чипа Apple A14 Bionic (выпущенного вместе с новыми iPad осенью 2020 года) с помощью нашего новая графика. В ходе работы над этим мини-проектом была создана полностью рабочая HD-версия, способная работать на чипах Apple со скоростью 120 FPS. Кроме того, было добавлено несколько графических улучшений, чтобы продемонстрировать мощь нового чипа.
В результате конкурентного и достаточно жесткого отбора наша игра попала в осеннюю презентацию Apple Event! Посмотреть и отпраздновать это событие собралась вся команда, и это было действительно круто!
Еще до запуска обновлённой версии игры в 2021 году возникла новая задача. Многие пользователи жаловались на проблемы с сетью, причиной которых было наше текущее решение на тот момент — транспортный уровень Photon, который использовался для работы с Photon Server SDK. Другой проблемой было наличие большого и нестандартизированного количества RPC, которые отправлялись на сервер в случайном порядке.
Кроме того, также были проблемы с синхронизацией миров и переполнением очереди сообщений в начале матча, что могло вызвать значительные лаги.
Чтобы исправить ситуацию, мы решили перенести стек сетевых совпадений в сторону более традиционной модели, где связь с сервером происходит через пакеты и получение игрового состояния, а не через RPC-вызовы.
Новая архитектура онлайн-матчей называлась WorldState и была предназначена для удаления Photon из игрового процесса.
Помимо замены транспортного протокола с Photon на UDP на основе библиотеки LightNetLib, эта новая архитектура также предусматривала оптимизацию системы связи клиент-сервер.
В результате работы над этой функцией мы сократили затраты на серверной стороне (перешли с Windows на Linux и отказались от лицензий Photon Server SDK), в итоге получили протокол, гораздо менее требовательный к конечным устройствам пользователей, что уменьшило количество проблем. с десинхронизацией состояний между сервером и клиентом и создал возможность разработки нового PvE-контента.
В одночасье изменить весь код игры было бы невозможно, поэтому работа над WorldState была разделена на несколько этапов.
Первым этапом стал полный редизайн протокола связи между клиентом и сервером, а движение мехов было перенесено на новые рельсы. Это позволило нам создать новый режим игры: PvE. Постепенно на сервер стали переезжать механики, в частности самые последние (повреждения и критические повреждения мехов). Работа над постепенным переносом старого кода в механизмы WorldState продолжается и в этом году у нас также будет несколько обновлений.
В 2022 году мы запустили новую для нас платформу: Facebook Cloud. Концепция платформы была интересной: запуск игр на эмуляторах в облаке и потоковая передача их в браузер на смартфонах и ПК без необходимости для конечного игрока иметь мощный ПК или смартфон для запуска игры; необходимо только стабильное подключение к Интернету.
Со стороны разработчика в качестве основных, которые будет использовать платформа, могут распространяться два типа сборок: сборка Android и сборка Windows. Мы выбрали первый путь из-за большего опыта работы с этой платформой.
Чтобы запустить нашу игру в Facebook Cloud, нам нужно было внести несколько изменений, например, переделать авторизацию в игре и добавить управление курсором. Нам также нужно было подготовить сборку со всеми встроенными ресурсами, поскольку платформа не поддерживала CDN, и нам нужно было настроить наши интеграции, которые не всегда могли корректно работать на эмуляторах.
Большая работа была также проделана с графической стороны, чтобы обеспечить функциональность графического стека, поскольку эмуляторы Facebook не были настоящими Android-устройствами и имели свои особенности с точки зрения реализации драйверов и управления ресурсами.
Однако мы увидели, что пользователи этой платформы столкнулись со многими проблемами — как с нестабильным подключением, так и с нестабильной работой эмуляторов, и Facebook принял решение закрыть свою платформу в начале 2024 года.
Что постоянно происходит на стороне программистов и что невозможно подробно обсудить в такой небольшой статье, так это регулярная работа с техническими рисками проекта, регулярный мониторинг технических метрик, постоянная работа с памятью и оптимизацией ресурсов, поиск проблем в сторонние решения партнерских SDK, рекламные интеграции и многое другое.
Кроме того, мы продолжаем исследовательскую и практическую работу по исправлению критических сбоев и ошибок ANR. Когда проект работает на таком большом количестве совершенно разных устройств, это неизбежно.
Отдельно хотелось бы отметить профессионализм людей, которые работают над поиском причин проблем. Эти технические проблемы зачастую носят сложный характер и приходится накладывать друг на друга данные нескольких аналитических систем, а также проводить некоторые нетривиальные эксперименты, прежде чем будет найдена причина. Зачастую наиболее сложные проблемы не имеют последовательно воспроизводимого случая, который можно было бы проверить, поэтому для их решения часто используются эмпирические данные и наш профессиональный опыт.
Следует сказать несколько слов об инструментах и ресурсах, которые мы используем для поиска проблем в проекте.
Это лишь небольшой список технических усовершенствований, которым претерпел проект за время своего существования. Трудно перечислить все, что было сделано, так как проект постоянно развивается, а технические менеджеры постоянно работают над составлением и реализацией планов по улучшению производительности продукта как для конечных пользователей, так и для тех, кто работает с ним внутри студии, в том числе для сами разработчики и другие отделы.
У нас еще есть много улучшений, которые продукт будет иметь в следующем десятилетии, и мы надеемся, что сможем поделиться ими в наших следующих статьях.
С днем рождения, War Robots, и спасибо огромной команде технических специалистов, которые делают все это возможным!