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

Введение


В статье будут рассмотрены следующие движки:

Ammo.js
Cannon.js
Oimo.js
box2dweb
Unity3D WebGL

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

Ammo.js


Является портом Bullet physics engine на javascript с использованием компилятора Emscripten и по заявлению разработчиков обладает практически идентичным функционалом. Функционал Ammo.js действительно обширен. Для работы с ним понадобится отдельная библиотека для визуализации. Чаще всего используется Three.js. При этом каждый цикл перерисовки придётся вручную синхронизировать положение и вращение каждого объекта на сцене с его физической моделью, движок не делает это автоматически.

Что касается производительности, она не слишком высокая, но и заметных просадок fps в большинстве проектов не будет.

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

Cannon.js


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

Процесс работы с движком довольно прост:

// Инициализируем движок
var world = new CANNON.World();
world.gravity.set(0, 0, -9.82); // Устанавливаем гравитацию (движок использует единицы СИ)

// Создаём объект и добавляем его на сцену
var radius = 1;
var sphereBody = new CANNON.Body({
   mass: 5,
   position: new CANNON.Vec3(0, 0, 10),
   shape: new CANNON.Sphere(radius)
});
world.addBody(sphereBody);

var fixedTimeStep = 1.0 / 60.0;
var maxSubSteps = 3;

// Запускаем цикл симуляции
var lastTime;
(function simloop(time){
  requestAnimationFrame(simloop);
  if(lastTime !== undefined){
     var dt = (time - lastTime) / 1000;
     world.step(fixedTimeStep, dt, maxSubSteps);
  }
  lastTime = time;
})();

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

mesh.position.x = body.position.x;
mesh.position.y = body.position.y;
mesh.position.z = body.position.z;
mesh.quaternion.x = body.quaternion.x;
mesh.quaternion.y = body.quaternion.y;
mesh.quaternion.z = body.quaternion.z;
mesh.quaternion.w = body.quaternion.w;

В данный момент движок практически не развивается, последняя активность в репозитории проекта более 2 лет назад, а на тот момент движок ещё только начинал развиваться, так что в некоторых местах он может оказаться не проработан.

Oimo.js


Oimo.js — переписанная на чистом javascript версия движка OimoPhysics. В сравнении с другими решениями, обладает очень хорошей производительностью и точностью, однако поддерживает только примитивную геометрию (кубы и сферы). Включён в состав Babylon.js — фреймворка для визуализации 2D и 3D графики, поэтому каких то дополнительных библиотек не потребуется.

// Инициализируем движок
world = new OIMO.World({ 
    timestep: 1/60, 
    iterations: 8, 
    broadphase: 2,
    worldscale: 1,
    random: true,
    info: false,
    gravity: [0,-9.8,0] 
});

// Добавляем физические объекты
var body = world.add({ 
    type:'sphere', 
    size:[1,1,1],
    pos:[0,0,0],
    rot:[0,0,90],
    move:true,
    density: 1,
    friction: 0.2,
    restitution: 0.2,
    belongsTo: 1,
    collidesWith: 0xffffffff;
});

var body = world.add({ 
    type:'jointHinge',
    body1: "b1",
    body2: "b1",
});

world.step();

// Копировать позицию и вращение так же нужно каждый цикл отрисовки
myMesh.position.copy( body.getPosition() );
myMesh.quaternion.copy( body.getQuaternion() );

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

На данный момент движок продолжает развиваться.

box2dweb


box2dweb — это порт box2d на javascript. Как понятно из названия, специализируется на симуляции 2D физики. Несмотря на это, box2dweb — довольно мощный инструмент, который нисколько не отстаёт от своих трёхмерных аналогов. Например движок включает крайне удобные системы обнаружения коллизий и имитации соединений (constraint).

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

Из плюсов так же стоит упомянуть простоту API и удобную документацию.

Unity3D


Unity3D — популярный кросс-платформенный игровой движок. Включает в себя простой удобный drag&drop редактор и обширный инструментарий по созданию 3D-контента. Последняя версия движка для написания игровой логики поддерживает C#.

