Всех с Новым Годом! Меня зовут Григорий Дядиченко, и я технический продюсер. Размер билда. Сегодня хотелось бы поговорить о нём и составить некий чек-лист, который полезно проходить перед выкладкой проекта. Если вам интересно, как можно инструментами Unity уменьшить вес сборки — добро пожаловать под кат!

Одна из больших задач в разработке с которой все постоянно сталкиваются — это оптимизация веса билда. В вебе лучше иметь минимальный вес приложения. В мобильных сторах существует ограничение, что билд должен весить не больше 200 мб для скачивания по мобильной сети на IOS и 150 мб для Android в случае апп бандла. Такое ощущение что плевать на размер сборки только разработчикам Call Of Duty, но пусть это будет на их совести. Перед релизом важно пройтись по ассетам в вашем проекте и подрезать что и где можно.

Из чего состоит приложение?

Перед тем, как начинать оптимизировать нужно понимать что мы можем оптимизировать и что стоит оптимизировать. Приложение состоит из:

  1. Текстуры

  2. 3д модели

  3. Анимации

  4. Звуки

  5. Шейдеры

  6. Код

  7. Код движка

Мы пойдём от простых оптимизаций к сложным. От более эффективных и первоочерёдных, к тем что нужно делать в "самый последний момент". И начнём мы пожалуй с текстур.

Оптимизация текстур — Текстурные Атласы

Текстуры — это общая боль для 2д и для 3д проектов. Поэтому начнём с них. И начнём с самого простого понятия текстурный атлас.

Текстурный атлас — это изображения собирающее в себе другие изображения. По сути берётся большая текстура и через uv-координаты разбивается на зоны, в которых будут лежать другие текстуры. Это самая база, но это важно знать. Текстурные атласы оптимизируют как отрисовку, так и вес билда.

Для 2д и интерфейсов в Unity есть механизм Sprite Atlas. Чтобы атласы заработали, нужно в настройках Project Settings->Editor->Sprite Packer включить Sprite Atlas. Если он не будет включен, то созданные атласы не будут ничего делать.

Чтобы создать атлас в Unity. Нужно в файлах проекта сделать Create->2D->Sprite Atlas. И дальше в поле Objects for Packing положить нужные текстуры (так же можно положить папку).

Важно: чтобы избежать артефактов в UI отключайте настройки Allow Rotation и Tight Packing. Для SpriteRenderer всё с этими настройками будет работать нормально.

В случае 3д игр атласы собираются обычно в 3д софте. Есть ассеты для объединения материалов и формирования атласов для 3д. Но это больше про оптимизацию рендера, чем про оптимизацию веса сборки.

Оптимизация текстур — Настройки Сжатия

Когда атласы настроены можно попробовать их ещё пожать или пожать текстуры. Тут нет конкретного рецепта. Скажем если вы ориентируетесь только на топовые мобильные устройства, то там чаще всего будет работать без проблем сжатие ASTC, даже на андроид. Можно по выбирать разные варианты самого сжатия и настроить их так чтобы найти баланс между весом и артефактами. Например возьмём обложу из одной статьи.

С настройками по умолчанию, да ещё она у нас и NPOT (размеры не степени двойки) она весит 2.6 мб.

Вернувшись к прошлому пункту. Мы засунем её в спрайт атлас. Теперь она весит 2.0 мб, хотя она стала больше. Как так вышло? Раньше она была не компрессированная (так как размер текстуры был не степеней двойки и некоторые алгоритмы компрессии в этом случае не работают), а теперь она с компрессией ETC2. Но мы же хотим увидеть "настоящее сжатие".

И тут уже посмотрим на два скрина. Стоит учитывать что некоторые форматы сжатия не поддерживаются устройствами.

Было: (2мб)

Стало: (230кб)

И вот мы размер нашей текстуры с 2.6 мб снизили до 230кб. Больше чем в 10 раз. Так как это глубокая степень оптимизации важно такие вещи хорошо тестировать.

Важно: не имеет никакого значения сколько файл весит у вас на диске. А то некоторые оптимизируют текстуры внешними утилитами. Это работает только если текстуры идут в билд as-is, скажем через папку StreamingAssets. Но обычно лучше использовать настройки компрессии движка ничего не выдумывая.

Оптимизация текстур — Градиенты

