{"id":4012,"url":"\/distributions\/4012\/click?bit=1&hash=5b9cad3f989520ad358a2237d28d1f12ecdc50cb8452456f27fcbce716b2c8f0","title":"\u041f\u043e\u044f\u0432\u0438\u043b\u0441\u044f \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0434\u043b\u044f \u0441\u0442\u0435\u0441\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0440\u0438\u0435\u043b\u0442\u043e\u0440\u043e\u0432","buttonText":"","imageUuid":"","isPaidAndBannersEnabled":false}

Оптимизация физики в многопользовательской игре — опыт разработчиков Shadow Fight Arena Статьи редакции

Плоская иерархия и XPBD.

Ведущий технический художник Banzai Games Роман Терский в колонке для DTF рассказывает, как команда оптимизировала и улучшала физику в мобильном файтинге Shadow Fight Arena. Разработчикам было важно добиться не только реалистичности и кинематографичности, но и согласованности движений персонажей на разных устройствах при синхронном PvP.

Визуально Shadow Fight Arena (SFA) выгодно выделяется среди других игр серии благодаря переходу на Physically Based Rendering (PBR). Мы значительно улучшили графику, повысив качество текстур, изменив освещение и добавив пост-эффекты.

Чтобы соответствовать установленной планке, мы также переработали физику в игре, улучшив её как визуально, так и в плане оптимизации. В предыдущей статье я рассказывал о технических решениях, которые мы применили при настройке реалистичной и оптимизированной физики в игре Shadow Fight 3 (SF3). Часть этих решений перекочевали в SFA, поэтому я буду периодически ссылаться на старую статью. Однако большинство этих решений были изменены или улучшены.

В этом тексте я расскажу, как и почему мы перешли на плоскую иерархию в сцене, как избежали артефактов при просадке FPS и зачем снова переписали физику для персонажей с нуля, построив её на твёрдых телах и добившись её полной детерминированности.

Плоская иерархия

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

Это значит, что чем больше костей в скелете персонажа, чем длиннее иерархические цепочки, тем сильнее нагрузка при просчёте их изменений во время анимаций. Для оптимизации этого процесса мы решили перейти на плоскую иерархию в сцене. Каждый раз после инициализации моделей на сцене мы разбиваем все иерархии, вытаскивая компоненты префабов так, чтобы каждый из них стал отдельным game object.

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

Как же переход на плоскую иерархию сказался на физике?

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

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

Во время последнего кадра анимации происходила синхронизация трансформов костей двух скелетов. Физический клон считывал положение и ротацию костей основного скелета и задавал своим точно такие же параметры.

Затем активировался rigidbody, и кости физического клона подвергались симуляции. Далее, вплоть до начала следующей атакующей анимации, уже основной скелет каждый кадр считывал трансформы костей физического клона, которые в этот момент двигались по физике, и задавал эти параметры своим костям.

Физический клон отлично показал себя в плане реалистичности поведения во время симуляции, но, к сожалению, такое решение не лучшее с точки зрения оптимизации, так как кости в иерархии в связке с джоинтами требуют лишних вычислений. Гораздо «дешевле» связывать джоинтами кости, являющиеся отдельными game object.

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

Во-вторых, мы отказались от фейкового импульса, который применялся в SF3 для исправления визуальных артефактов при симуляции физики твёрдых тел. В SFA использование этого решения невозможно, так как оно подразумевает нахождение симулируемых костей внутри иерархии. Однако фейковый импульс — скорее временная мера и не даёт достаточного физически реалистичного результата, и для SFA мы применили другой метод.

Для начала о проблеме. При стабильных 60 FPS все идёт гладко, но во время просадок, которые возможны на слабых устройствах, мы наблюдаем следующее: симулируемые кости начинают с задержкой «догонять» анимируемые кости, с которыми они связаны джоинтами.

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

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

В качестве решения мы отключили автоматическое обновление физики в Unity. Теперь мы самостоятельно обновляем физическое состояние объектов после основного апдейта и перед рендером. Это решение было бы невозможным, если бы мы использовали дефолтную физику Unity для персонажей. В таком случае симуляция для них развивалась бы по-разному в момент просадки FPS на одном из устройств, что непозволительно при синхронном PvP.

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

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

XPBD-детерминированная физика, построенная на твёрдых телах

Персонажи в серии игр Shadow Fight подвергались нескольким итерациям настройки физики. В SF3 мы использовали дефолтную физику Unity до тех пор, пока в игру не был введён режим синхронного PvP, для которого потребовалась одинаковая симуляция на двух клиентах. Из-за вычислений с плавающей запятой, которые используются внутри физики в Unity, от неё пришлось отказаться в пользу собственной физики, построенной на узлах, мышцах и ребрах и использующей целочисленные вычисления.

