December 14, 2023

Оптимизация проекта Unity  

источник

Спрайты

Спрайты это основа оптимизации, поэтому с них и начнём.
Тут мы уменьшим размер игры и поднимем фэпэсы

Все спрайты должны быть такого размера, чтобы каждая из сторон, делилась на 4

  1. Если есть необходимость использовать квадрат, то используем спрайт с строго 1 пикселем. Стандартный квадратный спрайт что предлагает юнити как квадрат, имеет 256 пикселей. Благодаря тому что мы будем использовать квадрат с одним пикселем, можно снизить нагрузку на рендер.
  2. Если у вас фон в виде градиента, то его можно сделать в ширину в 1 пиксель, а в высоту в 1024. В таком состоянии, он будет занимать намного меньше места. И так же с флагами, не 128x128 для флага России, а 1x3.
  3. Не забываем сжимать текстуру так сильно, насколько это только возможно. Для этого нажимаем по картинке и в самом низу, настраиваем его размер, сжатия и формат.
  4. После второго и первого пункта, мы не можем сжать текстуры, но не беда, у юнити есть Sprite Atlas. Мы его создаём и закидываем туда наши текстуры, которые нарушают законы степени 2-х и теперь мы их уже можем сжать. В финальный билд, все эти текстуры так же попадут и сам Atlas сожрёт вам дополнительную память, поэтому смотрим на меня как на идиота и не используем Sprite Atlas для таких неоптимизированных вещей. Лучше будет если те же флаги будут в атласе, который вы сделаете сами и так же, чтобы он был в степени 2-х. Это и фпс поможет поднять, отрисовывать все вместе и текстуры будут занимать не так много места. Конечно можно использовать Sprite Atlas если по фпс будет плачевно, но он добавит свои копейки в финальный билд. Немного, но добавит
  5. Mip Maps на некоторых версиях юньки, могут быть включены.
  6. jpg весят меньше чем png. Правда юнити их итак редактирует под свой лад, поэтому разницы вы не увидите

Хочу отдельно поговорить про фоны в виде градиента. Вдруг у вас всевозможных вариантом штук 20, а это опять же, хранить 20 фонов - жирно. Поэтому мы делаем один спрайт с белым градиентом 1x1024 пикселя. Можно и на 512, но тут зависит от градиента и качества. И теперь мы можем скрестить квадратный спрайт 1x1 пиксель и градиент 1x1024 и мы можем менять градиент как нашей душе угодно и не увеличивая вес игры.
Пример:

Давайте ещё пример, как мы можем сделать такую вот кнопку в 345 байтов с помощью нашего градиента и uiextensions

Для начала создаём в unity спрайт круга. Он 256 на 256 пикселей по умолчанию

Поэтому нам надо его вшакалить. Выставляем такие настройки, как указаны слева

Теперь мы заходим в Sprite Editor и ставим зелёные (border) линии в центре. Это нам поможет закруглять текстуру за счет того, что заходится за зелёной линией.

Теперь мы можем добавить нашу кнопку с этим спрайтом и закруглить её. Выбираем в нашем Image Type - Slised и ставим то закругление, которое нам нужно. В моем случае это 9

Сейчас кнопка примерно такая

Теперь мы добавляем градиент под маску нашей кнопки и с помощью uiextensions, мы добавляем на наш градиент скрипт UIAdditiveEffect. Он добавит материал, после чего мы можем скрипт удалить и мы получаем тот результат, про который я вам и говорил. Можно и без uiextensions обойтись, но будет менее красиво. Да и он в своем багаже имеет много чего полезного. В основном партиклы на UI

Если научиться пользоваться Border'ами, то можно такие вот кнопки (без иконки) укладывать в 92x44 пикселя. У них так же как и у круга выше, есть 1 пиксель для высоты и ширины, все остальные пиксели - это сглаживание углов. И выглядит вполне неплохо и занимают мало места

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

Вам нужно сделать её черно белой. Белым цветом - блик, чутка серым центр и более тёмный низ. В итоге у нас будет одна кнопка вместо двух.