Главное что может сломать компрессия — это градиенты. Но градиенты это математический объект, который в проекте может вообще не занимать место. По этой теме я писал целую статью.

В общих чертах градиенты на кнопках и на фонах лучше делать шейдерами. Странно видеть в проекте фон в виде градиента большим спрайтов 2048х2048. Который скажем на андроиде либо идёт без сжатия, либо с артефактами лесенкой. Лучше просто сделать шейдер. По производительности шейдеры тривиальные. Вес приложения будет ниже. Выглядеть будет красивее.

Оптимизация текстур — Подход к интерфейсам

Этот пункт не всегда возможен. Но часто (особенно в бизнес приложениях) весь интерфейс можно собрать "из одного кружка". Вот пример довольно простых вариантов.

Часто весь интерфейс — это набор иконок, 9-slice спрайтов и градиентов. Сделав один фрейм рамки для Sliced режима отображения. Используя в качестве градиентов шейдеры из этой статьи + маски. Можно собрать большую часть окон, кнопок и т.п. И это скорее подход к продакшену на старте проекта. К требованиям к художникам и так далее. Очень печально видеть в атласах какие-то огромные фоны окон, особенно если они легко делались аналогичной техникой. Допустим если брать окна посложнее, то вот пример симпатичнее сделанный так же.

Фон кнопок общий. Попап собирается из маленьких квадратов внизу и мелких спрайтов, которые делают эффект "рваной бумажки". Большие фоны просто нужны для получения размеров макета на данном листе. Получается сложное окно состоящее из атласа 256 х 128 хотя сам размер окна 388х473 и при сборке "в тупую" — это было бы два спрайта подобного размера.

То есть в основном в атласах должны быть иконки и подобные формы для 9-slice. Подробнее про 9-slice можно прочесть тут.

Важно: Почему не использовать шейдер для скруглений и подобного? Как показано выше — формы бывают самые разные, но дело не только в этом. При использовании генерации меша или шейдера трудно будет сделать в общем случае антиалиасинг, чтобы избежать лесенки на этих скруглениях. Поэтому лучше использовать спрайты и 9-slice технику.

Оптимизация моделей

С моделями важно понимать из чего складывается вес модели. Анимации мы вынесем в отдельный пункт и посмотрим что за вертексные параметры и что с ними можно делать?

Изменения веса модели в целом в Unity увидеть нельзя (только в логах редактора в сборке). Но его можно оценить, если посмотреть на меш модели. Вот для примера мы видим что вертекс в нашей модели весят 1 мб. Можем ли мы как-то это уменьшить? Да, но тут есть нюанс. Важно понимать за что отвечают вертексные параметры. Например в наших AR проектах мы пользуемся шейдерами, которым чаще всего не нужны нормали. Давайте их отключим. Это делается тут.

И вес вертексов уменьшился в два раза. При этом мы ничего не потеряли (в контексте проекта).

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

Тут же хочется сказать про Mesh Compression не вынося это в отдельный пункт. Эта настройка тоже может позволить снизить вес модели достаточно существенно. Но она вызывает артефакты и с ней надо просто пройтись по моделям, посмотреть что ничего критичного не сломалось и оставить на нужном уровне.

Оптимизация анимаций — Подход к анимациям

Тут мы главным образом поговорим про 3д. Самым главным требованием в анимациям в 3д модели в Unity является то, что все анимации должны быть внутри одной модели. В этом плане не очень удобно то же mixamo, так как оно качает отдельный fbx на каждую анимацию. Почему это плохо? В таком случае вы просто тянете кучу лишних моделей, так как движок не понимает, что это одна и та же модель. FBX же разные.

Поэтому анимации должны быть в одном fbx и уже в Unity нарезаться на отдельные клипы.

Правда всё описанное выше важно только если вы грузите анимацию по путям. То есть они лежат в папке Resources. В остальных случаях (как меня поправили в комментариях). В логе будет fbx, но на самом деле в сборку из него пойдёт только анимация.

Остальное это лишь настройка ошибки и режима компрессии анимации, которое так же делается для каждой модели по принципу: "пока не появились критичные артефакты".

Оптимизация звука, шейдеров, кода

Тут в Unity нет ничего особо интересного в плане настроек или не такое сильное влияние на вес билда. Звук можно ещё попробовать оптимизировать снижая его качество в настройках компрессии. А по остальному "использовать всё легковесное".