В Shadow Fight Arena мы пошли дальше и, основываясь на предыдущем опыте, создали новый ragdoll для персонажей, основанный на подходе Extended position-based dynamics (XPBD). Новая физика также использует целочисленные вычисления и полностью детерминированна, однако имеет ряд преимуществ перед предыдущем решением, построенным на узлах.

С технической точки зрения традиционная Unity-физика имеет constraint solver, а XPBD-физика — iterative solver. То есть вместо того, чтобы решать каждый тик систему уравнений для выяснения того, какое положение тел удовлетворит всем ограничениям, мы итерационно выводим систему из состояния с нарушенными ограничениями в состояние, соответствующее им.

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

В методе PBD, когда обнаруживается проникновение двух объектов, положение объекта напрямую корректируется в соответствии с заданными ограничениями, а затем обновляется информация о скорости. Итеративный метод PBD обеспечивает сравнимое с PhysX качество удовлетворения ограничений за счёт большего количества итераций. Благодаря этому PBD даёт больше детализации без потери очень быстрых движений и контактов, однако существенно хуже масштабируется по сравнению с PhysX. Поскольку в нашей игре одновременно могут быть активны только два ragdoll, нас это полностью устроило.

Наш ragdoll состоит из box-коллайдеров, соединённых между собой джоинтами двух типов: Spherical и Hinge. Первый тип ограничивает поворот коллайдеров по заданным осям, а во втором указывается конус вращения по определенной оси. Для этих box-коллайдеров рассчитываются столкновения только с бесконечным горизонтальным полом, соответственно они не имеют коллизий между собой и с внешними динамическими коллайдерами. Таким образом, на результат симуляции влияет только стартовая позиция.

Избежать вероятности проникновения тел друг в друга нам удалось за счёт точечной настройки каждого джоинта. Также для каждого тела настраивается масса, а для соединений — степень затухания (damping) и другие стандартные для подобной системы параметры.

После инициализации персонажа на сцене его скелет синхронизируется с нашим ragdoll. На старте симуляции каждое тело принимает начальную позицию кости, с которой оно было связано при настройке. Далее уже кости скелета каждый кадр перезаписывают свои позиции в соответствии с телами, исходя из результатов симуляции. Незадействованные кости, в свою очередь, на всё время симуляции сохраняют локальную позицию без изменений.

Еще при разработке физики, построенной на узлах, мы обнаружили, что использование большого количества итераций корректировки за один шаг (Substep) даёт намного более мягкий результат симуляции, чем много шагов по одной итерации. Автор метода XPBD так же сделал подобное наблюдение и положил этот принцип в основу своего подхода.

Настроив ragdoll и интегрировав новую физику в проект, мы заметили, что во время симуляции тело персонажа ведет себя абсолютно безвольно, так, как если бы персонаж был «в отключке» или мёртв. Это выглядело достаточно реалистично, но в нашей игре чаще всего после перехода в физику персонажи быстро встают и продолжают бой.

Нам хотелось добиться эффекта сгруппированности, как если бы персонаж был просто сбит с ног, но сохранял сознание. Для этого мы добавили ещё одно ограничение для box-коллайдеров — стремление к определённой позе. В результате мы получили сгруппированное, но очень жёсткое тело, поведение персонажа стало слишком «деревянным».

Для достижения более мягкой симуляции мы добавили регулируемый параметр податливости (Compliance) и сделали его динамическим. Первые 20 кадров симуляции мы повышаем значение Compliance, делая тело более мягким в момент получения удара и перехода в физику.

Это нужно для того, чтобы мягко выводить персонажа из стартовой позы к целевой, так как эти позы могут быть далеки друг от друга, и без Compliance этот переход может вызвать нежелательные импульсы в конечности. Затем мы обнуляем Compliance, оставляя ragdoll жёстким и сгруппированным вплоть до момента приземления, когда мы снова смягчаем тело, чтобы оно выглядело более расслабленным.

Original — Pose — Pose + Compliance

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

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

Помимо этого мы всегда придаем дополнительный импульс в размере 20% от основного по оси Z, слегка подкидывая ragdoll. А в случае если во время симуляции персонаж получит новый удар, мы сохраняем изначальный вектор движения, добавляя к нему только 30% от нового импульса. Все эти манипуляции позволили добиться достаточно реалистичного и кинематографичного результата.

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

