Как теги помогли заметно повысить фпс в игре на Unreal Engine
Наперёд результат: ещё вчера был фпс 25-35, сегодня 60-100 в зависимости от.
Очевидная банальность: всевозможной оптимизацией надо заниматься изначально и постоянно, вне зависимости от движка. Brightest Star - на UE, так что все мои пляски в нём.
В роли игрока фпс меня никогда не интересовал, даже 30 устраивает. Но всё же это гарантированные лопаты с говном от большинства других игроков. Так что в роли разработчика моим исходным ориентиром (условным) была уже другая планка: чтобы всё стабильно летало на 60 фпс без особых просадок.
Поэтому когда обширный игровой уровень обрастал очередной необходимой массой деталек и красот, а фпс синхронно тому падал, набухала мысль о некотором радикальном скачке в довесок к рутинной оптимизации.
Набухло по целому ряду причин. Две главные: надоело видеть 30-35 фпс и пора, наконец, отлипнуть от орбитальной станции, а заодно положить её в основу демки.
Тут, конечно, 95% интернет-советов-обучений напомнят о работе со светом, текстурами, лодами и т.п. Это совершенно правильные советы в целом и общем, применение которых (ну и подтягивание хвостов) подняло мои фпс до 45. А вот с индивидуальными особенностями своей игры надо разбираться самому.
Первое и главное - это узнать врага. Сделать это можно через встроенную в редактор статистику, аналитику, инструменты типа Unreal Insights.
И можно опытным путём - удалять куски уровня и смотреть что вышло. То и другое по-своему полезно и занятно, в это надо забуриваться на целый день как в детектив.
В моём случае проще всего оказалось докопаться к полусотне NPC, которые разбросаны по станции. Некоторые из них нужны и важны для геймплея. Большинство же - массовка для живой и естественной атмосферы (а это тоже очень важно). И всех их хочется добавить ещё больше, но явно не ценой производительности. Как тогда быть?
Сперва я хотел оптимизировать логику их движений и перемещений, критично оценить behavior tree и блюпринты, да и потыкаться в технические характеристики самих моделей. Явных косяков не увидел, и тогда на горизонте замаячил плохой путь: по крупицам ужиматься, принимать "сложные решения" и в конце концов хоть немного, но делать игру хуже.
А я ведь хотел вундервафлю, радикальное улучшение оптимизации.
Зашёл с другой логичной стороны: что не видим, то скрываем. Сказать просто, сделать тоже, но как именно сделать - много разных вариантов. Мне хотелось сделать это разом и массово, по триггеру, а не вешать что-то на тики с затратными проверками каждое мгновение.
Под такое условие не придумал ничего лучше и проще, как поставить невидимую коллизию на лестницу и действовать по событию выхода из неё.
Технически очень легко проверить поднялись мы или спустились. У персонажа есть координаты в пространстве, в момент выхода (!) из коллизии проверяем значение по вертикали. В моём случае если z>750 - поднялись, скрываем всех NPC с z<500, если z<750 - спустились, скрываем всех NPC с z>1000. Если кто-то шёл рядом со мной в пределах z от 500 до 1000, тот остаётся видимым.
И тут вопрос, как определять всех этих NPC, которых надо вырубить. Они же сильно разные, некоторые друг другу даже не родственные. Всех перебирать не очень удобно. А если кого-то одного нельзя вырубать ни при каких обстоятельствах?
На глаза попался Get All Actors with Tag, и я такой: хммм. Добавить тег сразу всем моделям из списка - буквально три клика и 5 секунд. И можно индивидуально порешать, кому добавлять тег, а кому нет.
И всех, кого надо скрыть, далее можно найти по этому тегу вот так просто:
Дальше - больше. Зачем дискриминировать NPC, если можно вырубать всех и вся. Мы всё ещё хотим радикальных решений. Оказалось, что тег точно так же можно навесить всем подряд объектам: стенам, столам, источникам света!
Получается, что невидимый тебе этаж реально пропадает и не создает лишнюю нагрузку. При этом я убрал лишь видимость объектов, а не уничтожал их. И фпс взлетел.
Единственная заминка случилась с золотыми отражающими сотами на полу и потолке.
Это декали, а у декалей нет своих тегов. Но у них есть тег компонента, куда можно прописать любое наше значение:
Решилось так же просто:
Итак, есть большой уровень с тысячами элементов архитектуры, декора, фурнитуры, техники, вилок и бананов. И ещё бродят полсотни NPC. Есть два базовых этажа, содержание которых на 95% не видно со стороны.
Этим 95% мы вешаем тег. Если разложить объекты по папочкам, то выделять всё нужное сможем за пару секунд. А по триггеру в заданном месте проверяем все объекты с нашим тегом: включаем или выключаем, смотря на каком он этаже (по координате z).
Этаж - простой понятный случай. Но то же самое можно делать в любых других условиях. Вместо триггера на лестнице и вертикальной координаты можно расчертить зоны на плоскости, проверять их и по тегам убирать с карты лишнее. А можно вообще ничего не проверять, а безусловно контролировать объекты с тегами по событию. Всё зависит от архитектурной планировки уровня.
Если удачно/правильно разместили коллизию, то визуально игрок вообще не заметит никакой разницы при сходе с лестницы, а фпс при этом гораздо выше. Т.е. игра функционирует гораздо лучше совершенно без потери механик или качества картинки.
Конечно, сами по себе теги на объектах никак не помогли повысить фпс, но с ними это получилось так быстро и удобно, что сейчас я дольше пишу статью об этом. И это восхитительно, что получилось добиться подобного результата минимумом трудозатрат.
И да, я ни разу не специалист в Unreal Engine. Я мог открыть велосипед или сделать что-то из серии "ну это же было очевидно". С удовольствием почитаю про иные решения подобных ситуаций и приму советов по оптимизации, их никогда не бывает мало. И само собой это не завершённая история по оптимизации, а только её начало. Игра в разработке и ещё навалит новых задачек. А пока с текущим промежуточным результатом в 60-100 фпс стало просто легче на душе.