Пару лет назад нашей команде выпала честь заняться созданием мобильной «Арматы». Придерживаясь правила «делаем игру, а не технологию», прототип мы создавали на том, что уже есть в движке. Это был UE 4.9, в основе физической модели — PhysX Vehicles, и много боли (как по поводу, так и без).
В дальнейшем наша команда создала open source-плагин PsRealVehicle, доступный по MIT лицензии. Этот плагин заточен под физику танков и колесных машинок для высоконагруженных сетевых шутеров, и его работу вы в любой момент можете наблюдать в нашем проекте Armored Warfare: Assault.
Работу над проектом мы начали на Unreal Engine 4.9. В то время «из коробки» физика машин поддерживала только четырехколесные машинки, а для шестиколесных «лавок» (LAV-400, наша первая «боевая» машина-прототип) пришлось сразу же использовать кастомную сборку движка. Со встроенной физикой танков было ещё хуже: данные о том, как всё работает и настраивается, приходилось по крупицам выуживать из кода.
Следуя правилу «прототипируй», мы сформировали для себя следующие требования:
Физика должна симулировать движение танка (программа-минимум) и колесных машин (весьма желательно — это фишка серии игр AW).
Выделенный сервер должен выдерживать до 20 машинок на онлайн-сессию, а клиент должен их обрабатывать.
Все колеса и гусеницы должны следовать неровностям поверхности, подвеска должна работать, танк — качаться.
Настройка физики должна определяться реальными величинами (масса машинки, мощность двигателя) и вести к реалистичному поведению (возможность преодолевать те или иные виды ландшафта).
Чем меньше мы пишем технического кода, тем лучше: движок должны делать Epic Games, а не мы.
Итак, требования приблизительно понятны, приступили к делу. Достаточно быстро мы собрали первый прототип, танчики ездили, машинки колесили, но было у нас две беды:
Всё это весьма бодро жрало процессор.
Создаваемая картина мира никак не хотела укладываться в рамки реалистичного поведения тяжелой техники. Тридцатитонный танк ни при каких настройках не мог взять 15-градусный подъем (а это один из самых легких вариантов). Мы потратили немало времени, возясь с настройками симуляции и трения ландшафта, но это приводило либо к задиранию мощности до космических значений (в 20+ раз больше реальных величин, и в результате машины вели себя нестабильно и непрогнозируемо), либо к игрушечным массам техники (PhysX отлично работает при массе ТС в районе полутора тонн).
Геймдизайнеры от такого были далеко не в восторге. Программисты плакали, кололись, но продолжали есть кактус (партия требует рабочего решения!). Прототип был одобрен руководством, и отправлен на создание альфа-версии (к слову, видео из прототипа есть на Youtube. Но время шло, а надежды на светлое будущее такой физики становились всё меньше. Настроек было недостаточно, их поведение выглядело чёрной магией. А позиция «игра, а не технологии» по-своему связывала руки. Хотя бы сомнениями в том, а потянем ли.
Тихонько вооружившись Википедией, остаточными школьными знаниями и опытом работы над физикой «на понтонах» а-ля Assassin's Creed, за пару дней я создал прототип новой физики танка, который и лег в основу плагина PsRealVehicle. В течение недели proof-of-concept был поставлен на ноги, показан CTO команды и защищён нагрузочным тестированием. Цифры говорили: своей физике — быть.
...-..., и в продакшн
Путь от прототипа до прода вышел куда длиннее. Если на концептуальную проверку хватило недели, то на адекватную бета-версию ушло уже месяца полтора. Создание полноценного «продовского» релиза заняло около полугода, постоянная доводка и коррекция — на протяжении всего времени работы над проектом. Во многом такой высокой скоростью разработки и внедрения физики в проект мы обязаны пришедшему как раз в то время в нашу команду техническому геймдизайнеру Игорю, чья дотошность в аспектах работы математической модели и ее субъективного восприятия игроками и привела к текущему результату. Должен признать, в технологическом смысле я — варвар: мое дело сделать топор, чтобы валить лес. После обработки Игорем и доводки модели другими ребятами мы получили production-ready открытое решение, масштабируемое и в высшей мере оптимизированное для нужд мультиплеера. Здесь есть чем гордиться: из множества доступных на рынке плагинов (включая встроенный PhysX Vehicles) наш самый быстрый и прозрачный в настройке.
Кстати, не обошлось и без смешных случаев. Пока мы работали с PhysX Vehicle и наши предельно скользкие многоколесные машинки не могли взобраться на мелкие холмы, я не раз слышал упреки, мол, не может наша команда настроить, чтобы как у людей было. Решение об использовании своего плагина было принято, в том числе, под влиянием этого кадра:
Новейшая разработка советской армии! Танк-паук, который способен подниматься на 89-градусные стены. Такое крыть было просто нечем :D
Особенности нашего решения
Прежде чем я перейду к описанию настройки танков и машин в PsRealVehicle на примере нашего AW: Assault, хочу сказать еще о паре вещей, которые легли в основу используемой физической модели.
Во-первых, мы четко придерживаемся того, что мы делаем не симулятор физики танка, а игру, в достаточной мере аркадную. Как ни печально, но мало кому нужен настоящий в своем поведении танк — это круто только на презентационных видео-роликах, а не в управлении, и уж тем более шутере. Игроку нужен такой танк, который ведет себя как танк в том понимании, которое уже создали другие блокбастеры. И чтобы работало на обычном девайсе, а не Titan'е каком-нибудь.
Первый пункт имеет следствие: некоторые вещи в игре работают весьма фейково. Если что-то выглядит как танк и ведет себя как танк — то это танк, и не важно, что внутри он немного фрегат, или что часть физики настроена условной магией трения. Одним из таких условностей является регулирование поворота машинки путем изменения угловой скорости, а не силами, приложенными к колесам и подвеске. Условно, танк поворачивает на X градусов в секунду, потому что мы так решили исходя из геймплейных соображений, а не потому, что у него гусеницы вращаются с такой-то скоростью.
Кстати, это не отменяет того факта, что «под капотом» можно включить «настоящую» физику поворота, именно она использовалась в первых прототипах. По-хорошему, к ней стоит прикрутить работу угловой подвески, подлом колеса и так далее. Если мы начнем делать гоночные симуляторы, обязательно к этому вернемся, а пока это один из пунктов в списке вероятных доработок.
Структура танка в AW: Assault
Иерархия классов
AAwmVehicle — главный C++ класс, отвечающий за машинку в игре, разбитый на множество компонентов (движение — UPsRealVehicleMovementComponent, звуки, VFX, статистика, броня и другие). От него наследуется блюпринт BP_DefaultVehicle, который является дополнительной прослойкой для настроек по-умолчанию для всех машин. Все прочие — частные блюпринты настроек для каждой единицы техники в игре.
Каждая машина представляет из себя набор таких данных:
Блюпринт, где прописаны все внешние ассеты, заданы звуки, свойства а-ля «колесная/гусеничная техника», и настроена физика.
Скелетал меш и привязанные к нему ассеты:
— Физический ассет (здесь никакой магии, всё стандартно).
— Анимационное дерево.
Текстуры (diffuse, normal map, RMM).
Материал инстансы (корпус, гусеницы + разрушенное состояние).
Набор мешей брони для системы пробитий.
Камеры, чекеры уровня воды и прочие вспомогательные коллизии.
Анимация танка
Настроить один танк — мелочь, каким бы сложным ни было анимационное дерево. Настроить десятки таких танков и поддерживать их в актуальном состоянии, при изменении костей, меша и так далее — совершенно неадекватный объем для ручной работы. К счастью, в нашем случае речь идёт о достаточно стандартизированном «персонаже»: танк можно препарировать на сущности и называть их шаблонно. Речь, конечно же, об именовании костей.
Такой подход позволил использовать по сути одно и то же анимационное дерево, которое святыми Ctrl+C / Ctrl+V множится на любое число танков и не требует никакой поддержки, кроме изначального QA-check'а.
Вся магия происходит внутри кастомных сипипи-нод. Это позволило не только стандартизировать дерево, но и сильно сократить количество вычислений на расчёте анимаций (стандартные ноды очень любят гонять системы координат туда-сюда).
Материалы танка
Для всех частей танка используется один мастер-материал, кастомизируемый парой Switch-нод.
Настоящий «вес» здесь имеют только M_Vehicles и MI_Vehicles_Clean_Treads, все остальные инстансы — просто наборы параметров и потребляют минимум памяти и места на диске.
В целом, набор графических ассетов для танка получается весьма стандартным для любых игр.
Работа брони
Несколько раз при общении с коллегами возникал вопрос, почему каждый кусок брони у нас сделан отдельным мешем, и почему мы не используем анриловскую систему коллизий?
На самом деле мы используем обычные анриловские коллизии, но только чтобы «поймать» пулю. После того, как прожектайл воткнулся в танк, обсчёт повреждений происходит пополигонально по всем листам брони, с учётом свойств каждого куска, многослойности, переотражений снаряда, кумулятивной воронки и прочих интересных механик.
Самое удобное для такого случая — читать «голые» данные меша, которые для выделенного сервера мы не strip'аем.
Сеть и экстра-оптимизация
Пара «околотанковых» моментов, которые я также хотел бы кратко упомянуть.
Всё движение танков осуществляется только на сервере, клиенты лишь интерполируют полученные значения. Никакой экстраполяции не используется. Частота синхронизации — 10 раз в секунду.
В зависимости от ScreenSize танка мы пропускаем то или иное количество тиков скелетал меша, что включает в себя обсчёт всех анимаций и некоторых физических взаимодействий. Если танк не виден на экране — он тупой не анимированный ящик, плавающий в пространстве. Встроенный Update Rate Optimization, несмотря на хорошую идею, обладает весьма грубой реализацией, не дающей ощутимого прироста производительности. Особенно, если речь о мобилках.
Для всех танков, кроме собственного, на клиенте отрезаются все компоненты, кроме самых базовых: камер, чекеров и прочего, из чего состоит танк. По сути, они не имеют ничего, кроме внешнего вида.
На выделенном сервере ездит форсированный LOD1 танка. Меньше вершин — меньше потребления процессора. Причём обновление положения кастомных кусков брони происходит только в момент попадания прожектайла: нет смысла обновлять состояние частей каждый тик.
Me, Myself & Tanks
Мы в Pushkin Studio считаем, что обмен опытом разработки очень важен, в том числе и для роста профессионального уровня внутри команды. Open Source-проекты — значимая составляющая такого подхода, и я рад, что могу представить сообществу такую технологию, как PsRealVehicle.
На мой взгляд, очень важно, что мы сами ежедневно используем все публикуемые нами плагины и утилиты. Ведь путь, ярко демонстрируемый самими Epic Games, заключается в том, что успех хорошей технологии — это использование её самими разработчиками в собственных продуктах. Сейчас мы работаем уже на версии UE 4.20, наш плагин прошел с нами весь этот путь, и мы не собираемся останавливаться!