Форматы сжатия: (для HTML5)

Использовать буду 3 картинки, пиксель арт 16x16, белую иконку для UI 128x128, картинку 512x512. Т.к. у нас 3 картинки, я буду оценивать в таком формате:
*Название формата*, - Вес и степень сжатия (если можно её указать). Степень смотрибельности вы оцениваете сами, все картинки находятся в самом низу. Первая без сжатия. Однако, если с картинкой будет всё совсем плохо, я упомяну.

Если вам лень всё это читать, то минуйте в самый низ списка, там я уже избрал предпочтительные варианты из тех, что в этом списке

Без сжатия:

16 - 63кб (Текстура в атласе, поэтому и весит так. Но тут мы скорей смотрим на его качество. Размер атласа - 112x144)
128 - 64кб
512 - 1мб
Сжимая обычный способом через Default:
16 - 2.7кб, качество сжатия - 100 (шакал)
128 - 1.5кб, качество сжатия - 20
512 - 11.2кб, качество сжатия - 0
—————
RGB(A) Compressed ASTC 12x12 blocks.
16 - 1.9кб, качество сжатия- 100 (шакальный, жесть)
128 - 1.9кб, качество сжатия - 50
512 - 28.9кб, качество сжатия - 50.
Какое бы качество сжатия не менял, вес не менялся, а качество менялось только у 16-го—————
RGB(A) Compressed ASTC 10x10 blocks.

16 -2.8кб, качество сжатия- 100 (шакальный жесть, но лучше чем у верхнего)
128 - 2.6кб, качество сжатия - 0
512 - 42.3кб, качество сжатия - 0
—————
RGB(A) Compressed ASTC 8x8 blocks.

16 -3.9кб, качество сжатия- 100 (шакальный жесть, но лучше чем у верхнего)
128 - 4кб, качество сжатия - 0
512 - 64кб, качество сжатия - 0
————— Миную многие форматы, ибо иначе это затянется

RGB(A) Compressed ASTC 4x4 blocks.

- Лучший для пиксельных игр16 -15.8кб, качество сжатия- 50 (вполне красивый. В атласе были некоторые проблемы, но они на один пиксель и таких очень мало)128 - 4кб, качество сжатия - 0
512 - 64кб, качество сжатия - 0
—————
RGBA Crunched ETC2.

- Лучший для большинства текстур16 -3кб, качество сжатия- 100 (шакал)128 - 2кб, качество сжатия - 30 (разницы не вижу)
512 - 11кб, качество сжатия - 0 (разницы вообще не заметил)
—————
ARGB 16 bit.

По сути как обычный спрайт, но в 2 раза хуже и сжимать нельзя.
16 -31.5кб (Цвет на шлеме изменился)128 - 32кб
512 - 0.5мб
—————
RGB Crunched DXT1|BC1

Они без фона и качество сжатия ничего не меняло
16 -2.4кб, качество сжатия - 100 (шакал)
128 - 0.8кб, качество сжатия - 0
512 - 6.8кб, качество сжатия - 0

Самая первая картинка - без сжатия. И далее по порядку

Проведя такое исследование на этих подопытных картинках, я бы хотел подвести итог:

Для пиксельных спрайтов:

RGB(A) Compressed ASTC 4x4 blocks.
ARGB 16 bit.

Для белых иконок:

RGBA Crunched ETC2 - Лучший
ARGB 16 bit
RGB(A) Compressed ASTC 12x12 blocks
RGB(A) Compressed ASTC 8x8 blocks.
RGB(A) Compressed ASTC 4x4 blocks

Для цветных спрайтов:

RGBA Crunched ETC2 - Лучший
RGB(A) Compressed ASTC 4x4 blocks
RGB(A) Compressed ASTC 12x12 blocks
RGB(A) Compressed ASTC 8x8 blocks

Если мы сравним с обычным сжатием и с нашими форматами:
Пиксель арт:

