12/12/2019

Как я портировал свою игру на Nintendo Switch

Как я портировал свою игру на Nintendo Switch

Когда-то я мог только мечтать о релизе игры на консоли. Даже Steam казался далекой целью — я работал в индустрии звукозаписи, но новость, что мощный игровой движок Unreal Engine 4 стал условно бесплатным и игры может делать любой желающий, заставила меня стряхнуть пыль с незаконченного высшего режиссерского образования и приняться за разработку кинематографичного хоррора о девочке, болеющей астмой.

Спустя 4 года работы моя игра Never Again вышла в Steam. Это был май 2019, а уже 30 января 2020 года состоится ее релиз на Nintendo Switch. Учитывая, что я никогда не планировал релиз на этой консоли, портирование принесло немало трудностей.

Скриншот из порта игры Never Again для Nintendo Switch

Программистом меня назвать очень сложно; довести игру до релиза на ПК я смог используя только визуальный скриптинг Blueprints, что с точки зрения оптимизации считается не очень хорошей идеей. 

Меня завораживала мысль, что я сделал игру на движке, которым пользуются огромные игровые студии, создающие ААА игры. Конечно же, я не испытывал угрызений совести от того, что старался пользоваться всеми возможностями UE4 по максимуму, часто не понимая, как это отразится на производительности. Совсем беспредельничать мне мешал маломощный компьютер с GT740, без которого я бы наворотил таких тяжеловесных эффектов, что ни о каком порте на Nintendo Switch не шло бы и речи. 

Целью было добиться на гибриде стабильных 30 fps. Чтобы оценить, насколько трудной станет эта задача, я запустил Never Again на девките вообще без какого-либо вмешательства в структуру игры. Пусть она поработает, словно бы это ПК. 

Как же это было долго...

Если не брать в расчет две бесконечности: сборку движка и компиляцию шейдеров под весь проект, запустить игру на консоли удалось с первого раза. Запущенное приложение встретило меня изумительными… 0 fps. По ощущениям картинка менялась раз в час, а по факту на CPU было 6000мс. Оптмизация предстала передо мной в виде хитрого ребуса, разгадать который оказалось той еще задачей. Но по истечению двух недель, которые я занимался портированием 24/7, этот первый этап перестал казаться чем-то сокрушительным и неразрешимым.

Если вы столкнетесь с такой же проблемой, вот первый совет:

Важный совет №1. Полностью отключаем симуляцию физики одежды Apex Cloth.

Чтобы это понять и запрофайлить игру на Switch, пришлось накрутить такие костыли, что об этом мне не позволяет рассказывать NDA. В целом, это наконец заставило меня глубже разобраться с профайлингом в Unreal Engine 4.

Прощай, красивое платьишко. 

Ура! Never Again полноценно запускается на Switch, и можно даже пройти игру, но все еще с большим трудом — вилка fps была от 10 до 15. Идей, что с этим делать, у меня было не так уж и много…

Я отключил тяжелые постпроцессы (Depth of field, Motion Blur (не понимаю, кому вообще нравится этот эффект ;)), Ambient Occlusion и т.д. За последний я очень боролся и не хотел выключать, т.к. картинка очень сильно менялась не в лучшую сторону. Но ради оптимизации придется потерпеть.

Затем пришло время сказать "до свидания" и сглаживанию. Отключил. Но его отсутствие я решил немного компенсировать понижением Resolution Scale в ручном режиме до 66.66%, а в консольном — до 80%.

Далее я настраивал разрешение экрана для консольного режима, установив 1920х1080 (меньше — не круто), а для портативного режима ограничился 1280х720. С разрешением у меня возник казус, полностью перевернувший мое представление о портировании. Но об этом — в конце.

Данные настройки помогли поднять fps до вилки от 18 до 24, что, в каком-то смысле, даже приемлемо для прохождения, но все еще не круто. Перфекционист внутри меня бил кулаком по столу и требовал стабильности.

Настало время убивать графику

Первое, что пришло в голову — отключить траву. Начальная локация в Never Again — освещенная детская комната, через окно которой видно улицу. А там, в свою очередь, уже куча источников света, порождающих огромное количество теней. Я не мог их запечь в силу обстоятельств, о которых можно написать целую отдельную статью. Освещение должно было оставаться стационарное (полу динамическое). Больше нигде в игре травы нет, так что жалеть ее я не стал.

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

На скриншотах можно заметить, что последняя картинка какая-то зернистая. Нет, это не низкое разрешение скриншота, это Dithering (Дитеринг).

Важный совет №2. Забываем о режиме Translucent. Прозрачные материалы делаем с помощью режима Masked + Dither.

Зачем это нужно? Видеокарте очень тяжело рендерить прозрачные материалы — нужно максимально облегчить ей эту задачу. Тут два варианта: либо вообще убрать прозрачность, либо зафейкать ее с помощью дитеринга. Результат получается не идеальный, но вполне приемлемый:

В консольном режиме такое стекло выглядит на удивление хорошо.

Это, конечно, сильно разгружает GPU, но сказать, что вызывает бурный скачок fps нельзя. Тем не менее, это еще один маленький камешек в фундамент оптимизации.

Далее я начал профайлить GPU и увидел, что львиную долю мс занимает освещение. А если конкретно — тени. Это, конечно, очевидно, а еще более очевидно, что нужно с этим что-то делать. Я полез смотреть на то, как реализованы тени в других 3D играх на Switch. Наиболее привлекательной мне тогда казалась графика у Outlast. Интересно, как они справились с тенями?

Очень качественный порт

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

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