Используемый итерационный подход весьма дешёвый по математике и оперирует очень маленькими шагами по времени, что даёт хорошую временную детализацию — не теряются очень быстрые движения и контакты. Детерминированность достигается за счёт целочисленных вычислений и строгой замкнутости физики в себе: отсутствие коллизий с внешними динамическими коллайдерами, только наш ragdoll, бесконечный пол и начальный импульс. На результат симуляции влияет только стартовая позиция.

Подробнее ознакомиться с методом Position based dynamics можно по ссылке. А демо и сорсы доступны для скачивания тут.

0
32 комментария
Написать комментарий...
ПириРейис

Какой же все таки у них охрененный риггинг движения и тела

Ответить
Развернуть ветку

Комментарий удален модератором

Развернуть ветку
Денис Кадыков

хе-х, зато Юнити запилили новую заставку логотипчика

Ответить
Развернуть ветку
Аккаунт удален

Комментарий недоступен

Ответить
Развернуть ветку
IlyaM

Решение с физикой действительно круто выглядит, очень мягко и реалистично.

Для оптимизации этого процесса мы решили перейти на плоскую иерархию в сцене.

Это как вы такого добились? Анимации же к иерархии привязаны. У вас какой-то экспортер особый? На сколько я понимаю у вас Каскадер и Мая в пайплайне и при экспорте в fbx там же иерархия запекается.

Ответить
Развернуть ветку
Роман Терский

У нас свой анимационный движок, который знает про иерархию, но эта информация уже не нужна, когда посчитанная поза переносится на сцену в Unity. Т.е. иерархии нет именно на трансформах, но во внутреннем представлении она есть.

Ответить
Развернуть ветку
IlyaM

А, ну я так и подумал что у вас есть волшебная кнопка "улучшения Unity". Спасибо за статью!

Ответить
Развернуть ветку
ПириРейис

Вопрос такой речь же о  cascadeur?
А вот описанный вами процесс он применим\совместим к Unreal engine?

Ответить
Развернуть ветку
Maxim Knz

Анимацию можно сохранить и на плоский скелет, просто она весить будет в 5-10 раз больше.

Оптимальное решение делать только ключевые кости плоскими.

Ответить
Развернуть ветку
Денис Кадыков

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

Честно говоря так и не понял зачем понадобилась плоская иерархия скелета, видимо на выходе самописная физика принимает именно такой вид

Ответить
Развернуть ветку
Дмитрий Фофанов

Понадобилась для оптимизации — в плоской иерархии присвоение позиции/вращения пересчитывает только матрицу самого объекта, а в случае глубокой иерархии присвоение родителю пересчитает его и всех детей. Итого за полный проход по всем костям получается большой оверхэд.
Анимации продолжили быть в локальных координатах, а присваиваются в трансформы — в глобальных. В проекте своя анимационная система, поэтому есть возможность обойти все кости от корня вниз, рассчитывая глобальные координаты и выставляя по ним объекты из плоской иерархии.
Нет, физике все равно в каком виде иерархия персонажа.

Ответить
Развернуть ветку
Денис Кадыков

но ведь вычисления никуда не делись. Просто теперь все повороты считает не Юнити, а ваша система.

Ответить
Развернуть ветку
Дмитрий Фофанов

Вычисления положения каждого объекта — да, те же самые. Но нет обновлений всех дочерних трансформов при выставлении положения/ориентации родителя.

Ответить
Развернуть ветку
Денис Кадыков

 Спасибо.

Удивительно что Юнити не считает от корневого к дочерним и только потом обновляет. Ведь родители не зависят от детей и нет смысла обновлять пока всех не посчитают. 

Либо я что то не понимаю. Но ок.

Ответить
Развернуть ветку
Дмитрий Фофанов

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

Ответить
Развернуть ветку
IlyaM

Анимирование персонажа, в любом пакете, в принципе требует иерархии для IK и FK, и она так и экспортируется. Как ее плоской после этого сделать? Просто вытащить кость предплечья и положить рядом с костью плеча не получится, потому что анимации костей всегда в локальном пространстве относительно друг друга.

Ответить
Развернуть ветку
Maxim Knz

Плоские деформационные кости наследуют трансформы от иерархичных костей скелетов IK FK, наследуются или черз матрицы преобразования или через констрейны.  Анимация бейкается потом и экспортится только деформационный скелет.

Ответить
Развернуть ветку
Chicot d’Aangleraye