Обычное - 63кб, без сжатия, ибо шакал
С сжатием - 2.7кб, качество сжатия - 100 (шакал)
RGB(A) Compressed ASTC 4x4 blocks - 15.8кб, качество сжатия - 30
Экономия: 47,2кб
Белые иконки:

Обычное - 16кб, без сжатия
С сжатием - 1.7кб, качество сжатия - 30
RGBA Crunched ETC2 - 1.7кб, качество сжатия 17
Экономия: отсутствует. Скорей что даже ухудшилась :)Цветные:Обычное - 256кб, без сжатия
С сжатием - 11.2кб, качество сжатия - 0
RGBA Crunched ETC2 - 11кб, качество сжатия 0
Экономия: 0.2мб.

Звуки

Рекомендую обратить на это внимание, т.к. для некоторых типов игр, это поможет ускорить загрузку игры на 5-20%
Рекомендую статью: https://habr.com/ru/post/437474/

  1. Сжимаем качество прямо в юньке. Некоторые звуки\музыку можно перевести в режим Mono, это конечно ударит по качеству, но звук будет весить меньше. Хотя бывают странные ситуации, что весить наоборот стало больше :\
  2. Меняем параметр как звуки будут загружаться. Благодаря этому параметру, мы можем пустить игрока в игру до того, как загрузится вся твоя музыка в игре, которая может идти в общей сложности минут 10. Для этого надо настроить Load in Background и Preload Audio Data. Подробнее есть в статье выше, если лень — то скриншоту ниже и настраивайте загрузку звука в зависимости от его предназначения.Тестировать мы будем на сцене, где сразу находятся 8 объектов и они сразу же начинают воспроизводить свою музыку.
    Общая длина всех композиций, составила бы: 424 сек или 7,14 минут
    И вот какие результаты:
    Игра ждёт когда загрузятся звуки: 7,9с
  3. Игра не ждёт когда закончится загрузка звуков: 6,95с
  4. Игра начинает загрузку звука, когда вызвать звук: 6,85с.
  5. Это всё на уровне погрешности и диких неточностей, но вероятнее всего для третьего пункта проверка была не удачной, т.к. звук так итак был со старта и вызывался сразу.
    В итоге, мы сократили время загрузки на 12%

Форматы звука:

Недооценённая тема, ведь звуков много и места жрут так же много. И вот у нас есть музыка которая идёт 83 секунды и она в формате .wav. Я их конвертирую с помощью сайта (да-да, это не правильно, я шарю) и посмотрим насколько хорошо этой выйдет.
Т.к. сайты сжимает аудио сами по себе, то упомяну что .wav я так же прогнал через этот сайт. Было 30мб, стало 14мб. Однако в юнити на импорте так и осталось 3.8мб, хотя качество и стало хуже

Так же, я укажу, если качество у звуков в юнити, поставить на 50
ogg - 1.5мб (0.8мб)
wav - 3.8мб (1.5мб)
mp3 -2.1мб (1.3мб)
flac - 3.8мб (1.5мб)
В итоге мы сократили вес на 40%

Настройки Audio в Project Settings

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

Картинка для загрузки в Template

Это же не сжатая картинка, не забывайте, её можно и даже нужно прогнать через сайты с сжатием фотографий и уменьшаем вес этой картинки на 50-70%

Splash Screen

Может показаться, что добавив свой фон к логотипу юнити - это классная идея, ведь этот фон уже используется в игре и поэтому дважды грузить его не надо, верно?

Однако у юнити своё мнение на этот счет, данную анимацию будут видеть 2 секунды, да, классную, но она будет идти как отдельная картинка. Так же, она не сжимается.
Данный фон, как на картинке выше, весит у нас 0,8мб

Шрифты

Шрифты, это то, что должно весить самую малую часть проекта, но нет. Вот к примеру стандартный шрифт LiberationSans занимает целых 350кб. Но мы же используем и TMP, так что добавляем 1мб.
Шрифты, которые поддерживают множество языков, могут весить итого больше

