Топ 17 практик оптимизации игры

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

Чтобы игра не тормозила, нужно её оптимизировать!

Топ 17 практик оптимизации игры, image #1
  1. Профайлинг (Profiling)

Самое важное при тестировании - это измерение показателей. Что именно в игре работает хорошо, а что именно тормозит и почему? Изучите в официальной документации какие вам доступны профайлеры, и как с ними работать.

В движке Unreal Engine самый полезный профайлер - Unreal Insights.

https://docs.unrealengine.com/5.3/en-US/testing-and-optimizing-your-content/

https://docs.unrealengine.com/5.3/en-US/unreal-insights-in-unreal-engine/

  1. Настройка среды под тесты производительности

Во время профайлинга вы будете сравнивать старые и новые результаты измерений.

Чтобы было проще замерять показатели рендера, можно расставить на карте (Map/Level) в нескольких местах Камеры (Camera Actor), и записывать показатели с этих камер. В итоге у вас будут максимально достоверные результаты измерений нагрузки на GPU.

Для замера нагрузки на CPU, имеет смысл изолировать объекты/акторы (Blueprint Actor) на отдельной не-игровой карте. В этом варианте на CPU не будут влиять другие системы игры/другие блюпринт-акторы, и вы получите точные замеры производительности.

В мультиплеер-играх можно записывать повторы (replays) и использовать их для профилирования. С таким подходом не нужно будет лишний раз собирать игроков онлайн для сетевого теста.

  1. Задумывайтесь об оптимизации с первого дня разработки

Если вы задумали, что игра будет работать в 60 фпс на консолях или ПК, то на протяжении всего процесса разработки нужно стремиться к этому показателю. Не обязательно всегда держать частоту кадров на уровне 60 кадров в секунду, но рекомендуется стремиться хотя бы к 55 кадрам на ранних стадиях разработки. Это даст уверенность в том, что игра точно будет хорошо работать, и позволит вам избежать кранчей на финальных стадиях разработки.

  1. «Сколько всего» в игре

В начале разработки игры обычно определяют количество «сколько всего» в игре будет главных героев, персонажей одновременно на экране, интерактивных и других объектов.

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

Если в игре планируется 100 персонажей, то об этом нужно знать и помнить с самого начала разработки. В этом случае нельзя откладывать оптимизацию на потом. Если персонажей в игре тысячи, то разработку игры сразу нужно делать на C++, а не на Блюпринтах.

  1. Scene Components - тяжёлые

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

Очень часто разработчики навешивают на машину и на модульного-персонажа кучу компонентов, а потом удивляются, что производительность упала.

Иногда стоит рассмотреть возможность объединения Акторов, Мешей (Static Mesh) и Скелетал-мешей (Skeletal Mesh) с помощью инструментов объединения. В Unreal Engine для этого есть Merge Tool.

https://docs.unrealengine.com/5.3/en-US/merging-actors-in-unreal-engine/

  1. Избегайте в Блюпринтах функции Tick

Функция Tick в Блюпринтах значительно медленнее, чем в C++. Например, 100 пустых Акторов, в которых ничего не происходит, просто так тратят 1 миллисекунду процессорного времени. Если нужен Актор с функцией Tick, то желательно чтобы класс этого Актора был написан на C++. В Блюпринтах можно пользоваться Таймерами или Таймлайнами (Timeline) для временных эвентов (Time Events).

  1. Включайте/отключайте Tick во время игры

Во время игры нам не всегда нужны «тикающие» Акторы. Убедитесь что Акторы и их Компоненты «тикают» только тогда, когда это действительно нужно. Пользуйтесь функцией SetActorTickEnabled.

  1. Снижайте Tick Rate

Большинству Акторов в игре ну нужно «тикать» раз 60 в секунду. Многие из них прекрасно работают и с более низким Tick Rate. Этот параметр можно менять прямо во время игры. Хорошей практикой будет снизить Tick Rate до 2 раз в секунду, пока объект не «активируется», тогда можно увеличить Tick Rate до максимума.

Например, Актор - Дверь или Лифт. Пока этот Актор находится в состоянии покоя, у него Tick Rate = 2. Когда мы подошли к этому Актору, пересекли его коллизию, то у него Tick Rate = 0, что значит Актор «тикает» на полной скорости.

  1. LOD для логики