Оптимизация кода движка

А вот это довольно сложно и рискованно. Делается это через отключение Built-in пакетов и включение Strip Enigne Code — High. На том же андроид это позволяет выиграть около 5мб общего веса приложения. И лучше эту настройку включать на старте проекта чтобы понимать что именно сломалось. Так как Unity часто скажет не "в чём проблема", а скажет "попробуй отключить Strip Engine Code". Это может сломать часть функционала, сломать ассет бандлы, сломать много чего ещё. Но пользоваться этим можно.

В первую очередь для этого важно разбираться в том, что такое link.xml, что подробно описано в мануалах Unity по стрипингу и по Unity linker'у. Но сама тема того, как с этим работать чтобы при создании бандлов ничего не ломалось — тянет на отдельную статью.

В заключении — чеклист

Спасибо за внимание! В конце хочется собрать небольшой список действий, который стоит проходить перед релизом проекта или новой фичи чтобы сохранять размер сборки небольшим. Но сначала хочется сказать про то о чём не было сказано. Про основной инструмент анализа размера билда. Лог редактора.

Если вы изначально не нацелены на задачу "вес билда должен быть минимальным" как это делается скажем для промо-проектов в вебе или playable ads. Чаще всего достаточно пожать текстуры и 3д модели. Остальные оптимизации начинаются когда "очень надо выиграть последний мегабайт". Но в первую очередь надо посмотреть что в целом занимает место в сборке. По логу чаще всего будет сразу видно странности и где "что-то не так".

Итак, а теперь чек-лист:

  1. Посмотреть лог редактора по сборке и найти странности

  2. Проверить что все текстуры лежат в атласах

  3. Настроить сжатие атласов под целевые платформы

  4. Проверить в сборке нет градиентов, которые можно заменить шейдером

  5. Посмотреть вес самых больших 3д моделей и попробовать поменять Mesh Comprassion проверяя результат на устройствах

  6. Если какие-то вертексные параметры не используются попросить моделлеров удалить их из модели и отключить их импорт в Unity

  7. Проверить что в проекте нет "дубликатов моделей" ради анимаций

  8. Опционально: Пройтись по настройкам качества звуков и для самых больших поискать баланс между качеством и размером

  9. К крайнем случае: Включить Strip Engine Code — High и починить всё, что сломается (никогда не делать прям перед релизом)

