Как стать автором
Обновить

Оптимизация GameThread в Unreal Engine 4 ч.1

Время на прочтение5 мин
Количество просмотров7.4K

Ссылка на часть 2

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

Significance Manager

Основная идея решения в том, что бы каждого игрока разделить в свой Significance Bucket по важности, основанной на дистанции, размерах, видимости и другой информации. У каждого такого сегмента есть максимальный размер и приоритет. Significance Manager может проводить расчет для LOD игрового мира. К примеру, приоритет игрока который не попадает в кадр в разы ниже, чем у игрока который находится рядом в зоне видимости.

Кроме того, Significance Manager можно использовать в качестве базовой системы для управления другими Scalability системами. Например, "Fortnite" - многоплатформенная игра, реализация которой под разные платформы очень различается. Мы можем установить разные параметры Bucket для разных платформ и даже разных моделей. Например, для консолей MaxSize каждого из наших сегментов составляет 5, 10, 10, 75, для мобильного телефона - 1, 5, 15, 79 и т. д. соответственно. Он также поддерживает настройку параметров для определенных моделей мобильных телефонов.

Анимации

В "Fortnite" персонаж состоит из 5 основных частей: Базового Скелета, головы, тела, рюкзака и оружия. "Базовый Скелет" это пустой скелет без какой либо информации о внешности. Каждая часть анимации имеет свой собственный AnimBP и все они выполняют свои процессы после CopyPose с "Базового Скелета", например, симуляция физики. Это решение очень гибкое и удобное, но несет большие трудности с точки зрения эффективности обработки. Так как у нас 100 игроков, что означает что около 500 SkeletalMesh, которые необходимо постоянно обрабатывать в GameThread. Мы конечно можем применить LOD к мешу, не сильно, но он уменьшит количество костей, но эти расчеты костей проводятся в WorkerThread.

Есть несколько альтернатив данному решению, например Mesh merge, который использовался в "Unreal Tournament", но оно несет свои проблемы. Во-первых, увеличивается количество используемой памяти, так как каждый игрок имеет свою уникальную сетку. Во-вторых, этот подход теряет какую либо гибкость. Ведь каждая часть может иметь свою собственную уникальную логику и если материалы объединить, уникальность анимации может исчезнуть.

Конечно, существуют и другие решения данной проблемы. Например, подвески не требующие анимации, можно просто прикрепить к Sockets. Есть еще вариант Master-Slave, но этот метод не имеет масштабируемости. Например, Master Skeleton без хвоста или плаща и независимая анимация или физическая симуляция хвоста или плаща невозможны.

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

(Подробнее можно ознакомится с методами в оффициальной окументации)

Процесс обновления анимации в Unreal Engine разделен на 3 этапа. Сперва выполняется операция обновление в GameThread. Обновление используется для расчета некоторых значений, таких как веса например. Следующим шагом идет тяжелый процесс по обработке Eval, такие как анимация, декомпрессия, смешивание(blend) и т.д. И завершает этот процесс запуск Notify и отправку данных в RenderThread.

Цитата Wang Mi

В одном кадре очень много персонажей, которые выполняют просчет скелетных анимаций. Все мы знаем что, сейчас мобильные устройства многоядерные. Чтобы лучше использовать их мощностя нам необходимо лучше обрабатывать запросы BPVM(Blueprint virtual machine) и разделение на потоки. На основе двух вышеуказанных способов оптимизации, мы не используем Event Graph, а делаем игровую логику как часть AnimInstanceProxy, тем самым движок сможет автоматически определять, сможет ли Event Graph обновлятся в других потоках. Если вы используете Fast Path, мы можем поместить обновление скелета в WorkingThread. Например есть 50 персонажей. В начале любого обновления персонажа, вычисления разделяются на другие потоки, а основной поток продолжает свою работу.

Если в AnimBP, есть хоть одна нода (not Fast Path), которой необходимо пройти BPVM, то вся система не сможет быть отправлена в обработку в асинхронном потоке. Это связано с тем, что безопасность потоков не может быть гарантирована для произвольных обращений узла ВР к интерфейсу и самому ВРVM.

Если же Event Graph реализован собственным Proxy в собственном наследуемом классе AnimInstance, и все его свойства в AnimInstance также реализованы в собственном наследуемом классе, то весь процесс обновления не нужно делать с помощью BPVM, поэтому он также будет автоматически помещен в AnyThread для обработки.