это верно: "Анимирование персонажа, в любом пакете, в принципе требует иерархии для IK и FK"
тут нет: "и она так и экспортируется". Экпортируется FK.
Экспортировать плоскую иерархию можно сдублироваав и разобрав риг, а потом переносом (бейком) анимации - то, что регулярно делается аниматорами (бейк сконтролов на кости, перебейк с мокапа и т.п.).

Ответить
Развернуть ветку
Андрей Опачирик

1)

Еще при разработке физики, построенной на узлах, мы обнаружили, что использование большого количества итераций корректировки за один шаг (Substep) даёт намного более мягкий результат симуляции, чем много шагов по одной итерации.

Разве на гифке правое тело не более корректно? То, которое 1 iteration / substep?
2)

Новая физика также использует целочисленные вычисления и полностью детерминированна

А этого достаточно для полной детерминированности? Просто я не в курсе. Просто звучит как довольно простое решение и подозрительно, что кто-то вообще использует флоаты для физических движков в таком случае
3)
Используете Jobs или че-нить нативное для своей физики вместо managed C#?

Ответить
Развернуть ветку
Дмитрий Фофанов

(кодил эту физику)
1) Да, верно, сабстепы вместо итераций показывают себя лучше. Думаю, тут сложности формулировки вмешались
2) Да, если внешних зависимостей нет, случайных чисел нет, то целочисленной математики достаточно. Ее не используют в PhysX или Havok потому, что она существенно дороже по производительности и накладывает серьезные ограничения в плане расчетов. Очень легко получить overflow/underflow, что, скорее всего, взорвет симуляцию.
3) Было в планах, но не понадобилось — производительности managed версии оказалось достаточно

Ответить
Развернуть ветку
Андрей Опачирик

2)

Очень легко получить overflow/underflow, что, скорее всего, взорвет симуляцию.

А почему в вашем случае это не проблема?

Ответить
Развернуть ветку
Дмитрий Фофанов

- небольшая, строго ограниченная сцена
- фактически один сценарий симуляции
- известные симулируемые тела
- известный максимум внешнего импульса

За счет этого можно проверить критические сценарии и быть более-менее уверенными, что в продакшне не будет проблем.

Ответить
Развернуть ветку
Андрей Опачирик

ок. спасибо. 
годная статья, вам бы на иглише ее распространять

Ответить
Развернуть ветку
Lonely Vasyan

Рэгдолл выглядит прикольно, но... Файтинги же традиционно были про камерность и постановочность. И потому там почти всегда анимации отлетающих персонажей делали вручную, чтобы выглядело максимально драматично. Ну, при условии, что это не симулятор ММА.
А тут получилась система, которая потенциально может заменить эйфорию из ГТА. Но нужна ли она файтингу в фэнтезийном сеттинге?

Кстати да, поскорее бы уже полноценный аналог эйфории от создателей всего этого. 

Ответить
Развернуть ветку
Роман Терский

Серия игр Shadow Fight всегда делала упор на физичность анимаций и "драматичные" анимации отлетающих персонажей не очень вписываются в эту концепцию. :)

Ответить
Развернуть ветку
Lonely Vasyan

Ну ладно. Вы в любом случае молодцы. Ток убийцу эйфории сделайте как можно скорее, чтобы плагином в юнити/анрил можно было добавить и пользоваться. Можете даже ценник любой ставить - за сколько угодно возьмут.

Ответить
Развернуть ветку

Комментарий удален модератором

Развернуть ветку

Комментарий удален модератором

Развернуть ветку

Комментарий удален модератором

Развернуть ветку

Комментарий удален модератором

Развернуть ветку
Аккаунт заморожен

Комментарий недоступен

Ответить
Развернуть ветку
Максим Карпов

А как вы написали физику и сменили последовательность в Unity? Переписали сам код движка?

Ответить
Развернуть ветку
Дмитрий Фофанов

Написали свое и не использовали встроенное

Ответить
Развернуть ветку
Максим Карпов

А детали? Больше интересно именно это.

Ответить
Развернуть ветку
Дмитрий Фофанов

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

Ответить
Развернуть ветку
Клуб фанатов Антона Логвинова

Once, i was a great invincible warrior…

Ответить
Развернуть ветку
Alice Thunderbird

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

впринципе можно и свое дерево строить - которое есть - уже -
но которого при плоском разложении нету в инспекторе объектов юнити
как именно дерева а просто линейный набор в столпе обороны игры :)

Drive MK : Magic Kingdom
https://www.youtube.com/watch?v=XeQryt_NU2Y

Ответить
Развернуть ветку
Читать все 32 комментария
null