На данный момент, стандартный и никому не нужный шрифт занимает 1\4 часть пустого проекта.

Заходим в наш шрифт TMP и нажимаем «Update Atlas Texture». Предположим, что в моей игре ну совсем мало текста и впишем туда только то, что нужно нам. А именно: б, в, г, д, ж, з, й, к, л, м, н, п, р, с, т, ф, х, ц, ч, ш, щ, а, е, ё, и, о, у, ы, э, ю, я. Конечно пишем это без пробелов

После этого нажимаем на кнопку ниже «Generate Font Atlas» и нажимаем на кнопку «Save». Так же, если вам нужно буквально 10 букв, то хранить атлас с буквами в 1024 на 1024 нету смысла. Поэтому можете его сжать до 128 на 128. В моём случае 256 на 256. И теперь наша игра имеет только русский язык и шрифт занимает 260кб, а в процентном соотношении 7,1% в пустом проекте.

Но если нам такое не подходит и в игре много разных символов, то мы сжимаем наш атлас 512 на 512, оставляем в Atlas Population в Dynamic, а Sample Point Size мы выставляем на 50. Если шрифт особо сильно не поплыл, то ставим меньше. Padding же ставим на 1-5, это позволит нам поместить побольше символов или же улучшить немного качество уже существующих. В этом случае, шрифт из 1мб становится в 264кб.

Так же, нам больше не нужен стандартный шрифт, и вы можете его сжать по таким же параметрам. И вообще, если шрифт LiberationSans вам вовсе не нужен, можете всё по нулям выставить или даже удалить. Только в Project Settings не забудьте заменить стандартный шрифт на другой. Однако в некоторых версиях юнити, этот шрифт является священным и при его удалении проект может крашить)

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

Project Settings

Т.к. вариаций будет много, то всё буду показывать в упрощённом варианте. А для того чтобы тест был правдив, проверять будем на подопытной игре.
Всё проверялось на версии Unity 2021.3.18f1

Color Space в игре - Gamma 0fps — Linear 0fpsGraphics Jobs - выкл 0fps — вкл 0fpsApi Compatibility Level - .NET Standard 0fps .NET Standard 2.1 0fps

Enable Exceptions - благодаря нему, вы видите что за ошибки появляются в игре. Но сильно влияет на fps. Только прочтите про него подробней, чем менять что-либо. Т.к. если поставить на None, то при любой ошибке, игра крашнет)
None - К сожалению протестить не смогу, но я бы и не рекомендовал его использовать.Explicitly Thrown Exceptions Only - 110-120fps, 26.1mb
Full Without Stacktrace - 140-150fps, 26.3mb
Если вы не собираете аналитику по ошибкам внутри игры, ставьте на Full Without Stacktrace

Compression Format — Brotli vs Gzip
Brotli: 130fps 26.6мб (минимальный фпс - 70)Gzip: 110fps 30.4мб (минимальный фпс- 20)

Graphics Api — WebGL1 vs WebGL2 (Это всё ситуативно, но на заглушке из крутящихся сфер вышло так)Auto - 110fps 25.8мб
WebGL1 - 0fps 25.5мб
WebGL2 - 0fps 25.6мб

Stack Trace: не заметил каких-то отличий, не по билду, ни по фпс.

Suppress Common Warnings - Разницы в размере на пустом проекте не было

Strip Engine Code - вкл 3.75мб — выкл 5.09мб
Managed Stripping Level - к сожалению у меня удалялись важные скрипты, но эта вещь поможет вам удалить мусор. В моем случае да

Build Settings

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

Code optimization: [IL2CPP Code Generation - Faster runtime]
Speed - fps 160 | 26.9mb
Size - fps 120 | 26.1mb
Примечание: на телефонах, фпс может ещё сильнее различаться

IL2CPP Code Generation [Code optimization - speed]
Faster runtime - fps 0, игра загружалась 0с | 0mb
Faster (smaller) builds - fps 0, игра загружалась 0с | 0mb

