Первые шаги в оптимизации и полировке игры на Unity3d. Оптимизация unity
Unity5. Оптимизация. - Create Games
Unity5. Оптимизация.
Как оптимизировать игру в Unity.
При разработке собственных игр есть вероятность, столкнутся с проблемами оптимизации, т.е. будут ли у вас все модели низкополигонными(Low Polly) или даже отсутствие объемных эффектов и частиц, игра все равно не работает должным образом на любых устройствах. Что бы избежать таких проблем, существуют основные правила или приемы оптимизации игр в Unity.
Первым делом, проверьте показатель “draw calls” в окне Profiler; если он составляет больше нескольких тысяч (для PC) или нескольких сотен (для мобильных устройств), то вам требуется оптимизация игровых объектов.
Вот несколько основных приемов и советов по оптимизации игр.
Текстуры и материалами.
Уменьшите количество различных материалов в сцене — используйте один материал для нескольких объектов,
Шейдеры для материалов используйте из категории Mobile/ — т.к. Они прекрасно работают и на не мобильных платформ, но являются упрощёнными версиями более сложных шейдеров.
Используйте сжатие текстур, когда это возможно, а также отдавайте предпочтение 16-битным текстурами перед 32-битными.
Используйте меньше текстур.
Настройка игрового мира.
Туман (Fog) – очень сильно нагрузит вашу игру,
Для не статических объектов с коллайдерами старайтесь использовать Rigibody в режиме isKinematic.
Используйте один источник света, а для отдельных затемнений карты используйте Lightmap`ы.
GPU обрабатывает слишком много вершин. Какое количество вершин является нормальным, определяется GPU и набором вертексных шейдеров. Можно посоветовать использовать не более 100 тысяч для мобильных устройств и не более нескольких миллионов полигонов для PC.
Сохраняйте количество вершин между 200 000 и 3 000 000 в каждом кадре, если целевая платформа — PC.
Установите свойство Static для неподвижных объектов, чтобы использовать внутреннею оптимизацию static batching.
Используйте скайбоксы для имитации далеко расположенной геометрии.
Тени в реальном времени хорошо выглядят, но они могут сильно снижать производительность, одновременно добавляя дополнительные draw calls для CPU и дополнительную обработку для GPU.
Настройка terrain.
Старайтесь не использовать большие размеры карт,
Установите значение pixel error максимальное значение,
Настройте отображение растительности, деревьев и объектов следующими параметрами:
- Detail Density — Количество объектов деталей/травы в данной условной единице площади. Можно снизить значение для снижения потребления ресурсов на рендеринг,
- Detail Distance — Расстояние (от камеры), после которого детали будут отсекаться,
- Tree Distance — Расстояние (от камеры), за которым деревья будут отсекаться,
- Fade length — Расстояние перехода деревьев из 3D объектов в плоские спрайты,
- Max Mesh Trees — Максимальное количество видимых деревьев, которые будут представлены в качестве полноценных 3D мешей. После этого предела, деревья будут заменяться плоскими спрайтами.
Скрипты.
Метод FixedUpdate больше подойдет для небольших проверок относящиеся к физике,
CompareTag быстрее делает проверку тэга по имени,
Делайте как можно меньше вызовов thisTransform.forward или position, лучше эти значения один раз получить в Update,
(thisTransform.position — target.position).sqrMagnitude быстрее Vector3.Distance,
Старайтесь для включение скрипта на объекте при возможности использовать не GetComponent(…).enabled с заранее отключенным скриптом, а AddComponent(…) при заранее не повешенном скрипте на объекте,
Используйте статические переменные.
Заключение.
Если стараться придерживаться этих правил, можно достаточно существенно оптимизировать вашу игру.
Возможно, здесь указаны не все методы, если вам есть что добавить или вы считаете что какой то способ не подходит для оптимизации, пишите нам в комментариях. Удачных проектов!
На главную
3dgame-creator.ru
Планирование оптимизации с Unity / Блог компании Intel / Хабр
Unity содержит ряд настроек и инструментов, позволяющих добиться плавной работы графики в играх. Для этого проекта мы отобрали те из них, с которыми могут возникнуть сложности, и проанализировали их влияние на производительность игр на ГП Intel.
Мы рассматриваем использование Unity с точки зрения разработчика игр. Мы стремились найти области снижения производительности, а затем определить, как улучшить работу приложений с помощью встроенных в Unity средств. Одно из преимуществ Unity состоит в возможности быстрого создания контента, но для того, чтобы добиться производительности, особенно на мобильных устройствах и планшетах, разработчику потребуется тщательно спланировать использование встроенных механизмов оптимизации производительности. В этой статье новым и существующим пользователям Unity предлагаются советы по повышению производительности при создании уровней и игр, а также описываются новые способы создания содержимого.
Введение
Создавать игры с использованием Unity относительно просто. В Unity имеется магазин, где можно приобретать различные элементы, такие как модели, готовые сценарии, демо или даже полные игры. Для тестирования я работал с существующей игрой, чтобы определить, в каких областях можно добиться повышения производительности, а в каких — нет. Я использовал техническое демо Unity под названием Boot Camp (ее можно бесплатно загрузить в магазине ресурсов), чтобы оценить сложность проблемы.Quality Manager
В Unity доступны дополнительные параметры рендеринга для игр: меню Edit->Project Settings->Quality (рис. 1). Это настраиваемые параметры рендеринга, которые можно настроить индивидуально. В Unity содержится встроенная документация, поясняющая параметры качества и их настройку с помощью API сценариев Unity.Рисунок 1. Доступ к тегам и слоям осуществляется через меню Edit->Project Settings->Tag inspector
Для изыскания возможностей оптимизации в Unity я решил попробовать изменить настройки качества, чтобы понять, что при этом можно выиграть или потерять, хотя я не тестировал все доступные параметры.
Качество текстур
В Quality Settings Inspector есть раскрывающееся меню, где можно выбрать разрешение рендеринга для текстур. Можно выбрать разрешение 1/8, 1/4, 1/2 или полное разрешение. Для оценки прироста или снижения производительности при разном разрешении текстур я измерил кадровую скорость тестовой сцены при всех доступных в Unity настройках качества по умолчанию (Fastest, Fast, Good и пр.), изменяя только качество текстур перед каждым измерением. На рис. 2 и 3 показано сравнение между сценами с 1/8 разрешения текстур и с полным разрешением текстур.Рисунок 3. Сцена Unity* Boot Camp с полным разрешением
Мы измерили кадровую скорость (в кадрах в секунду) с помощью Intel Graphics Performance Analyzers (Intel GPA) после изменения разрешения текстур. При уровне качества Fantastic (таблица 1) видно, что производительность не слишком заметно изменилась при изменении размера текстур.
Таблица 1. Изменение кадровой скорости при переключении между различным качеством текстур в Unity
На компьютерах с ГП Intel производительность теоретически не изменяется в зависимости от размера текстур, но следует учитывать и другие факторы, такие как общий объем памяти устройства и использование памяти приложением.
Shadow Distance
Shadow Distance — это параметр, определяющий глубину отбраковки, используемую для теней игровых объектов. Если игровой объект находится в пределах заданного расстояния от камеры, то тени этого объекта отрисовываются, если же объект находится дальше этого расстояния, то тени такого объекта не отображаются (исключаются из отрисовки). В зависимости от используемых параметров тени могут отрицательно повлиять на производительность, поскольку их расчет и отрисовка являются ресурсоемкими операциями. Тестирование влияния параметра Shadow Distance- Создайте тестовую сцену.
- Задайте для этой сцены параметры качества Unity по умолчанию.
- Постепенно увеличивайте значение параметра Shadow Distance и измеряйте кадровую скорость с помощью Intel GPA.
- Выберите другое значение качества по умолчанию в Unity и повторите измерение кадровой скорости.
Рисунок 4. Этот параметр доступен в меню Inspector: Edit->Project Settings->Quality
Рисунок 5. Техническое демо Unity* Boot Camp
Таблица 2. Изменение кадровой скорости при изменении значения параметра Shadow Distance в технической демонстрации Unity* Boot Camp
Тени значительно влияют на производительность. Тест показал, что кадровая скорость упала почти вдвое при переключении расстояния с 0 до 50 в режиме Simple. Важно учитывать, действительно ли видны игровые объекты, и убедиться, что ненужные тени не отрисовываются. Глубину отбраковки теней можно настраивать с помощью сценариев Unity для различных ситуаций. Мы проверили только воздействие глубины отбраковки теней, но аналогичные изменения производительности могут возникать и при настройке других параметров качества теней.
Слои
Всем игровым объектам в Unity при создании назначается слой. Изначально всем объектам назначается слой по умолчанию, как показано на рис. 6, но можно создать собственные уникальные слои. Это можно сделать двумя способами. Можно просто щелкнуть поле Layer и выбрать Add New Layer. Также можно использовать меню Edit->Project Settings->Tags.Рисунок 6. Меню Layer в окне Inspector игрового объекта
В окне Inspector (рис. 7) можно создать новый слой и указать, к какому номеру слоя он должен принадлежать. При использовании обоих методов открывается одно и то же окно Tag Manager. После создания слоя можно назначать этому слою игровые объекты, выбирая нужный слой в окне параметров в окне Inspector игрового объекта в поле Layer. Таким способом можно группировать объекты на одних и тех же слоях, чтобы затем обрабатывать их вместе. Помните о том, что такое слои, и как их создавать и настраивать, когда я буду рассказывать о некоторых других функциях слоев в этой статье.
Расстояния отбраковки слоев
Камера не будет отрисовывать игровые объекты, находящиеся за пределами плоскости отсечения в Unity. С помощью сценариев Unity можно задать для определенных слоев более короткое расстояние до плоскости отсечения.Рисунок 8. Образец сценария из документации Unity с изменением расстояния отбраковки слоев
Настройка более короткого расстояния отбраковки для игровых объектов требует некоторой работы. Сначала нужно разместить объекты на слое. Затем нужно написать сценарий, чтобы изменить расстояние отбраковки этого конкретного слоя, и назначить сценарий камере. Образец сценария на рис. 8 показывает, как создается массив 32 значений с плавающей запятой, соответствующий 32 доступным слоям, которые можно создать с помощью меню Edit->Project Settings->Tags. Если изменить значение индекса в этом массиве и присвоить его camera.layerCullDistances, то для соответствующего слоя изменится расстояние отбраковки. Если не назначить число индексу, то соответствующий слой будет использовать дальнюю плоскость отсечения камеры.
Для тестирования производительности layerCullDistances я создал три сцены, заполненные объектами низкой, средней и высокой сложности. В этих сценах идентичные игровые объекты собраны вместе и расположены в ряд с постепенным удалением от камеры. Я использовал Intel GPA для измерения кадровой скорости при постепенном увеличении расстояния отбраковки слоя, добавляя по группе объектов при каждом измерении (т. е. одна группа объектов при первом измерении, 6 групп объектов при шестом измерении).
На рисунках 9, 10 и 11 показаны сцены, которые я использовал для тестирования с объектами разных типов.
Сапоги: полигонов — 278, вершин — 218
Динозавры: полигонов — 4398, вершин — 4400Рисунок 10. Тестовая сцена с динозаврами — объектами со средним числом полигонов и вершин
Самолеты: полигонов — 112 074, вершин — 65 946Рисунок 11. Тестовая сцена с самолетами — объектами с большим числом полигонов и вершин
В таблицах 3, 4 и 5 показано изменение кадровой скорости во всех тестовых сценах.
Таблица 3. Данные, полученные в сцене с сапогами (рис. 9)
Таблица 4. Данные, полученные в сцене с динозаврами (рис. 10)
Таблица 5. Данные, полученные в сцене с самолетами (рис. 11)
Таблица 6. Данные всех тестовых сцен в режиме Fantastic
Эти данные показывают повышение производительности, которого можно добиться с помощью функции layerCullDistances в Unity.
В таблице 6 показано, как производительность изменяется при увеличении количества объектов на экране, особенно если это сложные объекты. С точки зрения разработчика игр, правильное использование layerCullDistances позволяет значительно повысить производительность. Например, для маленьких объектов со сложной моделью, расположенных дальше от камеры, можно настроить отрисовку только при достаточном приближении, когда эти объекты уже можно различить. При планировании и создании уровней разработчику следует учитывать сложность моделей и видимость объектов, расположенных на большом расстоянии от камеры. Заблаговременное планирование позволяет добиться лучших результатов при использовании layerCullDistances.
Камера
Я изучил возможности работы с камерой в Unity и ее настройки. Я попробовал выбрать различные параметры, поработал с другими компонентами и дополнениями.Рисунок 12. Меню Inspector, открывающееся после выбора камеры
При создании новой сцены по умолчанию появляется только один игровой объект камеры под названием Main Camera. Чтобы добавить еще одну камеру, сначала создайте пустой игровой объект: Game Object->Create Empty. Затем выберите этот пустой объект и добавьте компонент камеры: Components->Rendering->Camera. Камеры в Unity поддерживают широкие возможности настройки, как показано на рис. 12. Вот какие настройки я рассмотрел: Rendering Path и HDR.
Render Path
С помощью параметра Render Path можно указать в Unity, как обрабатывать рендеринг света и теней в игре. В Unity поддерживаются три типа рендеринга, вот они в порядке от наиболее ресурсоемкого к наименее ресурсоемкому: Deferred (отложенная, только в Unity Pro), Forward (заблаговременная) и Vertex Lit (освещение вертексов). В каждом случае тени и свет обрабатываются немного иначе, и для их обработки требуется разный объем ресурсов ЦП и ГП. Важно понимать, для какой платформы и для какого оборудования ведется разработка, чтобы выбрать соответствующий рендерер. Если выбрать рендерер, который не поддерживается графическим адаптером, Unity автоматически переключится на менее ресурсоемкий способ рендеринга.Рисунок 13. Окно Player Settings Inspector
Настроить значение Rendering Path можно двумя способами. Первый способ: Edit->Project Settings->Player (рис. 13). Раскрывающийся список Rendering Path находится на вкладке Others Settings. Второй способ: с помощью окна пользовательского интерфейса Camera Inspector (рис. 14). Если выбрать любой параметр, отличный от Use Player Settings, настройки по умолчанию будут заменены, но только для данной камеры. Поэтому можно использовать разные камеры с различными настройками буфера рендеринга для света и теней.
Рисунок 14. Раскрывающийся список при выборе параметра Rendering Path в окне Camera
Разработчики должны знать, каким образом действуют различные режимы рендеринга освещения, входящие в состав Unity. В разделе справочных материалов в конце этого документа приводятся ссылки на документацию по Unity. Убедитесь, что вы точно знаете целевую аудиторию своей игры и платформу, на которой потенциальные пользователи будут играть в эту игру. Это поможет вам выбрать соответствующий способ рендеринга для данной платформы. Например, если игра содержит несколько источников света и графических эффектов, использующих отложенный рендеринг, то на компьютерах с маломощным графическим адаптером играть в такую игру будет попросту невозможно. Если ваша целевая аудитория включает любителей казуальных игр, устройства которых не обладают высокой вычислительной мощью, то также могут возникнуть проблемы. Разработчики должны знать целевую платформу, для которой предназначена их игра, и соответственным образом выбирать способ рендеринга и обработки освещения в игре.
HDR (расширенный динамический диапазон)
При обычном рендеринге значения красного , зеленого (G) и синего (B) цветов каждого пикселя представлены десятичным числом, значение которого составляет от 0 до 1. Если ограничить диапазон значений для этих цветов, освещение будет выглядеть нереалистично. Чтобы добиться более естественного освещения, в Unity можно включить расширенный динамический диапазон (HDR). В этом случае значения R, G и B каждого пикселя могут выходить за пределы обычного диапазона. HDR создает буфер изображения, поддерживающий значения вне диапазона от 0 до 1, и выполняет постобработку графических эффектов, таких как размытие и блики. После вычисления эффектов постобработки значения R, G и B в буфере изображения сбрасываются до значений в пределах диапазона от 0 до 1 технологией построения карт оттенков Unity. Если построение карт оттенков не выполняется при использовании HDR, то пиксели могут оказаться за пределами допустимого диапазона, из-за чего некоторые цвета сцены могут выглядеть неправильно по сравнению с остальными.При использовании HDR следите за параметрами, влияющими на производительность. При использовании заблаговременного рендеринга сцены HDR будет работать только при наличии графических эффектов. В противном случае включение HDR ни на что не повлияет. При отложенном рендеринге HDR всегда используется. Если сцена обрабатывается с помощью отложенного рендеринга, а камере присвоены графические эффекты, то следует включить HDR. На рис. 15 сравнивается количество вызовов рендеринга для сцены с эффектами и отложенным рендерингом при включенном и отключенном HDR. Без HDR количество вызовов рендеринга значительно выше, чем при использовании HDR, если сцена содержит эффекты. На рис. 15 количество вызовов рендеринга представлено отдельными синими полосками, а высота каждой полоски соответствует величине нагрузки каждого вызова на ГП.
Рисунок 15. Измерение, проведенное с помощью Intel Graphics Performance Analyzers, показывает, что при отключенном HDR выполняется свыше 2000 вызовов рендеринга, тогда как при включенном HDR — немногим более 900 вызовов рендеринга
Ознакомьтесь с документацией Unity по HDR, чтобы понять, как работает эта функциональность. Также нужно знать, в каких случаях следует применять расширенный динамический диапазон, чтобы получить за счет этого прирост производительности, а в каких случаях это не имеет смысла.
Графические эффекты
В Unity Pro содержится ряд графических эффектов, которые улучшают вид сцены. Чтобы добавить компонент Image Effects после создания проекта, используйте меню Assets->Import Package->Image Effects. После импорта можно добавить эффект к камере двумя способами. Щелкните игровой объект камеры, в окне камеры выберите Add Component, а затем Image Effects. Также можно щелкнуть объект камеры в меню, выбрав Component->Image Effect.Рассеянное затенение в экранном пространстве — SSAO
Рассеянное затенение в экранном пространстве (SSAO) — это графический эффект в составе пакета Image Effect в Unity Pro. На рис. 16 показано различие между включенным и отключенным SSAO. Изображения выглядят схоже, но производительность существенно различается. У сцены без SSAO кадровая скорость составила 32 кадра в секунду, а с SSAO — 24 кадра в секунду, то есть на 25 % ниже.Рисунок 16. Сравнение одного и того же уровня с отключенным SSAO (сверху) и с включенным SSAO (снизу)
Будьте осторожны при добавлении графических эффектов, поскольку они могут отрицательно повлиять на производительность. При подготовке этого документа мы тестировали только SSAO, но следует ожидать схожих результатов и при использовании других эффектов.
Исключение заслоненных объектов
Исключение заслоненных объектов — это отключение рендеринга не только тех объектов, которые находятся за плоскостью отсечения камеры, но также и объектов, скрытых за другими объектами. Это очень выгодно с точки зрения производительности, поскольку значительно сокращается объем информации, которую следует обработать. Тем не менее, настройка исключения заслоненных объектов происходит не слишком просто. Перед настройкой сцены для исключения заслоненных объектов следует разобраться с используемой терминологией. Заслоняющий объект — объект, помеченный как заслоняющий, выступает в качестве преграды: все загороженные им объекты, помеченные как заслоняемые, не отрисовываются. Заслоняемый объект — если пометить объект таким образом, он не будет отрисовываться в Unity, если его загораживает заслоняющий объект. Например, если пометить все объекты, находящиеся внутри дома, как заслоняемые, то сам дом можно пометить как заслоняющий. Если игровой персонаж будет находиться снаружи этого дома, то все объекты внутри дома, помеченные как заслоняемые, не будут отрисовываться. При этом ускоряется обработка на ЦП и ГП. Использование и настройка исключения заслоненных объектов задокументированы в Unity. Ссылку на информацию по настройке см. в разделе справочных материалов. Для демонстрации изменения производительности (в зависимости от исключения заслоненных объектов) я создал сцену, где на переднем плане находится стена, а за ней — объекты со сложными моделями. Я измерил кадровую скорость сцены с исключением заслоненных объектов, а затем без него. На рис. 17 показана сцена с разной кадровой скоростью.Рисунок 17. На изображении слева исключение заслоненных объектов отключено, отрисовываются все объекты, расположенные за стеной, поэтому кадровая скорость составляет 31 кадр в секунду. На изображении справа исключение заслоненных объектов включено, объекты, заслоненные стеной, не отрисовываются, поэтому скорость возросла до 126 кадров в секунду.
Исключение заслоненных объектов должно быть настроено разработчиками вручную. Не следует забывать об исключении заслоненных объектов при проектировании игр, поскольку это снижает требования к оборудованию и позволяет повысить производительность.
Уровень детализации (LOD)
С помощью уровня детализации (LOD) можно присвоить одному игровому объекту несколько моделей разной сложности и переключаться между ними в зависимости от расстояния между объектом и камерой. Это может быть выгодно с точки зрения производительности для сложных игровых объектов, находящихся далеко от камеры. С помощью уровня детализации можно автоматически упрощать модели. Сведения об использовании и настройке уровня детализации см. в документации Unity. Ссылка приведена в разделе справочных материалов. Для тестирования прироста производительности при изменении уровня детализации я создал сцену с группой домов, которым присвоено 3 разные модели. Расположив камеру в одном и том же месте, я измерил кадровую скорость сцены с домами при использовании самой сложной модели. Затем я изменил расстояние детализации, чтобы задействовать модели с меньшей степенью детализации, и провел измерение еще раз. Я проделал эту процедуру для трех уровней моделей и записал полученные данные в таблице 5. На рис. 18, 19 и 20 показаны три разных уровня сложности моделей с указанием количества полигонов и вершин в каждой модели.Здание А • Вершин — 7065 • Полигонов – 4999 Здание Б • Вершин — 5530 • Полигонов – 3694 | Рисунок 18. Уровень детализации 0. Это наивысший уровень детализации, на котором используются самые сложные модели (кликните для увеличения) |
Здание А • Вершин — 6797 • Полигонов — 4503 Здание Б • Полигонов — 5476 • Вершин — 3690 | Рисунок 19. Уровень детализации 1. Этот уровень находится на одну ступень ниже по шкале детализации, на нем используются модели средней сложности (кликните для увеличения) |
Здание А • Вершин — 474 • Полигонов – 308 Здание Б • Полигонов – 450 • Вершин — 320 | Рисунок 20. Уровень детализации 2. Это последний уровень детализации, здесь используются наименее сложные модели (кликните для увеличения) |
Таблица 7. Сравнение кадровой скорости при разных уровнях детализации
В таблице 7 видно повышение производительности при настройке и использовании разных уровней детализации. Кадровая скорость значительно возрастает при переключении на менее сложные модели. Впрочем, при этом увеличивается объем работы художников, которым придется создавать несколько моделей каждого объекта. Проектировщики игр должны сами принимать решение о том, следует ли затратить дополнительное время на рисование добавочных моделей, чтобы за счет этого добиться повышения производительности.
Пакетная обработка
Избыточное количество вызовов рендеринга может привести к чрезмерной нагрузке на ЦП и снижению производительности. Чем больше на экране объектов, тем больше вызовов рендеринга нужно сделать. В Unity поддерживается так называемая пакетная обработка, позволяющая поместить несколько игровых объектов в один вызов рендеринга. Статическая пакетная обработка предназначена для статических объектов, а динамическая — для движущихся объектов. Динамическая пакетная обработка выполняется автоматически при выполнении всех требований (см. документацию по пакетной обработке), а статическую пакетную обработку требуется задавать вручную. Существуют определенные требования для совместного рендеринга объектов и для динамической, и для статической пакетной обработки. Все эти требования перечислены в документе по пакетной обработке, ссылка на который приведена в разделе справочных материалов. Для тестирования прироста производительности при статической пакетной обработке я создал сцену со сложными игровыми объектами в виде самолетов (рис. 21) и измерил кадровую скорость с пакетной обработкой и без нее (таблица 8).Рисунок 21. Статическая пакетная обработка тестовой сцены с очень сложными моделями самолета
Таблица 8. Разница в кадровой скорости и количестве вызовов рендеринга при включенной и отключенной статической пакетной обработке (рис. 21)
В Unity поддерживается два типа пакетной обработки, динамическая и статическая. Для получения наибольшего преимущества от пакетной обработки старайтесь объединить как можно больше объектов в пакет для одного вызова рендеринга. Сведения о том, какие объекты подходят для динамической или статической пакетной обработки, см. в документации Unity.
Заключение
В Unity можно без особого труда начать работу над проектами, но с не меньшей легкостью можно и допустить недостаточную производительность приложений. В Unity поддерживается ряд настроек и инструментов, позволяющих добиться плавной графики в играх, но не все эти средства удобно настраиваются и интуитивно понятны. Кроме того, некоторые настройки в Unity при их включении или неверном использовании могут отрицательно повлиять на производительность игр. При разработке с использованием Unity важно заранее подготовить план, поскольку для некоторых функций, связанных с производительностью, требуется настройка вручную, которая может оказаться значительно труднее, если не запланировать ее заблаговременно (при создании проекта).Дополнительные материалы по теме
Оригинал статьи: How To Plan Optimizations with Unity*Документация по параметрам качестваAPI сценариев для параметров качестваТехническая демонстрация Boot CampДокументация по уровню детализацииДокументация по исключению скрытых частейДокументация по пакетной обработкеДокументация по параметру Rendering Path
Intel GPAДобавление поддержки мультисенсорного ввода в игры Unity* для Microsoft Windows* 7 и классического интерфейса Windows* 8Реализация последовательностей жестов в Unity* 3D с помощью библиотеки TouchScriptИспользование сенсорных жестов для управления параметрами физики в Unity* 3D с TouchScriptИспользование стандартного GUI Unity* 3D в сочетании с ресурсами TouchScript
habr.com
Оптимизация 2D игр на Unity
Этот пост будет периодически дополняться и обновляться.Обновлено 10.10.2018
На Youtube куча уроков по созданию простейших 2D игр на Unity. Реально, сделать неплохой платформер можно за день, при наличии опыта и готовых ассетов. Но многие начинающие игроделы сделав проект и протестировать его на ПК, с ужасом наблюдают как их творение тормозит на мобильном устройстве.
В мануалах, что встречаются в сети, большинство советов собрано к версии Unity 4.6+, кроме того, они почти все на английском, что для некоторых является преградой. В этом посте, я постарался собрать те моменты, которые помогли мне избавится от лагов на iOS и Android. Но важно понимать — не все можно решить лишь настройками, очень важна и архитектура приложения, и подготовленные текстуры, и знание более оптимальных алгоритмов.
Что нужно предпринять, чтобы повысить производительность, поднять FPS, снизить CPU?
1. Кешируем все!
Все, что будет использоваться больше одного раза лучше закешировать. Операции типа GameObject.Find() или GetComponent() достаточно ресурсозатратны, а если они вызываются где-нибудь в цикле или в Update (), то производительность может упасть.
void Awake () { _cachedTransform = transform; } void Update () { _cachedTransform.localPosition = _newPosition; }Не используйте Resources.Load () каждый раз когда нужно в рантайме загрузить что либо, это тоже дорогая операция. Лучше при старте закешировать и работать с кешем. Ну и конечно, объединяйте в префабы.
2. Настройки графики
В Unity есть 6 стандартных пресетов настройки качества графики. Но так как мы говорим про оптимизацию для 2D и для мобильных устройств, то все что выше Simple нет смысла ставить. Конечно, если у вас есть какие-то специфические моменты, частицы, и т. д., то с параметры можно поэкспериментировать найдя оптимальный баланс.
3. Используем FrameRate
По-умолчанию FrameRate равен 30. И зачастую этого бывает достаточно. Но например, при создании UI где есть прокручивающие списки и движущие элементы, может появится дрожание или шлейф. Я это заметил при тестировании на iPhone, поэтому вручную повысил FrameRate. А понижая FrameRate на сценах или игровых меню, где ничего не двигается, можно значительно снизить CPU, а следовательно продлить жизнь батарее устройства. Пользователи не любят когда игра сжирает аккумулятор за час.
public int _frameRate = 60; void Start () { #if UNITY_IOS QualitySettings.vSyncCount = 0; #endif } void Update () { #if UNITY_IOS if (_frameRate != Application.targetFrameRate) Application.targetFrameRate = _frameRate; #endif }4. Атлас текстур
Определенно, нужно упаковывать все свои спрайты в атласы. Для этого есть как встроенный инструмент Sprite Packer, так и продвинутый TexturePacker. Для работы встроенного упаковщика, достаточно задавать теги в настройках импорта, объединяя текстуры и спрайты по смыслу и месту использования.
Таким образом уменьшается количество вызовов отрисовок ваших спрайтов. Проверить как идет отрисовка можно с помощью встроенного инструмента Frame Debugger.
5. Используем пул объектов
GameObject.Instantiate () — очень дорогая операция! Если есть возможность не использовать ее в процессе игры — не используйте. Для большого количества однотипных объектов надо использовать пул объектов. Один раз инициализировав определенное количество, например пуль, они будут использоваться снова и снова, вместо создания и уничтожения каждый раз. Урок по пулу объектов и готовый шаблон, для начала будет достаточно.
6. Меньше Update — больше событий
Метод Update () вызывается каждый кадр, FixedUpdate () в некоторых случаях еще чаще. Если у вас в этих местах происходит много различных проверок и действий, это можно сказаться на производительности. Я использую событийную модель: вместо проверки условия в Update (), когда происходит какое-либо действие, отправляется событие, а в нужном месте это событие «слушается» и обрабатывается в зависимости от переданных параметров. Для этого можно использовать менеджер событий о котором я писал ранее.
7. Выключаем неиспользуемые компоненты
Деактивируйте объекты которые не попадают в камеру и которые не видно на сцене. Это также повысит производительность. Можно использовать вот такой хак, чтобы автоматически деактивировать объекты которые выходят за определенные границы.
8. Настройки билда
Билд под конкретную платформу, тоже можно оптимизировать. Например, если акселерометр не используется в игре, его лучше вообще отключить. Кроме того, я не использую автовыбор графического API, а задаю его сам убирая все остальные, опять же, если вы не используете какие-то специфические функции из OpenGLES 3.0, а так второй версии вполне хватает, она уже давно протестирована. Включаем статичный и динамический батчинг, а для Android многопоточный рендеринг. Для iOS включаем Script Call Optimization в Fast but no Exceptions, но тут момент — если будет какое-то исключение, то игра крашится.
9. Используем Profiler
Не стоит обделять вниманием профайлер. Обязательно протестируйте свою игру и посмотрите, в какие моменты идет максимальная нагрузка. Эти места нужно оптимизировать в первую очередь. Большинство ответов можно найти на stackoverflow.com или форуме Unity, просто загуглив название метода который тратит больше всего ресурсов.
10. Использование материала для UI Image и SpriteRenderer
Если у вас сложный интерфейс и много компонентов, особенно UI Image, то они существенно будут влиять на FPS. Чтобы повысить производительность, улучшить плавность анимаций, можно применить такой хак: там где вы не используете маски, у картинок нужно задать материал Sprites-Default. Если это сделать вместе с маской, то маска не сработает и получите примерно такой варнинг:
Material Sprites-Default doesn’t have _Stencil property
Чтобы убрать эту ошибку нужно немного изменить стандартный шейдер, сделать новый материал и применить его там где есть маска, тогда все сработает. Ссылка на измененный шейдер.Цена плавности — повышение CPU :(
11. Уменьшаем размер текстур
Отличная утилита которая позволяет снизить потребления памяти для текстур до 3х раз. Как это работает и ссылка на Github, в статье на Хабре.
12. Практическое руководство по оптимизации Unity игр
Подойдет как для 2D, так и для 3D. Много полезной информации которую в документации вряд ли найдешь. Тут и про инструменты, и про опыт. Рассказывает специалист по эксплуатации из Unity Technologies — очень интересно. Узнал про memory profiler и то, что Camera.main не закеширована О_О. Обязательно смотреть всем.
13. Используем оптимизированный код
Снова хочется посоветовать набор оптимизированных скриптов от Leopotam. Коллекции, сериализация, векторы и многое другое. Настоятельное рекомендую, к изучению и использованию.
14. Используем одинаковые материалы
Если на объектах стоят разные материалы, они не будут батчится и будет больше вызовов отрисовки. Соответственно, нужно по возможности использовать как можно меньше разных шейдеров и материалов. Для понимания, как работает начальная оптимизация графики 2D, на Lynda.com есть небольшой курс.
15. Размеры атласов и спрайтов
Для применения сжатия спрайтов на мобильных устройствах нужно использовать атласы с размерами кратными степени 2, т. е. 1024х1024, 2048х2048.
16. Используйте UI Profiler
В Unity 2017 появился UI Profiler! Крутая штука, теперь можно выяснить почему в интерфейсе много вызовов отрисовки, увидеть какие материалы на объектах и всё это оптимизировать. Особенно актуально, если у вас сложные интерфейсы со множеством элементов, которые например, прокручиваются в ScrollRect.
17. Уголок оптимизатора
На сайте Unity появился специальный раздел посвященный оптимизации — Optimization corner. Там и про UI, и профайлеры, и основные ошибки, в общем, стоит ознакомиться.
Пока все. Тут не было про физику, потому что пока я ее не использовал. Возможно, в будущем добавлю. Пишите в комментариях ваши проверенные способы оптимизации для 2D игр под Unity.
mopsicus.ru
Оптимизация 2d-приложений для мобильных устройств в Unity3d / Хабр
Недавно наша студия завершила разработку большого обновления — Captain Antarctica: Endless Run — для устройств на iOs. Кропотливая работа над обновлением затронула производительность, которая оказалась очень низкой на слабых устройствах. Я боролся с этим целую неделю и добился как минимум 30 FPS, а также значительного сокращения размера приложения. Хочу рассказать, как я это сделал, ну и как делать не стоит. Статья пригодится любым разработчикам на Unity (причем не только менеджерам проектов и техническим специалистам, но и просто программистам, художникам и дизайнерам), потому что она затрагивает как оптимизацию на Unity в целом, так и конкретно оптимизацию 2d-приложений для мобильных устройств.Возможно все!
Начну с того, что каждый раз, когда я приступаю к оптимизации, я поначалу не верю, что можно что-то еще оптимизировать, особенно если проект уже прошел несколько циклов оптимизации до этого. Но просмотр официальной документации Unity, тем на форумах, статей в Интернете наводит меня на мысль о новых возможных улучшениях. Таким образом я веду специальный список, в котором записаны основные идеи по тому, что можно оптимизировать в проекте на Unity, постоянно обновляю его и первым делом обращаюсь к нему, когда речь заходит об оптимизации. Этими идеями я хочу с Вами поделиться. Надеюсь, статья поможет Вам сделать Ваш проект намного более шустрым. Сразу обозначу, что разработка велась на Unity 3.5.6, целевая платформа — устройства Apple от iPhone 3GS и новее.Базовые правила
Для начала приведу несколько правил, которыми я пользуюсь при разработке и оптимизации. 1. Не оптимизируйте заранее. Это золотое правило должно быть знакомо всем, кто когда-либо занимался оптимизацией. Вспомним правило 80/20: 80% пользы получается от 20% работы.Цифры довольно условны, но я имел ввиду вот что: наиболее вероятно, что большая часть оптимизаций, которые Вы собираетесь сделать на начальном этапе проекта, скорее всего вообще никак не повлияет на конечный проект в целом.
Однако, есть пара исключений из этого правила, которые особенно важны при разработке для мобильных устройств, потому что это правило больше подходит для PC-проектов. А PC намного производительнее мобильных платформ и менее ограничены в ресурсах. Так вот, исключения:
- Допустим, есть код, или конструкция, которую вы уже сейчас знаете, как написать лучше, с меньшей затратой по памяти/процессора и тд. Вы об этом знаете по своему опыту, потому что не раз оптимизировали компоненты подобного рода, и знаете, что это приводит к увеличению производительности. Так почему бы уже сейчас не написать ее правильно? Обычно такие вещи складываются в свод правил типа «как правильно писать код, и как писать не надо», и грамотный программист постоянно им пользуется во избежание ошибок в будущем. Нечто подобное имеет место для художников, дизайнеров и тд.
- Если есть действие, которое будет повторяться много раз, и с самого начала можно его оптимизировать, почему бы это не сделать сразу. Дальше все будет идти автоматом, и не нужно будет исправлять одну и ту же вещь несколько раз. Сюда относятся, например, вспомогательные скрипты, помогающие дизайнерам ускорить однообразную работу с группой объектов.
Что мне помогло решить проблему с производительностью?
Еще до прогона через профайлер было очевидно, что слабое место — в кол-ве Draw Calls. В среднем сцена выдавала порядка 70 DrawCalls, что для устройств уровня iPad1 и ниже является фатальным. Нормально для них — 30-40 Draw Calls. Посмотреть кол-во Draw Calls можно прямо в редакторе в окне Game->Stats:.Кол-во Draw Calls, показываемых в редакторе, совпадает с таковым на конечных устройствах. Профайлер это подтвердил. Вообще, очень полезно смотреть эту статистику, и не только программистам, но и дизайнерам, для нахождения «тугих» мест в игре. В наших сценах плохо работало группирование нескольких Draw Calls для одного и того же материала в один Draw Call. Это называется Dynamic Batching. Я начал рыть на тему «как понизить кол-во Draw Calls и улучшить их группирование». Ниже перечислены основные правила, придерживаясь которых, можно получить приемлемое количество Draw Calls. Вот те, которые мне очень сильно помогли:
1. Использовать атласы для комбинирования нескольких текстур в одну большую. На самом деле важнее даже, чтобы спрайты/модели использовали не то что бы одну общую текстуру, а скорее один общий материал. Именно в кол-ве различных материалов измеряется кол-во Draw Calls (в идеальном случае). Поэтому у нас в проекте используемые изображения всегда объединены в атласы, разбитые на категории: объекты, используемые на всех сценах, объекты GUI, задний фон и тд. Вот пример такого атласа: Такое разбиение так же будет полезно в будущем для применения к текстурам различных настроек. Но об этом позже.
2. Не стоит изменять Transform->Scale. Объекты с измененным Scale попадают в отдельную категорию, увеличивающую кол-во Draw Calls. Я заметил это, когда еще раз проходился по документу Draw Call Batching. Вот что значит перечитывать;) Пройдясь по сцене, я обнаружил огромное количество таких объектов:
- Оказалось, что дизайнеры уже давно увеличили некоторые часто используемые объекты в 1.2 раза через Scale прямо в прифабе объекта. В итоге, мы пришли к решению увеличить их размер прямо в текстуре. Это к тому же соблюдало условие пиксель-в-пиксель, что очень важно для 2d-игры.
- Были объекты, которые имели одно и то же изображение, но разный Scale. Для таких объектов был написан специальный скрипт, который переводил нужный Scale с Transform прямо на меш, используемый для спрайта, т.е. менял размер меша и оставлял Scale = (1, 1, 1).
- Также Scale часто использовался у нас для отражения объекта, например, Scale.x = -1 отражает объект слева направо. Все такие скейлы были заменены на соответствующие им повороты.
- Еще у некоторых объектов Scale был изменен в анимации, пару раз неоправданно. Не забывайте проверять анимации, часто изменения в них — неявные, и могут быть обнаружены только после запуска.
Еще несколько подсказок (взятых в том числе из документа Unity), как сократить количество Draw Calls:
- Статические объекты могут быть помечены как Static. Тогда будет использоваться Static Batching (только в Pro-версии), который тоже поможет сократить кол-во Draw Calls.
- Старайтесь использовать объекты с одним и тем же материалом на одном и том же расстоянии от камеры. Пример: у нас различия по расстоянию в 10 юнитов уже давали 1-2 дополнительных Draw Call. При этом какая-то особая закономерность выявлена не была, но я подозреваю, что есть связь между размерами камеры, размерами объектов, их расстоянием до камеры и количеством Draw Calls. Экспериментируйте!
- Старайтесь, чтобы объекты с разными материалами не перекрывали друг друга. Это тоже увеличивает кол-во Draw Call, особенно для полупрозрачных объектов.
- Многопроходные (multi-pass) шейдеры увеличивают количество Draw Call. У нас таких не было, но полезно будет учесть это в будущем.
- Каждая система частиц дает 1 Draw Call. (Имеется ввиду старая система частиц Unity 3.5.6, используемая нами по сей день. Как обстоят дела в Unity 4, я не знаю). Поэтому если на экране одновременно N систем частиц — это автоматически как минимум N Draw Calls. Обычно, одного и того же эффекта можно достигнуть разными способами, в том числе меньшим числом как систем частиц, так и частиц в системе. Часто видел, как начинающие дизайнеры эффектов используют огромное кол-во частиц (и огромный размер самих частиц), чтобы создать вау-эффект. При этом они не думают о производительности (особенно учитывая, что все это делается в редакторе на PC) и обычно достаточно меньшего количества частиц, чтобы достичь того же эффекта.
Скачки производительности
Второй фактор, влияющий на производительность — так называемые скачки производительности. Порою в игре были «зависания» на 0,5-1 секунду, что конечно же было неприемлемо и напрямую влияло на геймплей. Причем такое наблюдалось даже на самых последних устройствах. И в этом случае помог профайлер! Вот список правил для уменьшения скачков производительности:
1. Старайтесь не использовать Instantiate(), особенно для сложных объектов. Скачки производительности приходились в основном на вызовы Instantiate(), которые создавали новые объекты из прифабов, или клонировали существующие. Причем некоторые объекты были очень громоздкими, что и повлияло на время их создания. Вместо этого пришлось переписать систему так, чтобы объекты использовались заново. Т.е. состояние объекта после окончания использования (или перед использованием) приводилось к начальному. Это также помогло сократить объем используемой памяти (так как на новые объекты больше не нужно было новой памяти) и количество вызовов Destroy().
2. Минимизируйте количество вызовов Destroy().Destroy (особенно для больших объектов) почти всегда приводит к манипуляциям с памятью. А это обычно плачевно сказывается на производительности. Это правило напрямую связано с правилом выше, ибо вызовы Instantiate()/Destroy() обычно связаны. Таким образом, использование объектов заново лишило необходимости уничтожать их.
3. Минимизируйте вызовы gameObject.SetActiveRecursively(). Для сложных объектов вызов может быть очень долгим, потому что он предполагает не просто активацию объектов и их компонентов, но в некоторых случаях и загрузку необходимых ресурсов.
4. Минимизируйте вызовы Object.Find(). Думаю, не стоит объяснять, что время этой операции зависит от кол-ва объектов на сцене. Сюда же относятся функции типа GetComponent().
5. Минимизируйте вызовы Resources.UnloadUnusedAssets() и GC.Collect(). Unity иногда сама прибегает к ним, если недостаточно памяти для загрузки нового ресурса или пришел запрос от ОС освободить неиспользуемую память. Таким образом, первые 2 правила автоматически сокращают кол-во таких вызовов. Лучшее место для вызова Resources.UnloadUnusedAssets вручную — перед загрузкой сцены или непосредственно сразу после ее запуска. Это также поможет освободить дополнительную память для сцены, что иногда бывает критично. Соответствующий скачок производительности можно скрыть, например, экраном загрузки;)
Использование правил выше привело к устранению скачков производительности и намного более плавным геймплею и картинке.
Другие оптимизации
Далее привожу другие правила, которые могут помочь Вам. Большинством из них я сам пользовался на предыдущих этапах оптимизации.Скрипты
- Не используйте GetComponent<>() в Update, FixedUpdate и других подобных функциях. Вместо этого лучше кэшировать компонент в Awake() или Start(). Если объектов, использующих скрипт, очень много, такое кэширование может значительно сократить время работы скрипта.
- Кэшируйте встроенные компоненты типа transform, renderer и тд. Особенно если они используются в функциях типа Update(). Ибо каждый такой вызов делается через GetComponent(). См. правило выше. Это можно сделать, например, так:
- Используйте Vector2(3,4).sqrMagnitude вместо magnitude.
- Используйте Color32 и Mesh.colors32 вместо Color и Mesh.colors при доступе к цветам меша.
- Можно использовать OnBecameVisible/OnBecameInvisible для скриптов, которые могут быть отключены, когда камера больше не видит объект.
- Используйте встроенные массивы. Имеются ввиду массивы типа T[]. Они намного быстрее всех остальных коллекций.
- Отключите логи при работе приложения на устройстве. Обычно печать лога требует доступа к файловой системе, а если логов много, это может привести к печальным последствиям для производительности.
- Отключите исключения. Как следствие — старайтесь не использовать их в коде. Отключение исключений поможет сохранить до 30% производительности. Но не забывайте, что если ошибка произойдет, и она не сможет быть обработана исключением — это падение приложения на устройстве. Поэтому приходится выбирать — либо хорошее тестирование и прирост производительности, либо надежность, но производительность чуть хуже. В случае, когда производительность удовлетворяет, преимущество лучше отдать второму варианту.
Физика
- Чем меньше одновременно активных Rigidbody, тем лучше. Деактивируйте неиспользуемые.
- Сокращайте количество FixedUpdate в единицу времени. Fixed Time = 0.03333 гарантирует физику со скоростью 30 вычислений в секунду, что обычно очень неплохо. Даже 20 может быть приемлемым, что соответствует Fixed Time = 0.05.
- Минимизируйте использование Continuous или Dynamic collision detection. Они очень ресурсозатратны. Для 2d-игр они обычно не нужны.
Анимации
- Следите за количеством анимированных объектов на экране. Чем меньше — тем лучше.
- Вместо нескольких простых анимаций на сложном объекте, можно сделать одну сложную, делающую то же самое.
- Можно попытаться уменьшить Sample Rate у клипа. Особенно если анимаций очень много.
- Используйте Culling Type = Based On Renderers или Based On User Bounds. Тогда анимация будет проигрываться только тогда, когда объект виден на экране. Если это, конечно, устраивает.
Система частиц
- Используйте Vertical Billboard вместо Billboardв Particle Renderer.
- Используйте на мобильных устройства шейдеры из Mobile->Particles. Это касается не только системы частиц.
- Используйте как можно меньше систем частиц и самих частиц в системе для достижения нужного эффекта.
- Для слабых систем можно отключить некоторые малозаметные или несущественные системы частиц. Что позволит сохранить несколько Draw Calls и поднять на них производительность в ущерб эффектности. Уверяю, зачастую приятнее получать удовольствие от процесса игры, нежели от мегакрутых эффектов.
GUI
- Не используйте OnGUI(). Каждый такой вызов — несколько дополнительных Draw Calls. Тоже самое относится к GUILayout. И вообще, поддержка GUI в Unity сделана очень плохо. У нас, например, своя система GUI, основанная на спрайтах. В Asset Store есть несколько других очень полезных плагинов для GUI.
- Размер текстуры, генерируемой для шрифта, можно уменьшить, добавив только используемые символы. В Font Settings установить Character = Custom Set, а в Custom Chars включить используемые символы:
- Можно попробовать использовать отдельные камеры для объектов сцены и GUI. Это позволит сделать GUI zoom-независимым, что освобождает от использования на нем Scale при увеличении/уменьшении. Иногда это может увеличить производительность.
Другое
- Отключить акселерометр, если он не используется. Можно также понизить частоту измерений в Player Settings.
- Можно ограничить максимальный FPS на старых устройствах. Например, установив его в: Application.targetFrameRate = 30. Обычно это приводит к более гладкой картинке. Также, это уменьшает просадку аккумулятора, т.к. за то же время требуется меньше процессорной мощности. Вообще, на устройствах Apple FPS > 60 не имеет смысла, т.к. частота обновления экрана у них — 60.
- Иногда периодическая частая сборка мусора сглаживает производительность. Потому что сама система делает это редко и когда уже все совсем плохо, и может накопиться большое кол-во объектов для уничтожения, что приводит к скачку производительности. Если делать это чаще, объектов для уничтожения будет меньше, и освобождение памяти будет более гладким. С другой стороны, за все время работы сцены сборка мусора может и не понадобиться.
Уменьшение размера приложения
Для чего это может понадобиться? Раньше это делалось потому, что приложения размером <20Mb можно было загружать на iOS через 3g-сеть. Что в принципе должно увеличить кол-во закачек, хотя конкретной статистики я не видел. В связи с выпуском iPad3 приложения стали «жирнее», и порог был поднят до 50Mb. Не стоит также забывать, что после заливки приложения в AppStore оно будет увеличено в размере в среднем на 4Mb. Для проверки, сколько приложение будет весить после заливки в AppStore в xCode в Organizer->Archives даже появилась специальная кнопочка Estimate Size:Все необходимое по уменьшению размера билда описано в документах Unity:
Я же опишу здесь то, что использовал сам. Начнем с того, что влияет на производительность:1. Используйте правильный формат текстуры. Это также позволит уменьшить объем используемой текстурной памяти, а соответственно и повысить производительность. Иногда достаточно использовать формат текстуры 16 bits, особенно если вся графика нарисована всего в нескольких цветах. Сравните: Для монотонных текстур можно использовать только ее серую и альфа-компоненту и лепить из них готовый объект, используя специально написанный для этого шейдер и умножение на цвет: Для задников и нечетких объектов можно использовать компрессию. Сейчас PVRTC-компрессия на iOS довольно продвинутая. Стоит помнить, что чем больше текстура — тем лучше ее качество после компрессии. На маленьких текстурах использование компрессии может быть неприемлемым. Чтобы это все имело смысл, нужно разделять объекты на группы типа «задний фон», GUI, игровые объекты, о чем я уже писал. Тогда на каждый тип можно завести свой атлас и использовать различные настройки формата текстуры.
2. Используйте правильный формат звуков. Раньше я не задумывался об этом. Использование компрессии на звуках позволило мне не только сократить размер приложения, но и объем используемой им памяти. Сам я пользуюсь следующими правилами:
- Используйте Audio Format = Native для очень коротких и маленьких по размеру звуков (< 100 Kb).
- Для остальных используйте компрессию. Compression = 96 Kbps — это уже очень приемлемо. Ниже — заметны искажения.
- Используйте Load Type = Compressed in Memory для большинства ужатых звуков. Это позволит уменьшить объем используемой памяти, но может влиять на производительность, так как требует распаковки во время воспроизведения.
- Для фоновой музыки используйте Load Type = Stream from disk, особенно на системах с быстрым HDD. Это позволит сохранить очень много памяти.
- Используйте Decompress on Load во всех остальных случаях. Звук будет занимать больше памяти, но практически не будет «сажать» CPU, что поможет иногда избавиться от скачков производительности.
- Используйте Hardware Decoding для фоновой музыки. Встроенный декодер в устройствах iOS позволяет сократить использование CPU на проигрывании фоновой музыки, но это может быть сделано только для одного трека одновременно.
- Используйте Force to Mono, если стерео не нужно. Или если в файле оно присутствует, но не различимо. Это в 2 раза уменьшит объем используемого места (как в памяти, так и на диске).
- Установите в настройках проекта Stripping Level = Use micro mscorlib. Это только для владельцев Pro. Не используйте без надобности единицы из System.dll и System.Xml.dll. Они не совместимы с Use micro mscolib.
- Установите API Compatibility Level в .Net 2.0 subset. Но иногда после этого код может не работать, если соответствующие классы/функции и тд не входят в .Net 2.0 subset. Что однажды случилось в моем случае.
- Также следует избавиться от зависимостей от ненужных библиотек.
- Установите Script Call Optimization Level в Fast but no exceptions, чтобы отключить исключения. Это также уменьшит размер билда.
- Установите Target Platform в armv6 (OpenGL ES1.1). Если вам не нужен armv7. Или наоборот, в armv7, но не оба одновременно. Учитывая, что Apple все меньше поддерживает устройства с armv6, имеет смысл оставить лишь armv7.
- Не используйте массивы JS. Лучше не использовать JS вообще, используйте C#. Обычно, после переписывания кода скриптов с JS на C# приложение весит меньше.
Использованные источники вдохновения
В первую очередь использовались документы Unity — самые полезные ресурсы от самих разработчиков Unity. Их читать нужно в первую очередь, желательно по несколько раз, а через некоторое время еще раз, потому что они постоянно обновляются. Отдельно выношу уже указанные документы по сокращению размера приложения: Другие источники:Заключение
Проведенная мною оптимизация позволила существенно повысить производительность игры и играбельность в целом. В качестве дополнительного бонуса был уменьшен размер приложения;) Надеюсь, моя статья поможет Вам сделать ваше приложение на Unity еще лучше. Возможно, кому-то будет интересно почитать мои предыдущие статьи: Скорее всего, следующей моей статьей будет статья о том, как упростить работу в Unity и минимизировать кол-во ошибок. Если у кого-то есть вопросы, предложения, поправки — я всегда готов выслушать и обсудить.habr.com
Первые шаги в оптимизации и полировке игры на Unity3d / Хабр
После того как я закончил мой первый проект, пришла мысль о портировании его на мобильные устройства или хотя бы запуске на встроенном GPU. Во всех гайдах по оптимизации, в одном из первых советов вам сообщат, что не стоит переживать о производительности заранее, начинайте оптимизировать, после того как все закончите, и вы постепенно приведете все в порядок. Так и я, выпустив изначально игру на десктоп, решил, что никогда не будет поздно оптимизировать ее под мобильные устройства. К сожалению, мне не удалось в полной мере достичь поставленной цели, потому как, похоже, что мобильные игры следует с самого начала разрабатывать с прицелом на слабое железо. На данный момент, для дальнейшей оптимизации под мобильные платформы я вижу только необходимость серьезно переделать геймплей и дизайн игрового мира. Однако и в текущем варианте получен ценный опыт оптимизации под Unity3d и результирующий прирост производительности более чем в 300% на интегрированном GPU.Давайте начнем с CPU
Достаточно очевидный список сформировавшийся во время оптимизации:- Не используйте Свойства! Поля и методы — ваши лучшие друзья.
- Кешируйте все, что вы получаете через GetComponent<>, сюда же входят transforms, rigidbodies и др.
- Старайтесь никогда не обращаться к объектам дважды для получения одних и тех же данных. Почти всегда это доступ через Свойства, от которых необходимо отказываться. Зачастую можно увидеть как в разных скриптах запрашивается позиция одного и того же объекта, что лучше заменить на кеширование той самой позиции, обновляя ее один раз внутри Update или даже FixedUpdate в этом объекте.
- Кешируйте всю математику. Каждый вызов Vector.Up будет под капотом вызывать конструктор, что не очень быстро. Я создал статический CachedMath класс, в который были сложены все направления, часто используемые векторы и кватернионы.
- Попробуйте обходиться без использования типа String. Каждая строка требует выделения памяти, и при бесконтрольном использовании строк, вы увидите, как GC остановит все потоки для своего вызова. В моем случае основными источниками строк были индикатор FPS и таймер во время гонки. Решением стало создать пул строковых литералов для всех цифр от 1 до 100. Это полностью исключило выделение строк в каждом кадре.
- Никогда не используйте foreach, просто замените на for, если хотите сберечь GC и драгоценное время CPU. К тем же последствиям зачастую приводит и использование шаблонных методов(generics).
- LINQ является еще одним источником нагрузки на GC. Старайтесь упрощать ваши LINQ выражения, или еще лучше, полностью заменять их на простые конструкции.
- Все строки используемые в Animator-объектах следует сконвертировать в целочисленные идентификаторы через Animator.StringToHash()
- Инстанциирование объектов является очень тяжелой операций, поэтому стоит для частых созданий использовать пул объектов и затем их переиспользовать.
- Удаляйте все пустые методы Update и FixedUpdate. Также, если ваш скрипт использует оба или только фиксированный, то стоит подумать о переносе любой возможной логики из фиксированного в обычный Update.
Также никогда не поздно упростить некоторую логику в ваших скриптах, или количество обрабатываемых данных в разумных пределах. Однако еще раз повторюсь, что вам нужны веские основания полученные с помощью профилировщика, что конкретный метод слишком медленен. После изменений обязательно проследите, чтобы профилировщик отображал меньшие цифры, чем до начала оптимизаций.
Самым плохим моментом оптимизаций является то, что ваш структурированный и "идеальный" код растекается в местами не очень читабельное нечто. К сожалению, это неизбежно. Главное помнить о том, что это жертва в угоду производительности.
Теперь GPU
С CPU советы были достаточно универсальны и они применимы в любом проекте. Чего не скажешь о GPU-оптимизациях, которые зачастую сильно зависят от конкретной сцены. Однако, если вы не используете сильной магии в своих шейдерах, то явный индикатор — это количество проходов GPU(pass-calls).Моя игра содержит открытый мир с океаном как основой для передвижений и несколькими островами в качестве декораций. В моем случае проходов было больше 2000, и мне удалось снизить это значение до примерно 300.
Материалы. Уменьшайте количество используемых материалов насколько это возможно. Каждая смена материала это новый проход, также как и каждый текстурный слой внутри материала это тоже новый проход. Конечно, я несколько упрощаю и проходы формируются не так просто, но факт остается — слишком много проходов будут непомерно нагружать слабый GPU. Для мобильных устройств рекомендуют что-то в районе 40-60 проходов. Более продвинутые устройства могут обрабатывать и в районе сотни. Так что вам есть куда стремиться!
Видимые объекты. В моей сцене слишком много объектов, которые постоянно присутствуют на экране. Проблема лишь в том, что они и должны быть видимы! Конечно, издалека нам не нужна такая же детализация как и вблизи, поэтому очевидным решением было использовать LOD-объекты.
Импостеры. Я предпочел заменить мои объекты с помощью импостеров (в целом это очень похоже на биллборды, но это множество текстур полученных пререндером объекта со всех сторон). Во встроенном Asset-Store от Unity3d множество готовых платных решений для LOD и импостеров. Однако я решил воспроизвести базовый алгоритм самостоятельно. Я создал скрипт-расширение редактора, который создавал копию необходимого объекта, менял его слой, затем создавал камеру которая была ограничена только этим специальным слоем, и производил отрисовку объекта в текстуры со всех сторон. Были добавлены основные параметры, как название результирующей папки с текстурами, разрешение получаемых текстур, расстояние до объекта, смещение по высоте, количество сторон и флаг для сохранения или отключения освещения во время создания импостера. После того как все действия завершены, скрипт удалял уже ненужную копию объекта.
Спрайты. Теперь почти все объекты заменяются спрайтами на определенном удалении от камеры. Но количество проходов было все еще огромным. Тогда я обнаружил, что спрайты это далеко не всегда легковесная форма для отображения. Каждый спрайт по умолчанию триангулирует картинку, создавая множество вершин. На каждые 900 или около того(по официальной документации) вершин, создается очередной проход (официально группировка|пакетирование|batching — сохранение данных множества объектов в одну инструкцию для GPU — вообще неприменим к SpriteRenderer объектам). В то же время нельзя заменить все спрайты на полные квадратные регионы с прозрачностью, т. к. все прозрачные пиксели все еще требуют отрисовки, и GPU их не пропускает. Также прозрачность ведет к проблемам во время отрисовки всех спрайтов из-за проверки на глубину отрисовки. GPU все еще будет создавать дополнительный проход для одного или двух спрайтов, между отрисовкой множества уже сгруппированных только потому, что этого требует проверка по глубине. Единственное, что удалось сделать — это изменить тип спрайта на Multiple, что меняет внутренний механизм триангуляции, который создает намного меньше вершин.
Упаковщик спрайтов(SpritePacker). Это последнее о чем вы должны помнить при работе со спрайтами. Чтобы явно указать для спрайта необходимость упаковки в карту атлас, нужно указать его Tag. В момент отрисовки спрайтов из одного атласа GPU не создает дополнительных проходов, даже если порядок отрисовки по глубине не оптимален для неупакованных спрайтов. Размер результирующего атласа также важен. По умолчанию он ограничен значением в 2048х2048. Это максимальный размер атласа, и он динамически подстраивается под оптимальный, в зависимости от заполнения. В моем случае этого было недостаточно для упаковки всех необходимых мне спрайтов на одной странице. Замена алгоритма упаковки на собственный, который основан на базовом, но с измененным значением размера на 4096x2048 значительно улучшило производительность.
Дальнейшее увеличение до 4096x4096 почти не отразилось на количестве проходов, но при этом даже несколько ухудшило производительность. Стоит помнить, что некоторые спрайты не могут быть размещены на одном атласе вместе — для этого они должны иметь одинаковые настройки компрессии, и часть других параметров, иначе они будут автоматически разделены по разным группам. Поэтому старайтесь группировать спрайты по атласам логически и визуально, чтобы в один момент на экране отображалось как можно меньше атласов, ведь каждое переключение между ними, включая неоптимальное расположение по глубине будут стоить вам проходов.
В моем случае я разделил атласы на UI-спрайты, затем все объекты, которые расположены очень далеко — пришлось использовать несколько атласов, но они были разделены на диаметрально противоположные по расположению в мире группы, и одновременно на экране увидеть их достаточно сложно, и все оставшиеся промежуточные объекты.
После всех изменений, производительность улучшилась настолько, что отключение всех объектов импостеров практически не влияет на результирующий FPS.
Вода. В моем случае, мне необходимо было получить более производительную воду. Изначально в сцене использовалась waterProDaytime с включенным преломлением, которая подверглась минимальным изменениям для поддержки пены вдоль береговой линии. Была убрана камера преломлений и заменена на вызов grabpass. Все дело в том, что для корректного отображения преломлений, камере пришлось отключать матрицу отсечения, т. к. в противном случае — все объекты, выше уровня воды, просто не отбрасывали теней. Из-за этого ограничения, камера дополнительно отрисовывала сцену целиком, и вызов grabpass оказался в этом случае быстрее. Также был изменен параметр LOD множителя на время отрисовки отражений. Таким образом импостеры чуть дольше отображаются в воде, что дополнительно снижает нагрузку.
Все изменения повысили производительность на интегрированном GPU с 6-8 до 22-24 кадров в секунду. По-прежнему низкий показатель, но лучшего добиться пока не удалось. Все еще рекомендую запуск своей игры на дискретной графике.
Полировка
Новый релиз не хотелось выпускать только с изменением в производительности, поэтому было решено закрыть несколько достаточно важных моментов в том, как игра выглядит, а именно — UI.Все, кто видел мою игру в первом релизе говорили, что UI плох. Он просто мертв, и даже если я не видел этого раньше, сейчас я понимаю что же с ним не так.
В нем отсутствовали звуки, не было движения, жизни. Запустив один из проектов, я начал замечать детали главного меню, которые раньше для меня были просто невидимы. Так что я добавил все недостающее в первом приближении. Теперь выбранная кнопка анимирована, перевод фокуса и нажатие кнопок озвучены из бесплатных ресурсов в Asset-Store.
Все элементы в самой игре анимировать было бы слишком отвлекающим и раздражающим игрока эффектом, поэтому я добавил внутреннее переливающееся свечение, которое очень мягко напоминает о UI элементах.
И последним стал экран медалей. Он был просто статичен, в нем было ужасно скучно находиться. А ведь это должно быть местом в котором игрок с наслаждением наблюдает за своим прогрессом. Так что я добавил немного жизни. Теперь его украшают три системы частиц, создающих мягкие переливающиеся шары света похожие на светлячков. Частицы, к сожалению по умолчанию не доступны на UI, поэтому пришлось создавать дополнительную камеру, которая отрисовывает только эти частицы в текстуру, затем накладывающуюся на UI.
Что было оставлено позади
Мне не удалось запустить игру на мобильных устройствах. Даже самое мощное железо не воспроизводит игру с приемлемой производительностью. Возможно, с будущими версиями Unity3d что-то изменится, но на данный момент, как я уже упоминал, разработка игры с прицелом на мобильные устройства должна проводиться иначе с самого начала.Также, в моем случае проявлялись очень странные артефакты отрисовки на мобильных устройствах со светящимися в некоторых случаях, буквально как лазеры материалами, которые выглядят вполне нормально на десктопе.
Другая цель, появившаяся во время оптимизаций в виде быстрого запуска приложения, или даже плавного запуска, также не была достигнута. Холодный старт в моем случае длится больше минуты. Причем каждый последующий запуск сокращает это время почти вдвое. Так что, похоже, это какое-то внутреннее необходимое требование Unity-плеера. Самым главным же недостатком является зависание UI потока при активации сцены. Я уже использую асинхронный вариант загрузки сцены, даже переключил его в Additive режим, однако UI просто останавливается после 90% загрузки, когда необходимо переключить флаг allowSceneActivation. Было бы здорово, если кто-то подскажет обходной путь для Unity5.x, или нечто вроде вызова события которое может потокобезопасно и с главным приоритетом изменять Ui-объекты с их перерисовкой, чтобы была хоть какая-то индикация процесса приложения.
P.S.
Конечно, это только моя история и она не решит магическим образом все ваши проблемы. Местами она слишком субъективна, но все же надеюсь, что кто-то найдет эти советы полезными.P.P.SЯ описал главное, не вдаваясь глубоко в технические детали. Проект подвергся очень большому количеству изменений, и не все было описано — сейчас могу точно вспомнить, что с прошлой публикации просили разобраться с водой просачивающейся сквозь лодку — и это точно было закрыто. Также были добавлены звуки при прохождении чекпоинтов и получения медалей, как и небольшая анимация таймера на чекпоинтах и цветовое информирование о времени. Если кому-то интересно посмотреть все в действии, к сожалению, видео еще нет, однако триал теперь бесконечен.
habr.com
Juds » Оптимизация 2d-приложений для мобильных устройств в Unity3d
Недавно наша студия завершила разработку большого обновления — Captain Antarctica: Endless Run — для устройств на iOs. Кропотливая работа над обновлением затронула производительность, которая оказалась очень низкой на слабых устройствах. Я боролся с этим целую неделю и добился как минимум 30 FPS, а также значительного сокращения размера приложения. Хочу рассказать, как я это сделал, ну и как делать не стоит.Статья пригодится любым разработчикам на Unity (причем не только менеджерам проектов и техническим специалистам, но и просто программистам, художникам и дизайнерам), потому что она затрагивает как оптимизацию на Unity в целом, так и конкретно оптимизацию 2d-приложений для мобильных устройств.
Возможно все!
Начну с того, что каждый раз, когда я приступаю к оптимизации, я поначалу не верю, что можно что-то еще оптимизировать, особенно если проект уже прошел несколько циклов оптимизации до этого. Но просмотр официальной документации Unity, тем на форумах, статей в Интернете наводит меня на мысль о новых возможных улучшениях. Таким образом я веду специальный список, в котором записаны основные идеи по тому, что можно оптимизировать в проекте на Unity, постоянно обновляю его и первым делом обращаюсь к нему, когда речь заходит об оптимизации. Этими идеями я хочу с Вами поделиться. Надеюсь, статья поможет Вам сделать Ваш проект намного более шустрым.Сразу обозначу, что разработка велась на Unity 3.5.6, целевая платформа — устройства Apple от iPhone 3GS и новее.
Базовые правила
Для начала приведу несколько правил, которыми я пользуюсь при разработке и оптимизации.
1. Не оптимизируйте заранее.Это золотое правило должно быть знакомо всем, кто когда-либо занимался оптимизацией. Вспомним правило 80/20: 80% пользы получается от 20% работы.
Цифры довольно условны, но я имел ввиду вот что: наиболее вероятно, что большая часть оптимизаций, которые Вы собираетесь сделать на начальном этапе проекта, скорее всего вообще никак не повлияет на конечный проект в целом.
Однако, есть пара исключений из этого правила, которые особенно важны при разработке для мобильных устройств, потому что это правило больше подходит для PC-проектов. А PC намного производительнее мобильных платформ и менее ограничены в ресурсах. Так вот, исключения:
- Допустим, есть код, или конструкция, которую вы уже сейчас знаете, как написать лучше, с меньшей затратой по памяти/процессора и тд. Вы об этом знаете по своему опыту, потому что не раз оптимизировали компоненты подобного рода, и знаете, что это приводит к увеличению производительности. Так почему бы уже сейчас не написать ее правильно? Обычно такие вещи складываются в свод правил типа «как правильно писать код, и как писать не надо», и грамотный программист постоянно им пользуется во избежание ошибок в будущем. Нечто подобное имеет место для художников, дизайнеров и тд.
- Если есть действие, которое будет повторяться много раз, и с самого начала можно его оптимизировать, почему бы это не сделать сразу. Дальше все будет идти автоматом, и не нужно будет исправлять одну и ту же вещь несколько раз. Сюда относятся, например, вспомогательные скрипты, помогающие дизайнерам ускорить однообразную работу с группой объектов.
2. Найдите то, что нужно оптимизировать.Несколько лет назад я имел такую ситуацию: проходишься по коду, сценам и тд и оптимизируешь все, что только можно, а потом смотришь на производительность — это ошибка начинающего оптимизатора.Для начала нужно найти то, что тормозит систему, на чем бывают скачки производительности. В этом очень сильно помогает профайлер. Конечно, он довольно условен и сам немного нагружает систему, но польза от него неоспорима! В Unity Pro есть встроенный пройфалер, довольно удобный. Но если у Вас обычный Unity, можно использовать профайлер xCode, или любой другой подходящий. Профайлер помогает находить наиболее нагружающий код, показывает используемую память, насколько звуки грузят систему, кол-во DrawCall на конечном устройстве и тд. Таким образом, прежде чем оптимизировать, прогоните приложение через профайлер. Думаю, Вы много чего нового узнаете о своем проекте)
Что мне помогло решить проблему с производительностью?
Еще до прогона через профайлер было очевидно, что слабое место — в кол-ве Draw Calls. В среднем сцена выдавала порядка 70 DrawCalls, что для устройств уровня iPad1 и ниже является фатальным. Нормально для них — 30-40 Draw Calls. Посмотреть кол-во Draw Calls можно прямо в редакторе в окне Game->Stats:.
Кол-во Draw Calls, показываемых в редакторе, совпадает с таковым на конечных устройствах. Профайлер это подтвердил. Вообще, очень полезно смотреть эту статистику, и не только программистам, но и дизайнерам, для нахождения «тугих» мест в игре.В наших сценах плохо работало группирование нескольких Draw Calls для одного и того же материала в один Draw Call. Это называется Dynamic Batching. Я начал рыть на тему «как понизить кол-во Draw Calls и улучшить их группирование». Ниже перечислены основные правила, придерживаясь которых, можно получить приемлемое количество Draw Calls. Вот те, которые мне очень сильно помогли:
1. Использовать атласы для комбинирования нескольких текстур в одну большую.
На самом деле важнее даже, чтобы спрайты/модели использовали не то что бы одну общую текстуру, а скорее один общий материал. Именно в кол-ве различных материалов измеряется кол-во Draw Calls (в идеальном случае). Поэтому у нас в проекте используемые изображения всегда объединены в атласы, разбитые на категории: объекты, используемые на всех сценах, объекты GUI, задний фон и тд. Вот пример такого атласа:Такое разбиение так же будет полезно в будущем для применения к текстурам различных настроек. Но об этом позже.
2. Не стоит изменять Transform->Scale.
Объекты с измененным Scale попадают в отдельную категорию, увеличивающую кол-во Draw Calls. Я заметил это, когда еще раз проходился по документу Draw Call Batching. Вот что значит перечитывать;) Пройдясь по сцене, я обнаружил огромное количество таких объектов:
- Оказалось, что дизайнеры уже давно увеличили некоторые часто используемые объекты в 1.2 раза через Scale прямо в прифабе объекта. В итоге, мы пришли к решению увеличить их размер прямо в текстуре. Это к тому же соблюдало условие пиксель-в-пиксель, что очень важно для 2d-игры.
- Были объекты, которые имели одно и то же изображение, но разный Scale. Для таких объектов был написан специальный скрипт, который переводил нужный Scale с Transform прямо на меш, используемый для спрайта, т.е. менял размер меша и оставлял Scale = (1, 1, 1).
- Также Scale часто использовался у нас для отражения объекта, например, Scale.x = -1 отражает объект слева направо. Все такие скейлы были заменены на соответствующие им повороты.
- Еще у некоторых объектов Scale был изменен в анимации, пару раз неоправданно. Не забывайте проверять анимации, часто изменения в них — неявные, и могут быть обнаружены только после запуска.
В результате устранения практически всех изменений Scale удалось снизить кол-во Draw Call практически вдвое! Правда, эти улучшения заняли у нас порядочное время, поэтому стоит помнить о Scale уже на начальном этапе дизайна уровней. И теперь в Памятке дизайнерам у нас жирным шрифтом красным цветом написано: Старайтесь избегать изменения Scale.
Еще несколько подсказок (взятых в том числе из документа Unity), как сократить количество Draw Calls:
- Статические объекты могут быть помечены как Static. Тогда будет использоваться Static Batching (только в Pro-версии), который тоже поможет сократить кол-во Draw Calls.
- Старайтесь использовать объекты с одним и тем же материалом на одном и том же расстоянии от камеры. Пример: у нас различия по расстоянию в 10 юнитов уже давали 1-2 дополнительных Draw Call. При этом какая-то особая закономерность выявлена не была, но я подозреваю, что есть связь между размерами камеры, размерами объектов, их расстоянием до камеры и количеством Draw Calls. Экспериментируйте!
- Старайтесь, чтобы объекты с разными материалами не перекрывали друг друга. Это тоже увеличивает кол-во Draw Call, особенно для полупрозрачных объектов.
- Многопроходные (multi-pass) шейдеры увеличивают количество Draw Call. У нас таких не было, но полезно будет учесть это в будущем.
- Каждая система частиц дает 1 Draw Call. (Имеется ввиду старая система частиц Unity 3.5.6, используемая нами по сей день. Как обстоят дела в Unity 4, я не знаю). Поэтому если на экране одновременно N систем частиц — это автоматически как минимум N Draw Calls. Обычно, одного и того же эффекта можно достигнуть разными способами, в том числе меньшим числом как систем частиц, так и частиц в системе. Часто видел, как начинающие дизайнеры эффектов используют огромное кол-во частиц (и огромный размер самих частиц), чтобы создать вау-эффект. При этом они не думают о производительности (особенно учитывая, что все это делается в редакторе на PC) и обычно достаточно меньшего количества частиц, чтобы достичь того же эффекта.
Скачки производительности
Второй фактор, влияющий на производительность — так называемые скачки производительности. Порою в игре были «зависания» на 0,5-1 секунду, что конечно же было неприемлемо и напрямую влияло на геймплей. Причем такое наблюдалось даже на самых последних устройствах.И в этом случае помог профайлер! Вот список правил для уменьшения скачков производительности:
1. Старайтесь не использовать Instantiate(), особенно для сложных объектов.
Скачки производительности приходились в основном на вызовы Instantiate(), которые создавали новые объекты из прифабов, или клонировали существующие. Причем некоторые объекты были очень громоздкими, что и повлияло на время их создания. Вместо этого пришлось переписать систему так, чтобы объекты использовались заново. Т.е. состояние объекта после окончания использования (или перед использованием) приводилось к начальному. Это также помогло сократить объем используемой памяти (так как на новые объекты больше не нужно было новой памяти) и количество вызовов Destroy().
2. Минимизируйте количество вызовов Destroy().
Destroy (особенно для больших объектов) почти всегда приводит к манипуляциям с памятью. А это обычно плачевно сказывается на производительности. Это правило напрямую связано с правилом выше, ибо вызовы Instantiate()/Destroy()обычно связаны. Таким образом, использование объектов заново лишило необходимости уничтожать их.
3. Минимизируйте вызовы gameObject.SetActiveRecursively().
Для сложных объектов вызов может быть очень долгим, потому что он предполагает не просто активацию объектов и их компонентов, но в некоторых случаях и загрузку необходимых ресурсов.
4. Минимизируйте вызовы Object.Find().Думаю, не стоит объяснять, что время этой операции зависит от кол-ва объектов на сцене. Сюда же относятся функции типа GetComponent().
5. Минимизируйте вызовы Resources.UnloadUnusedAssets() и GC.Collect().
Unity иногда сама прибегает к ним, если недостаточно памяти для загрузки нового ресурса или пришел запрос от ОС освободить неиспользуемую память. Таким образом, первые 2 правила автоматически сокращают кол-во таких вызовов. Лучшее место для вызова Resources.UnloadUnusedAssets вручную — перед загрузкой сцены или непосредственно сразу после ее запуска. Это также поможет освободить дополнительную память для сцены, что иногда бывает критично. Соответствующий скачок производительности можно скрыть, например, экраном загрузки;)
Использование правил выше привело к устранению скачков производительности и намного более плавным геймплею и картинке.
Другие оптимизации
Далее привожу другие правила, которые могут помочь Вам. Большинством из них я сам пользовался на предыдущих этапах оптимизации.
Скрипты
- Не используйте GetComponent<>() в Update, FixedUpdate и других подобных функциях. Вместо этого лучше кэшировать компонент в Awake() или Start(). Если объектов, использующих скрипт, очень много, такое кэширование может значительно сократить время работы скрипта.
- Кэшируйте встроенные компоненты типа transform, renderer и тд. Особенно если они используются в функциях типаUpdate(). Ибо каждый такой вызов делается через GetComponent(). См. правило выше. Это можно сделать, например, так:
/// <summary> /// Cached transform /// </summary> protected new Transform transform { get { if(cachedTransform == null) cachedTransform = base.transform; return cachedTransform; } } private Transform cachedTransform = null;
/// <summary> /// Cached transform /// </summary> protected new Transform transform { get { if(cachedTransform == null) cachedTransform = base.transform; return cachedTransform; } } private Transform cachedTransform = null;
|
- спользуйте Vector2(3,4).sqrMagnitude вместо magnitude.
- Используйте Color32 и Mesh.colors32 вместо Color и Mesh.colors при доступе к цветам меша.
- Можно использовать OnBecameVisible/OnBecameInvisible для скриптов, которые могут быть отключены, когда камера больше не видит объект.
- Используйте встроенные массивы. Имеются ввиду массивы типа T[]. Они намного быстрее всех остальных коллекций.
- Отключите логи при работе приложения на устройстве. Обычно печать лога требует доступа к файловой системе, а если логов много, это может привести к печальным последствиям для производительности.
- Отключите исключения. Как следствие — старайтесь не использовать их в коде. Отключение исключений поможет сохранить до 30% производительности. Но не забывайте, что если ошибка произойдет, и она не сможет быть обработана исключением — это падение приложения на устройстве. Поэтому приходится выбирать — либо хорошее тестирование и прирост производительности, либо надежность, но производительность чуть хуже. В случае, когда производительность удовлетворяет, преимущество лучше отдать второму варианту.
Физика
- Чем меньше одновременно активных Rigidbody, тем лучше. Деактивируйте неиспользуемые.
- Сокращайте количество FixedUpdate в единицу времени. Fixed Time = 0.03333 гарантирует физику со скоростью 30 вычислений в секунду, что обычно очень неплохо. Даже 20 может быть приемлемым, что соответствует Fixed Time = 0.05.
- Минимизируйте использование Continuous или Dynamic collision detection. Они очень ресурсозатратны. Для 2d-игр они обычно не нужны.
Анимации
- Следите за количеством анимированных объектов на экране. Чем меньше — тем лучше.
- Вместо нескольких простых анимаций на сложном объекте, можно сделать одну сложную, делающую то же самое.
- Можно попытаться уменьшить Sample Rate у клипа. Особенно если анимаций очень много.
- Используйте Culling Type = Based On Renderers или Based On User Bounds. Тогда анимация будет проигрываться только тогда, когда объект виден на экране.Если это, конечно, устраивает.
Система частиц
- Используйте Vertical Billboard вместо Billboardв Particle Renderer.
- Используйте на мобильных устройства шейдеры из Mobile->Particles. Это касается не только системы частиц.
- Используйте как можно меньше систем частиц и самих частиц в системе для достижения нужного эффекта.
- Для слабых систем можно отключить некоторые малозаметные или несущественные системы частиц. Что позволит сохранить несколько Draw Calls и поднять на них производительность в ущерб эффектности. Уверяю, зачастую приятнее получать удовольствие от процесса игры, нежели от мегакрутых эффектов.
GUI
- Не используйте OnGUI(). Каждый такой вызов — несколько дополнительных Draw Calls. Тоже самое относится кGUILayout. И вообще, поддержка GUI в Unity сделана очень плохо. У нас, например, своя система GUI, основанная на спрайтах. В Asset Store есть несколько других очень полезных плагинов для GUI.
- Размер текстуры, генерируемой для шрифта, можно уменьшить, добавив только используемые символы. В Font Settings установить Character = Custom Set, а в Custom Chars включить используемые символы:
- Можно попробовать использовать отдельные камеры для объектов сцены и GUI. Это позволит сделать GUI zoom-независимым, что освобождает от использования на нем Scale при увеличении/уменьшении. Иногда это может увеличить производительность.
Другое
- Отключить акселерометр, если он не используется. Можно также понизить частоту измерений в Player Settings.
- Можно ограничить максимальный FPS на старых устройствах. Например, установив его в:Application.targetFrameRate = 30. Обычно это приводит к более гладкой картинке. Также, это уменьшает просадку аккумулятора, т.к. за то же время требуется меньше процессорной мощности. Вообще, на устройствах Apple FPS > 60 не имеет смысла, т.к. частота обновления экрана у них — 60.
- Иногда периодическая частая сборка мусора сглаживает производительность. Потому что сама система делает это редко и когда уже все совсем плохо, и может накопиться большое кол-во объектов для уничтожения, что приводит к скачку производительности. Если делать это чаще, объектов для уничтожения будет меньше, и освобождение памяти будет более гладким. С другой стороны, за все время работы сцены сборка мусора может и не понадобиться.
Уменьшение размера приложения
Для чего это может понадобиться? Раньше это делалось потому, что приложения размером <20Mb можно было загружать на iOS через 3g-сеть. Что в принципе должно увеличить кол-во закачек, хотя конкретной статистики я не видел. В связи с выпуском iPad3 приложения стали «жирнее», и порог был поднят до 50Mb. Не стоит также забывать, что после заливки приложения в AppStore оно будет увеличено в размере в среднем на 4Mb. Для проверки, сколько приложение будет весить после заливки в AppStore в xCode в Organizer->Archives даже появилась специальная кнопочка Estimate Size:
Все необходимое по уменьшению размера билда описано в документах Unity:
Я же опишу здесь то, что использовал сам. Начнем с того, что влияет на производительность:
1. Используйте правильный формат текстуры.Это также позволит уменьшить объем используемой текстурной памяти, а соответственно и повысить производительность. Иногда достаточно использовать формат текстуры 16 bits, особенно если вся графика нарисована всего в нескольких цветах. Сравните:Для монотонных текстур можно использовать только ее серую и альфа-компоненту и лепить из них готовый объект, используя специально написанный для этого шейдер и умножение на цвет:Для задников и нечетких объектов можно использовать компрессию. Сейчас PVRTC-компрессия на iOS довольно продвинутая. Стоит помнить, что чем больше текстура — тем лучше ее качество после компрессии. На маленьких текстурах использование компрессии может быть неприемлемым.Чтобы это все имело смысл, нужно разделять объекты на группы типа «задний фон», GUI, игровые объекты, о чем я уже писал. Тогда на каждый тип можно завести свой атлас и использовать различные настройки формата текстуры.
2. Используйте правильный формат звуков.Раньше я не задумывался об этом. Использование компрессии на звуках позволило мне не только сократить размер приложения, но и объем используемой им памяти. Сам я пользуюсь следующими правилами:
- Используйте Audio Format = Native для очень коротких и маленьких по размеру звуков (< 100 Kb).
- Для остальных используйте компрессию. Compression = 96 Kbps — это уже очень приемлемо. Ниже — заметны искажения.
- Используйте Load Type = Compressed in Memory для большинства ужатых звуков. Это позволит уменьшить объем используемой памяти, но может влиять на производительность, так как требует распаковки во время воспроизведения.
- Для фоновой музыки используйте Load Type = Stream from disk, особенно на системах с быстрым HDD. Это позволит сохранить очень много памяти.
- Используйте Decompress on Load во всех остальных случаях. Звук будет занимать больше памяти, но практически не будет «сажать» CPU, что поможет иногда избавиться от скачков производительности.
- Используйте Hardware Decoding для фоновой музыки. Встроенный декодер в устройствах iOS позволяет сократить использование CPU на проигрывании фоновой музыки, но это может быть сделано только для одного трека одновременно.
- Используйте Force to Mono, если стерео не нужно. Или если в файле оно присутствует, но не различимо. Это в 2 раза уменьшит объем используемого места (как в памяти, так и на диске).
Далее следуют другие шаги по уменьшению размера билда. Большинство настроек делается в Player Settings:
- Установите в настройках проекта Stripping Level = Use micro mscorlib. Это только для владельцев Pro. Не используйте без надобности единицы из System.dll и System.Xml.dll. Они не совместимы с Use micro mscolib.
- Установите API Compatibility Level в .Net 2.0 subset. Но иногда после этого код может не работать, если соответствующие классы/функции и тд не входят в .Net 2.0 subset. Что однажды случилось в моем случае.
- Также следует избавиться от зависимостей от ненужных библиотек.
- Установите Script Call Optimization Level в Fast but no exceptions, чтобы отключить исключения. Это также уменьшит размер билда.
- Установите Target Platform в armv6 (OpenGL ES1.1). Если вам не нужен armv7. Или наоборот, в armv7, но не оба одновременно. Учитывая, что Apple все меньше поддерживает устройства с armv6, имеет смысл оставить лишь armv7.
- Не используйте массивы JS. Лучше не использовать JS вообще, используйте C#. Обычно, после переписывания кода скриптов с JS на C# приложение весит меньше.
Использованные источники вдохновения
В первую очередь использовались документы Unity — самые полезные ресурсы от самих разработчиков Unity. Их читать нужно в первую очередь, желательно по несколько раз, а через некоторое время еще раз, потому что они постоянно обновляются.
Отдельно выношу уже указанные документы по сокращению размера приложения:
Другие источники:
Заключение
Проведенная мною оптимизация позволила существенно повысить производительность игры и играбельность в целом. В качестве дополнительного бонуса был уменьшен размер приложения;) Надеюсь, моя статья поможет Вам сделать ваше приложение на Unity еще лучше.
Источник
www.juds.com.ua
Уменьшение (оптимизация) размера приложения в unity3d. Часть 2
А теперь давайте поговорим об оптимизации текстур и других вещах, помогающих сделать наш билд меньше (вернее упихать в заветные 20 метров). На официальной страничке unity3d описано несколько приемов как сэкономить размер приложения этими способами. Итак, предлагается следующее.Оптимизация используемых текстур включает в себя в первую очередь использование сжатых форматов текстур там где только возможно. Если сжатие не уменьшит занимаемый размер, то можно попробовать уменьшить размер текстуры. Что в принципе логично: зачем вам текстура размеров 2048, если у вас экран 1024х768 и она занимает места не больше, чем экран? В новом unity3d (3.4) настройка текстуры выглядит как изображено выше. Итак, Aniso Level, Filter Mode, Wrap Mode оставляем без изменений. Начнем с Texture Type. Выставляем Advanced и радуемся: здесь можно полностью настроить текстуру как нашей душе угодно (и как позволяет редактор unity3d). Non power of 2 выставляем в ToSmaller или ToLarger. Generate Cubemap в моей 2d-игре не нужна, отключил, впрочем как и Alpha from grayscale тоже не пользуюсь за ненадобностью. Отключите Generate Mip Maps - это также позволит сэкономить на размере текстуры. Normal Map и Light Map отключаем, если не надо. Максимальный размер текстуры выставляем как можно более меньший. Что по поводу сжатия, то тут надо экспериментировать. Обычно наилучшим эффектом обладает сжатие DXT5.Далее приведу таблицу соотношения размеров изображения в зависимости от типа сжатия (взято из того же источника):
Desktop
Сжатие | Занимаемая память |
RGB Compressed DXT1 | 0.5 bpp (bytes/pixel) |
RGBA Compressed DXT5 | 1 bpp |
RGB 16bit | 2 bpp |
RGB 24bit | 3 bpp |
Alpha 8bit | 1 bpp |
RGBA 16bit | 2 bpp |
RGBA 32bit | 4 bpp |
RGB Compressed PVRTC 2 bits | 0.25 bpp (bytes/pixel) |
RGBA Compressed PVRTC 2 bits | 0.25 bpp |
RGB Compressed PVRTC 4 bits | 0.5 bpp |
RGBA Compressed PVRTC 4 bits | 0.5 bpp |
RGB 16bit | 2 bpp |
RGB 24bit | 3 bpp |
Alpha 8bit | 1 bpp |
RGBA 16bit | 2 bpp |
RGBA 32bit | 4 bpp |
Также можно оптимизировать меши и анимации. Сжатие может быть включено в настройках импорта меша (Mesh import settings).
Еще возможно уменьшить количество dll, включенных в ваш проект. Это я рассматривал в прошлой статье по оптимизации (см. оптимизация, часть 1).
Поспешу заметить, что от количества импортированных ассетов размер файла нисколько зависеть не будет, поэтому, даже если у вас в ассетах лежат ненужные текстуры и т.д., то не обязательно их удалять - unity3d просто не включит их в билд. Благодаря постпроцессингу изображений в качестве текстур можно экспортировать и psd-файлы, это размер не изменит, unity3d его пережмет как ему надо.
Спасибо за внимание, если что-то непонятно или недосказано с вопросами и предложениями прошу в комментарии.
getencapsulated.blogspot.com