Я нигде не настраивал Cull Distance, т.к. знал, что движок хорошо справляется с этим сам: все, что находилось вне кадра, не рендерилось. Моя игра коридорна, все действие разворачивается в замкнутых пространствах, и у меня не было никакой необходимости настраивать дальность отрисовки. 

Но когда важна каждая миллисекунда, пришлось настраивать дистанцию отрисовки всем объектам, на что ушло несколько дней. Не сказать, что прирост производительности был колоссальным, но на этом этапе fps уже встал с колен и уверенно приближался к значению 25. Пришло время разбираться с CPU.

Первым делом под руку попались физические объекты. 

В Never Again много физики, что для Nintendo Switch — не очень хорошая новость, т.к. любая физика — серьезная нагрузка на процессор. В целом, физические объекты были достаточно оптимизированы еще для ПК: когда они валялись без дела, то погружались в сон и тем самым переставали нагружать процессор. К таким объектам можно отнести куски от кукол в пещере, которыми я завалил весь пол. Число таких объектов пришлось сократить, чтобы уменьшить количество их столкновений друг с другом.

Наверное, я — больной ублюдок, раз решил вообще в эту игру физику вставить...

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

Важный совет №3. Используйте физику в играх разумно, а лучше — вообще от нее откажитесь.

Вот мы и дошли до логики.

Я начинал разрабатывать игру с очень низкими навыками программирования. Я на ходу изучал возможности Blueprints, и это дало о себе знать при портировании.

За 4 года разработки накопилось очень много фундаментальных ошибок, исправление которых, особенно в квестах и катсценах, могло сделать игру непроходимой. Пару лет Never Again была в Раннем Доступе, и все это время я упорно возводил шаткую башню из костылей. И если в Steam при обнаружении пользователем бага я мог моментально перезалить билд и все исправить, то со Switch такое бы не прокатывает: каждое обновление тщательно проверяется, и это занимает немало времени. Поэтому переписывать код было опасно. Проще отрезать функционал, который не влиял на геймплей.

Первое, на что нужно обратить внимание — как много у вас процессов на EventTick, нет ли нагромождения логики, которая беспрерывно выполняется каждый кадр. В целом, я за этим следил, но все же такое место в игре у меня было… 

Каждый кадр срабатывали три LineTrace, которые определяли координату под каждой ногой и туловищем персонажа, чтобы красиво сгибать ноги в зависимости от рельефа поверхности, на которой стоит главная героиня (Inverse Kinematics). В связи с этим, следующий важный совет:

Важный совет №4. Как можно меньше используем LineTrace с большим количеством логики, тикающий каждый кадр.

Стоило мне убрать IK, как сразу высвободилось 7 мс с CPU, и игра стала изредка выдавать желанные 30 fps

На этом моменте я застрял, ведь больше откручивать было нечего… Все, что осталось — попробовать включить Forward Shading.

Что это значит? Помните, в начале статьи я говорил, что боролся за близкий моему сердцу пост процесс Ambient Occlusion? Дело в том, что Forward Rendering был придуман для VR игр. Он отключает некоторые тяжелые визуальные эффекты и тем самым в разы уменьшает нагрузку как на GPU, так и на CPU. Ambient Occlusion, соответственно, тоже был отключен.

Сразу бросилось в глаза, что вместе с Ambient Occlusion перестали отображаться декали. Я заменил декали на плейны с текстурой, и это было нетрудно. По Ambient Occlusion я горевал до тех пор, пока первый раз не зашоукейсил игру на Nintendo Switch. Игроки ничего странного в визуале не увидели и в целом охарактеризовали порт как "красивый".

Помните, я упомянул про казус с разрешением экрана? Я долго не мог понять, почему в проекте после перехода на Forward Rendering fps поднялся до небес, а в консоли все также оставалось от 25 до 30 fps. Проблема оказалась в Device Profiles: я неправильно указал смену разрешений для ручного режима и консольного. После исправления я почувствовал великое облегчение, статистика показала эпичные 55 fps в меню, вместо стабильных 30, за которые я боролся две недели.

Да, записал гифку с видоса, что такого?

Тут можно было бы и закончить, но есть еще пара моментов, на которые нужно обратить внимание.

Важный совет №5. Уменьшаем размер текстур.

Крупные текстуры — распространенная ошибка, которой мне удалось избежать по причине особенности стиля игры и слабого компьютера. Но, тем не менее, многих эта ошибка может коснуться. Я поставил себе ограничение, что размер текстур не должен превышать 1024х1024, а чаще всего использовал вообще 512х512. Помните: размер памяти у Nintendo Switch — не безграничный.

Важный совет №6. Пользуйтесь Blueprint Interfaces.

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

В целом, это все. Можно было бы еще рассказать про прохождение всех проверок билда компанией Nintendo, но это совсем другая история, и прячется она под NDA. Все обладатели Switch смогут увидеть плод моей боли, пота и слез в Nintendo eShop уже 30 января 2020.


Текст: Валентин Щекин

Недавние статьи

WN Conference Abu Dhabi’24: откройте бизнес-возможности в ОАЭ

07/02/2024
Уже 15-16 февраля более 800 представителей игровой индустрии встретятся для двух дней активного нетворкинга и обучения.

WN Conference Belgrade’23 уже скоро!

25/11/2023
7-8 декабря более 800 представителей местной и международной индустрии встретятся для двух дней активного нетворкинга.

7-8 июня WN Conference возвращается в Турцию

23/05/2023
Присоединяйтесь оффлайн или онлайн к бизнес-конференции для представителей игровой индустрии WN Istanbul’23.