Особенности разработки мобильной MMO RTS. Часть 4

0c894cf085cc4a9f9c4c5a71e8ddf602.jpg

Содержание:


  1. Оптимизация размера игры
  2. Бандлы и загружаемые ресурсы. Что требуется от системы?
  3. Дифы манифеста
  4. Экономия на кодогенерации

Мы прошли экватор цикла о создании MMO RTS. Сегодняшняя статья посвящена оптимизации.

Оптимизации размера игры


Помните предупреждение от AppStore и GooglePlay о том, что вы пытаетесь загрузить приложение больше 100 Мб через мобильную сеть? Это сообщение очень сильно снижает показатели конверсии. Продвигать такие объемные игры очень дорого. Мы провели исследование и выяснили, что конверсия падает почти на 20%. Для бесплатных игр этот показатель на грани выживания, для платных не так критичен.

На iOS всё еще сложнее. Начиная с ARM64, в билд попадают 2 разных исполняемых файла, для 32-битной архитектуры и 64-битной. То есть вместо 70 Мб для вшитых ресурсов остается только 30.

На каждом iOS-билде мы проводим пессимистичное вычисление его размера. Для этого xCode-архив делится на ресурсы и исполняемый файл. Ресурсы жмутся в .zip, и результат суммируется с размером исполняемого файла. Этого показателя достаточно, чтобы узнать, как изменяется размер проекта. Более подробное руководство и формулы есть на сайте Unity.

Если вся графика правильно жмется, меши оптимизированы, а ненужные зависимости устранены, остается не так много решений — например, вынесение контента в бандлы и оптимизация кодогенерации.

Бандлы и загружаемые ресурсы. Что требуется от системы?


Вся базовая графика, необходимая для отображения UI пользователю после логина, остается в билде.

43c6e3dbd69a464e9b8a250fc2af75c0.png

Мы начали работать с бандлами еще на четвертой версии движка. В пятой версии Unity предлагает другой подход, но мы его не используем, потому что у нас уже есть готовая система.

Если вкратце, система работает так:

  1. В Unity-проекте есть набор папок, названия которых заканчиваются на .bundle. Содержимое этих бандлов — ресурсы, запрашиваемые клиентом в рантайме по имени файла этого ресурса.
  2. Через специальную утилиту разработчик билдит измененные бандлы и формирует манифест. Это текстовый файл с информацией, необходимой клиенту.
  3. Сформированные бандлы выливаются на контент-сервер, а манифест на сервер, который возвращает его клиентам во время логина. Бандлы могут обновиться или добавиться без обновления клиента.
  4. Клиент парсит манифест, загружает и кэширует бандлы, как только их запрашивает код.

В итоге у нас сформировались требования к манифесту.

b787a268fb3d480995990659574aca69.png

Работа с кэшем бандлов в Unity осуществляется через численное выражение версии. Мы храним эту информацию в манифесте. Когда нужно обновить файл, из версии формируется путь к бандлу на контент-сервере. Это гарантирует, что любое изменение — это новый файл.

Система поддерживает зависимости бандлов друг от друга. Часто разные 3D модели зависят от общего бандла с текстурами. Информация хранится в манифесте и используется в клиенте для приоритизации загрузки ресурсов.

Мы добавили возможность локализовать бандлы. Это нужно для игровых баннеров с текстами на разных языках и озвучки визарда.

Бандлы формируются для всех сжатий, которые мы поддерживаем. Клиент знает, с какой компрессией он работает, и формирует путь к запрашиваемому бандлу, добавляя в него имя компрессии.

0256d626f17e4172a609e9524c8be8f9.png

У нас в продакшне могут находиться разные версии приложения. Поэтому возможно использование разных ресурсов. Мы храним информацию для разных версий приложения в одном манифесте, но не в виде кучи наборов данных. Они хранятся в виде дифов от какой-то базовой версии, меньше которой в продакшне уже нет. На клиенте из базовой версии и всех наложенных дифов мы формируем финальный набор данных, который и используем.

Экономия на кодогенерации


Что касается кодогенерации, тут нужно понимать, как работает платформа .NET/Mono и IL2CPP. Обращайте внимание на generic-типы. Для .NET/Mono каждая специализация generic является отдельным типом. Для reference-типов специализации ссылаются на одну — Object. В случае с value-типами компилятору нужно учитывать размер объекта этого типа. Потом он создает отдельные специализации.

Основные проблемные места — классы с большой реализацией и generic-коллекции. Помимо типизированных массивов, неприятным моментом для нас оказалось то, что использование значений перечислений enum в качестве значения ключа в словаре приводит к генерации новой копии кода Dictionary.

Для выявления таких реализаций мы использовали ReSharper и его опцию Find generic substitutions. После того, как мы их обнаружили, стараемся свести количество специализаций к минимуму.

Предыдущие статьи:


  • Часть 1;
  • Часть 2;
  • Часть 3.

Комментарии (0)

© Habrahabr.ru