Name Files As Hashes - выкл 26.4mb - вкл

Проект с которого проводились тесты:

Билды:

Occlusion culling

Анимации

Анимации можно отключать за экраном и этим надо пользоваться, ведь у тебя может проигрываться 30 сложных idle анимации за экраном. И не думайте что анимации не требовательны, сам прочувствовал на себе, когда у меня было анимировано 60 объектов. Я только за счет отключения анимации за экраном, получил +20~ фпс на почти мёртвом ноутбуке.
Документация, которая на 20 слов. Однако, изменений в редакторе вы не увидите, поэтому смотрите лишь на % нагрузки

Партиклы

Не буду цитировать документацию, там всего-то 20 слов :)

Occulusion Culling НЕ ВСЕГДА ПОДНИМАЕТ ФПС!

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

Динамическое разрешение

Я сделал для вас динамическое разрешение. Для URP и для WebGL.

Работает он так - есть DPI и то насколько низким мы можем его сделать. И если fps ниже чем X, то он немного ухудшает качество. Если фпс выше чем Y, то он повышает качество.

Я выставил такие настройки:

Fps до: 80-100
Fps после: 130-150

Скрипт для URP:
https://disk.yandex.ru/d/kFnms_cHt_9MTw

Аналогичный скрипт, но уже для WebGL:
XXX
Единственное что, ставьте время проверки фпс не слишком маленькое, ибо смена разрешения, создаёт дополнительную нагрузку на пк

Динамическое отключение баннеров при низком фпс

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

С баннером: 0fps
Без баннера: 0fps

Addressables

Предположим что вы уровни храните в префабах, (почему-бы нет?) и вы столкнулись с тем, что загрузка игры идёт крайне медленно. Это из-за того, что эти уровни сразу загружаются в память, если мы ссылаемся на них. В итоге, наша игра на старте загружает не одну менюшку, а менюшку и 50 уровней сверху. Благодаря Addressable мы это исправим. Это конечно не уменьшит вес игры, однако, это позволит игре быстрей загружаться и работать шустрей. Однако, если мы захотим с него загрузить уровень, то это будет дольше, но происходить это будет асинхронно, и благодаря этому, вы можете поставить экран загрузки. Или мини-игру.

Делать тесты будем на простой игре при старте мы загружаем с префаба карту. Звучит слишком круто, но карта в 2д и не большая, так что весить много не будет.
Скорость загрузки без Addressable: 45,5с (124мб ОЗУ)
Скорость загрузки с Addressable: 34,8с (120мб ОЗУ) (примерно 5-7 секунд ждал загрузки карты)После загрузки: 130мб ОЗУ
Addresable: 130мб ОЗУ

Результаты по кол-ву освобождённых МБ не так внушительны, но если я загружу 30 оружий, то будет видна большая разница

Так же, вы можете подгружать игру с разных серверов. При этом, вам не надо менять код, а просто немного изменить настройки. В итоге можно будет подгрузить контент с сервера, за счет чего можно получить размер билда 8мб, а на сервере держать 12мб.
В: Дорого стоят эти сервера?
О: Нет. За *1,5гб вы отдадите *2,4р. Это всё зависит от серверов, но я к примеру использую от Selectel. Причем файлы кэшируется, поэтому игрок скачивает их один раз, а дальше при повторной сессии не нужно будет.

А если денег нет вообще, есть ли выбор из бесплатных серверов?
Яндекс сервера могут предоставить 4000 рублей на тест, а так же есть сервера от амазона. Однако амазоном сейчас нельзя воспользоваться.

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