В общем всё. Остальное уже тонкие оптимизации зависящие от контекста проекта. Типа техники палитр или же паковки множества текстур в одну текстуру. Как можно увидеть в статье многие оптимизации делаются не в последний момент, а выстраивается на уровне процессов. Так что желаю всем небольших сборок и больших успехов в разработке игр! Подписывайтесь на мой блог в телеграм, там много интересного.

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


  1. gazkom
    05.01.2023 10:16

    Мне интересно кто такой технический продюсер. А то гугл что-то невнятное на эту тему пишет.


    1. DyadichenkoGA Автор
      05.01.2023 10:18

      Да это скорее я себя так обозвал. Можно почитать в целом на сайте у меня https://noxatra.ru/

      А так, если коротко. Аля как продюсер в кино, только в диджитал проектах. Нахожу проект, составляю бюджет, подбираю команду, руковожу процессом производства и так далее.


  1. Tutanhomon
    05.01.2023 11:07
    +1

    что-то мне думается, что вы не правы насчет того, что если анимации лежат по разным fbx, то это увеличит размер билда. Не fbx ведь в билд попадает, а импортированные Юнити и конвертированные бинарные данные из него - отдельно меши и анимации, в зависимости от того что вы используете в сценах/префабах. Из FBX с анимацией в билд попадут только анимационные клипы, на которые будет ссылаться ваш Animator/Animation.


    1. DyadichenkoGA Автор
      05.01.2023 11:15
      +1

      Ну в целом да. Я перепроверил сейчас. И судя по вот этому.

      В билд идёт только анимация. Но есть исключение, которое я добавлю. Если оно лежит в ресурсах. Просто бывает такое, что fbx кладётся в ресурсы. Чтобы загружать модель по пути, а не по ссылке. И нужно дописать, что это конкретно про этот кейс.


  1. Tutanhomon
    05.01.2023 11:11
    +1

    Вообще, в контексте "Размера билда Юнити", хотелось увидеть что-то более неожиданное, как например уменьшение размера даже пустого билда, путем пересмотра содержимого PackageManager, или еще каких-нибудь грязных трюков :)


    1. DyadichenkoGA Автор
      05.01.2023 11:21

      Не, я на старте заявил, что тут разбираются инструменты именно Unity. А там мало неожиданного. Просто остальное очень контекстуально. Возьмём палитры. Допустим у нас 8-ми битная игра. Но допустим большая часть компрессий не позволяет настраивать в Unity битность текстуры. Поэтому получается если у нас текстуры 32 бита, а при этом 8 бит на цвет, то в одной текстуре мы можем хранить 4 текстуры. И можно написать инструмент. Но применимо это только для 8-ми битных игр.

      Причём очевидно что лучше не использовать JPEG подобные алгоритмы сжатия, так как по принципу работы JPEG там будут весьма специфичные артефакты. Так как цветовая информация в JPEG ужимается. А без изменений остаётся грейскейл. И так далее.


  1. Suvitruf
    05.01.2023 11:31

    билд должен весить не больше 100 мб для скачивания по мобильной сети на IOS

    Нет же. Ещё в 2017 повысили. А сейчас наверно ещё выше лимит.


    1. DyadichenkoGA Автор
      05.01.2023 11:33

      Ща исправлю. 200МБ, если в настройки не лезть телефона. С аойс 13 можно поставить что любой размер будет качаться.



  1. dm_bondarev
    05.01.2023 15:20
    +1

    Strip Enigne Code — High

    Strip Enigne Code это чекбокс. High это про Managed Stripping Level

    нет ничего особо интересного в плане настроек или не такое сильное влияние на вес билда

    шейдеры на скриншоте по размеру в 10-ке. в Stripping scriptable shader variants утверждают, что эта операция уменьшила "вес" шейдеров в билде примерчика с 511 до 151 МБ


  1. focus
    06.01.2023 01:13
    +1

    Спасибо за статью, мои 6 копеек в копилку:

    1. Правильные настройки качества и сжатия звуков довольно важный аспект оптимизации проекта в котором планируется богатое звуковое сопровождение. В таких проектах это один из тех пунктов оптимизации, что следует учитывать на уровне процессов, например организовав шаблонные настройки сжатия для звуков разного типа (музыка, sfx, речь и т.п.). Это поможет не только снизить вес билда, но и поспособствует более рациональному использованию ресурсов CPU, I/O и RAM в рантайме. Есть неплохая статья с разумными выводами относительно настроек импорта звуков.

    2. Существенно снизить объем билда можно срезав неиспользуемые варианты шейдеров, особенно когда вам заранее известен спектр железа под которым будет работать проект. Выше есть комментарий на эту тему со ссылкой на соответствующий пост в блоге Unity.

    3. А ещё есть настройка алгоритма сжатия билда: Compression Method, прямо в Build Settings. На некоторых проектах и платформах выбор правильного алгоритма сжатия может снизить вес крайне существенно ценой увеличения времени запуска и чтения упакованных ресурсов (порой совсем незначительного).

    4. Часто в Sprite Atlas'ах оказываются неиспользуемые текстуры бесполезно раздувая их размеры и количество, за этим стоит поглядывать.

    5. Стоит упомянуть классику с неверным использованием папок Resources, когда в них лежат ассеты, не подгружаемые в рантайме через Resources.* API. Это касается и вложенных папок.

    6. Для анализа Build Report у Unity есть пакет Build Report Inspector, с ним может быть поудобнее.


    1. DyadichenkoGA Автор
      06.01.2023 01:24

      Да, спасибо. Отличное дополнение :)

      Я мало с жирными в плане звука проектами работал. Ну и по остальным пунктам согласен.


    1. Tutanhomon
      06.01.2023 12:06

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


      1. focus
        06.01.2023 15:11
        +1

        Да, это атавизм давних времён, но на удивление всё ещё часто встречается в различных проектах студий от мала до велика, причём не только в легаси контексте. А ещё иногда это всё-таки удобнее чем Addressables или бандлы, например при использовании внутри пакетов - не тянет лишних зависимостей и полностью инкапсулировано.

        В общем случае с вами согласен - если есть возможность отказаться от использования Resources - лучше так и поступить.