Unity имеет встроенную симуляцию физики, для этого используется встроенный движок PhysX от NVIDIA. PhysX даёт обширный функционал по симуляции физики твёрдых тел, жидкостей и тканей, обладает очень хорошей производительностью, хотя многие плюсы аннулируются при работе на графических ускорителях не от NVIDIA. Крайне приятным фактом является то, что с 3 декабря 2018 исходный код движка доступен под открытой лицензией BSD-3, тем не менее движок слишком сложный, чтобы пытаться переписывать его под себя или разбираться в его устройстве, так что тут лучше поможет документация.

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



Тем не менее WebGL версия Unity в силу особенностей своей архитектуры (трансляция кода из C# в С++ и далее в JavaScript), имеет ряд проблем с производительностью, потреблением памяти и работоспособностью на мобильных устройствах, и не похоже, что разработчики собираются с этим что-то делать в ближайшее время. Поэтому данный вариант не пользуется популярностью и я не стану его подробно рассматривать.

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


Сравним производительность движков по тому, как они справляются с обработкой коллизий большого количества объектов. Используемый браузер — Firefox 64.0.2 x64.
Движок fps при обработке 100 объектов fps при обработке 500 объектов fps при обработке 1000 объектов
ammo.js 40-50 25-27 15-25
cannon.js 30-40 20-25 15-20
oimo.js 45-55 35-40 35-40

По результатам тестов Oimo.js показывает лучшую производительность.

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

Вывод


В целом, выбор конкретного движка зависит от поставленной задачи. Если требуется простой в понимании и легко осваиваемый движок — хорошо подходит Сannon.js или Oimo.js. Если требуется больше функционала, лучше использовать Ammo.js. В определённых ситуациях, если большая производительность не требуется, можно попробовать использовать Unity.

Комментарии (11)


  1. Keyten
    15.01.2019 18:19
    +1

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

    Простите, что?


    1. movl
      16.01.2019 12:56

      Я так понял, имелось ввиду, что при компиляции кода из Си/С++ в JS, компилятор использует множество оптимизаций, которыми программист на JS может пренебрегать в пользу удобства написания кода, и из-за этого код изначально написанный, например, на C++ и скомпилированный в JS, может оказаться эффективнее, чем аналогичный код изначально написанный на JS.


  1. newpavlov
    15.01.2019 18:28
    +2

    А ещё можно использовать написанный на Расте nphysics через WASM: nphysics.org/demo_body_status3


    1. humbug
      15.01.2019 18:40
      +1

      Да. Можно посмотреть на поведение движка на шарах в кубе с гранью 8 (512 шаров). У меня отрисовка фрейма занимает 0.001-0.002 секунды.


      1. AngReload
        15.01.2019 20:20

        Это в статике на сферах. На кубиках в падении или если их мышкой таскать — у меня подтормаживает до 0.023s -> 43fps. И, кажется, симуляции недостаточно точные — в демке с неровной землёй один кубик проваливается сквозь сетку.


        1. humbug
          15.01.2019 20:34

          Да, я намеренно дал ссылку на шары, ведь в статье как раз таки бенчи на шарах.


          Стоит заметить, что кубики требуют больше ресурсов для обсчета.


  1. ThisMan
    15.01.2019 18:41

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


  1. KpoKec
    15.01.2019 19:22

    Unity имеет встроенную симуляцию физики, для этого используется встроенный движок PhysX от NVIDIA

    если только для 3D, в 2D там box2D используется


  1. KpoKec
    15.01.2019 19:24
    +1

    имеет ряд проблем с производительностью, потреблением памяти и работоспособностью на мобильных устройствах

    с кривыми руками на любом движке проблемы будут. Множество игр в Гугл Плей на нём написано, на ПК много, Escape From Tarkov тоже на нём пишут.
    Поэтому данный вариант не пользуется популярностью и я не стану его подробно рассматривать.
    да вообще без комментариев


  1. KpoKec
    15.01.2019 19:29

    Например, Metal War Online работал в WebGL, пока

    С 1 апреля 2015 года, Google Chrome, а за ним и многие другие браузеры, прекратили поддержку Unity Web Player.


    1. Altren
      15.01.2019 23:27

      Unity Web Player был плагином для браузера и не использовал WebGL