Так же, если вы будете хранить игру на сервере, есть один не приятный момент, что вы к примеру добавили музыку на два префаба и выгрузили его на сервер, как думаете, музыка загрузится один раз, два или три? Правильный ответ три. Один попадёт в финальный билд, а два будут в файлах addresable. Для таких случаев я могу порекомендовать создать новый пак и назвать его как «Music» и поместить туда префаб с музыкой и через синглтон вызывать нужную композицию. Теперь музыка не будет дублироваться между разными префабами (если вы её использовали к примеру в двух разных сценах\префабах), теперь мы создаём в проекте папку «EditorOnly», т.е. это всё то, что не попадёт в финальный билд. И туда закидываем нашу музыку, чтобы её не было в билде. В итоге, мы освобождаем размер игры и размер на сервере.
Если интересно, то игра стала весить с 8мб - 6мб
На сервере с 12мб до XXмб основной игры и XXмб музыки. Причем музыка грузиться отдельно и после того, как загрузиться игра, поэтому игра будет грузиться шустро. Пусть музыку и придётся подождать

Анимации

Анимации можно сжать и при этом, игрок этого может и не заметить.
Подробнее: https://www.techarthub.com/animation-compression-unity/

Гифок тут не будет, поэтому вам рекомендую ознакомиться с сайта выше, где можно будет наглядно увидеть, как меняется качество

Заходим в нашу импортированную анимацию и во вкладке «Animation» мы видим «Anim. Compression» и вот тут уже можно задавать настройки.

Примеры на то, насколько сильно влияет на само качество анимации, можно ознакомиться по ссылке выше. Поэтому и информация об качестве сжатия, я так же возьму с сайта выше.
Error на 0,5 - 119кбError на 10 - 69кбError на 100 - 14кб

URP | UR Data

Если вы не используете Post-Processing, то отключаем их, потому-что, они создают спрайты из-за которых у нас размер билда становится больше.

Для URP проектов, надо ставить отдельную галочку в Dynamic Batching как по скриншоту ниже.

Но приколитесь, юнитеки посчитали эти настройки не настолько важными, поэтому их скрыли. Заходим в Preferences — Core Render Pipeline — Visibility — All Visible и включаем Dynamic Batching

Мусорные файлы

Вы используете Visual Scripting в проекте? Нет? А вы знали что он попадёт в финальный билд?

Вы можете возразить, мол они не попадут в билд если не использовать — однако это эксклюзивно для Visual Scripting. В 2022 версии юнити это исправили, но у прошлых версиях, он попадает в финальный билд.

Заходим в Package Manager, в In Project и удаляем. Если вы всё же используете Visual Scripting, то в вашем случае нужно удалить не нужные библиотеки. И удаляем ноды которые не используем, ибо они попадают в конечный проект и каких-то 60 неиспользуемых нод, могут весить как 1мб (цифры взяты с головы, но весят они реально много).

В итоге, вес игры составлял: 3,7мб, а стало 3,6, это успех!

Другое

Не достойно попасть отдельно или куда-либо быть добавлено из основных пунктов.

  1. Кол-во объектов на сцене влияют на загрузку игры. Лучше пусть на сцене будет 3 объекта со старта, чтобы потом загрузились остальные 30 для вашего интерфейса. Ибо это будет куда быстрей, чем сразу 33 объекта загружать. +Скорость загрузки. Ведь игрок охотней подождёт ещё 10 секунд, если он увидит, что игра уже загрузилась, а он ждёт уже другую загрузку. Хотя, никто не запрещает дать игроку поиграть Cookie Clicker например. Или что ещё по проще
  2. Используйте как минимум 2021 версию юньки. А если вы ориентируетесь и на мобилки, то 2022
  3. Дочерние объекты в анимации - зло. Они бьют по производительности
  4. WebGPU в юньке нету, поэтому не думайте что сможете нагрузить свою игру чем-то крутым. И не думайте что это некая палочка выручалочка, у него тоже есть свои ограничения.
  5. В Canvas'е, убираем у всех не кликабельных объектов Raycast target. Для маленьких игр это жизнь малиной не сделает, но почему бы и нет?
  6. Второй+ билд всегда весит больше чем первый.

источник

Чисто от себя в благодарность:

Зацените какого мне кабанчика нарисовал Barandera. Милашка прям. Он ещё обложки для игр рисует

источник