Перенос Event Graph to C++

Blueprints замечательная технология, но у нее есть свои проблемы и недостатки. Один из таких AnimBP - Event graph. Если он содержит много логических вычислений и сам по себе сложен, расходы ресурсов могут быть особенно большими. Перенеся вычисления из ВР в С++, может позволить сэкономить много процессорного времени.

Fast Path

Некоторые математические вычисления часто используются в AnimGraph, поскольку выполнение в ВР часто сопровождаются лишними вызовами виртуальной машины. Некоторые расчеты можно перенести в AlphaCurveScaleBiasClamp в ноде, тем самым перенеся вычисления в С++, что само по себе снижает расходы на вызовы BPVM. Отличительной чертой Fast Path есть небольшой значок молнии, и необходимо старатся превращать эти ноды в Fast Path как можно больше в процессе разработки.

Отключать обновление анимации как можно чаще

У Skeletal Mesh есть одна очень важная настройка, MeshComponentUpdateFlag, которую можно установить в OnlyTickPoseWhenRendered, тем самым, когда персонаж не будет попадать на экран, любая обработка анимации будут пропускаться(например, если у вас звуки шагов привязаны к anim notify, за вашей спиной они не будут слышны). Это одна из тех вещей, которые могут быть обработаны при помощи Significance. Когда игроки слишком далеки от вас, отпадает необходимость обрабатывать анимацию оружия, в некоторых случаях даже игрока.

Большинство падающих объектов имитируют физику и являются Skeletal Meshes. Одна из проблем расчета для таких мешей это динамический путь. Статические объекты в UE, будут непосредственно сортироваться и группироваться при добавлении на сцену, что значительно сокращает потребление ресурсов при отрисовке сцены. Динамические же объекты получаются на этапе initViews каждого кадра в начале рендера. Этот процесс весьма отличается от статики, он не попадает в статическую таблицу отрисовки и тем самым снижает эффективность процесса. Такие скелетные объекты, данные которых не нуждаются в изменении в каждом кадре, добавляются в StaticRenderPath, тем самым ускоряя рендеринг

URO (Update Rate Optimization)

У нас, нет необходимости делать расчеты костей каждый кадр для каждого игрока. Вы можете настроить разную частоту обновление на основе разной важности данных(Significance Manager), и даже забрать у некоторых персонажей интерполяцию. Ведь если персонаж занимает очень маленькую часть экрана, его можно считать невидимым.

Дополнительно с настройками Skeletal LOD, мы можем установить Skeletal Control Code для управление анимации в AnimGraph, чтобы не рассчитывать на определенных LOD, тяжелых процессов, таких как ИК, физика и т.д.

RigidBody

Использование RigidBody, как замена AnimDynamics, приносит в проект неоспоримое улучшение как производительности так и гибкости настроек.

Calculation of Bounds

Нельзя просто взять и применить Use Fixed Bounds. Аниматоры создают классные анимации, и после применения Fixed, начинают происходить странные вещи(например, персонажи пропадают). Bounds Расчитываются каждый кадр используя Collison Shapes. Мы можем использовать Bound родителя, для того что бы не расчитывать его 5 раз(рюкзак, парашут, оружие получают Bound родителя). (Подробнее как происходит расчет Bound вы можете узнать из USkinnedMeshComponent::CalcMeshBound).

Другие способы оптимизации анимаций

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

Плагин для обмена анимацией. Принцип заключается в обновлении Eval костей как можно чаще. Другие анимации используются копирыванием и добавление суперпозиции, что по-прежнему оптимизируется процессором.

Niagara simulates a large group , подходит для моделирования сцены массового побоища огромного количества людей и относится к GPU-симулируемым анимациям.

Итог

В этой части мы рассмотрели Significance Manager и оптимизацию через анимации. Продолжение статьи можно прочитать по ссылке.

Теги:
Хабы:
Всего голосов 1: ↑1 и ↓0+1
Комментарии0

Публикации

Истории

Работа

Ближайшие события

7 – 8 ноября
Конференция byteoilgas_conf 2024
МоскваОнлайн
7 – 8 ноября
Конференция «Матемаркетинг»
МоскваОнлайн
15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань