Оптимизация проекта Unity
Спрайты
Спрайты это основа оптимизации, поэтому с них и начнём.
Тут мы уменьшим размер игры и поднимем фэпэсы
Все спрайты должны быть такого размера, чтобы каждая из сторон, делилась на 4
- Если есть необходимость использовать квадрат, то используем спрайт с строго 1 пикселем. Стандартный квадратный спрайт что предлагает юнити как квадрат, имеет 256 пикселей. Благодаря тому что мы будем использовать квадрат с одним пикселем, можно снизить нагрузку на рендер.
- Если у вас фон в виде градиента, то его можно сделать в ширину в 1 пиксель, а в высоту в 1024. В таком состоянии, он будет занимать намного меньше места. И так же с флагами, не 128x128 для флага России, а 1x3.
- Не забываем сжимать текстуру так сильно, насколько это только возможно. Для этого нажимаем по картинке и в самом низу, настраиваем его размер, сжатия и формат.
- После второго и первого пункта, мы не можем сжать текстуры, но не беда, у юнити есть Sprite Atlas. Мы его создаём и закидываем туда наши текстуры, которые нарушают законы степени 2-х и теперь мы их уже можем сжать. В финальный билд, все эти текстуры так же попадут и сам Atlas сожрёт вам дополнительную память, поэтому смотрим на меня как на идиота и не используем Sprite Atlas для таких неоптимизированных вещей. Лучше будет если те же флаги будут в атласе, который вы сделаете сами и так же, чтобы он был в степени 2-х. Это и фпс поможет поднять, отрисовывать все вместе и текстуры будут занимать не так много места. Конечно можно использовать Sprite Atlas если по фпс будет плачевно, но он добавит свои копейки в финальный билд. Немного, но добавит
- Mip Maps на некоторых версиях юньки, могут быть включены.
- 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/
- Сжимаем качество прямо в юньке. Некоторые звуки\музыку можно перевести в режим Mono, это конечно ударит по качеству, но звук будет весить меньше. Хотя бывают странные ситуации, что весить наоборот стало больше :\
- Меняем параметр как звуки будут загружаться. Благодаря этому параметру, мы можем пустить игрока в игру до того, как загрузится вся твоя музыка в игре, которая может идти в общей сложности минут 10. Для этого надо настроить Load in Background и Preload Audio Data. Подробнее есть в статье выше, если лень — то скриншоту ниже и настраивайте загрузку звука в зависимости от его предназначения.Тестировать мы будем на сцене, где сразу находятся 8 объектов и они сразу же начинают воспроизводить свою музыку.
Общая длина всех композиций, составила бы: 424 сек или 7,14 минут
И вот какие результаты:
Игра ждёт когда загрузятся звуки: 7,9с - Игра не ждёт когда закончится загрузка звуков: 6,95с
- Игра начинает загрузку звука, когда вызвать звук: 6,85с.
- Это всё на уровне погрешности и диких неточностей, но вероятнее всего для третьего пункта проверка была не удачной, т.к. звук так итак был со старта и вызывался сразу.
В итоге, мы сократили время загрузки на 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, это успех!
Другое
Не достойно попасть отдельно или куда-либо быть добавлено из основных пунктов.
- Кол-во объектов на сцене влияют на загрузку игры. Лучше пусть на сцене будет 3 объекта со старта, чтобы потом загрузились остальные 30 для вашего интерфейса. Ибо это будет куда быстрей, чем сразу 33 объекта загружать. +Скорость загрузки. Ведь игрок охотней подождёт ещё 10 секунд, если он увидит, что игра уже загрузилась, а он ждёт уже другую загрузку. Хотя, никто не запрещает дать игроку поиграть Cookie Clicker например. Или что ещё по проще
- Используйте как минимум 2021 версию юньки. А если вы ориентируетесь и на мобилки, то 2022
- Дочерние объекты в анимации - зло. Они бьют по производительности
- WebGPU в юньке нету, поэтому не думайте что сможете нагрузить свою игру чем-то крутым. И не думайте что это некая палочка выручалочка, у него тоже есть свои ограничения.
- В Canvas'е, убираем у всех не кликабельных объектов Raycast target. Для маленьких игр это жизнь малиной не сделает, но почему бы и нет?
- Второй+ билд всегда весит больше чем первый.
Чисто от себя в благодарность:
Зацените какого мне кабанчика нарисовал Barandera. Милашка прям. Он ещё обложки для игр рисует