Если вы работаете над ролевой игрой, то хорошая практика - иметь значительно упрощенного Актера для ваших NPC. Когда игрок приближается, он заменяется на правильного актера NPC с хорошей анимацией и более сложной логикой. Также рассмотрите возможность отключения физики движущихся объектов, которые находятся слишком далеко, чтобы игрок мог на них повлиять.

  1. «Не работай, если не смотрят»

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

Топ 17 практик оптимизации игры, image #2
  1. Упрощение репликации

Сеть движка Unreal собирает объекты, находящиеся рядом с игроком, а затем проверяет их на наличие измененных свойств. Стоимость проверки измененных свойств во многом зависит от количества Акторов/Компонентов, а не от количества реплицируемых свойств. Лучше иметь PlayerCharacter со 100 реплицируемыми свойствами вместо PlayerCharacter + 9 компонентов по 10 реплицируемых свойств каждый. Это противоречит хорошей архитектуре, но нет хорошего способа обойти это.

https://cedric-neukirchen.net/docs/category/multiplayer-network-compendium/

  1. Пул Акторов

В Unreal Engine создание (Spawn) Акторов очень медленный. Если для снарядов/пуль и взрывов постоянно Спавнить Акторы, то это создаст слишком большую нагрузку на Сборщик Мусора (Garbage Collector, GC).

Сборка мусора в Unreal Engine: https://habr.com/ru/articles/661469/

  1. Аккуратнее с анимацией

Анимация в Unreal Engine - это известный Боттлнэк (узкое место, Bottleneck). Когда смешиваете анимации в АнимБП (AnimBP, Animation Blueprint), следите чтобы использовался быстрый путь (маленький значок молнии). Предпочитайте использовать State Machine вместо смешивания анимации через Blend.

Топ 17 практик оптимизации игры, image #3

https://docs.unrealengine.com/5.3/en-US/animation-blueprints-in-unreal-engine/

  1. Избегайте Overlap-events для движущихся объектов

Каждый компонент у которого включены Оверлап-события (Overlap-events, одна коллизия перекрывает другую коллизию), будет проверять физические перекрытия каждый раз при движении. Такие события могут срабатывать несколько раз за время 1 Draw Call.

Основное использование Оверлап-событий — для триггеров, которые определяют персонажа игрока.

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

  1. Минимизация количества трансформаций

Каждый раз, когда вы вызываете SetLocation или SetRotation, Unreal Engine обновляет всю цепочку преобразований, и если есть физические объекты, он обновляет физику. Необходимо стремиться к строго ОДНОМУ вызову SetLocation или связанным с ним вызовам на каждый кадр движущихся объектов.

Движок Unreal имеет вызовы SetTransform и SetActorLocationAndRotation, которые позволяют изменять вращение и местоположение за один вызов вместо двух. Вызов SetActorLocationAndRotation в два раза быстрее, чем вызов SetLocation и SetRotation по отдельности.

  1. Объединение логики Акторов в Менеджеры

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

Если у вас будет большое количество чего-либо, вероятно, будет хорошей идеей объединить их с помощью какого-нибудь «менеджера», который будет обновлять все объекты. Отличные кандидаты для группировки в Менеджер: снаряды, показатели урона или некоторые части логики ИИ. Это хорошо сочетается с идеей «LOD для логики».

  1. Граф репликации в Мультиплеере

Граф репликации (Replication Graph) сделали для оптимизации производительности сервера в Fortnite. Он позволяет вам переопределить логику «какие объекты имеют отношение к этому игроку». Благодаря этому, можно динамически изменять скорость обновления сети для каждой зоны на карте. И определить какие объекты находятся в диапазоне репликации.

В ShooterGame есть его полная реализация. В проекте можно найти интересные вещи, например, граф репликации ограничивает частоту обновления PlayerInfo. При каждом обновлении отправляются только данные двух игроков.

https://docs.unrealengine.com/4.27/en-US/Resources/SampleGames/ShooterGame/

При правильном использовании графа репликации вы сможете сделать игру на 200 или более игроков в многопользовательской игре.

https://docs.unrealengine.com/5.3/en-US/replication-graph-in-unreal-engine/

---
Оригинальная статья: https://dev.epicgames.com/community/learning/tutorials/3o6/expert-s-guide-to-unreal-engine-performance

1078 views·137 shares