В этой статье я хочу дать описание существующих решений для создания приложений с симуляцией физики, сравнить их производительность и предоставляемый инструментарий.
Введение
В статье будут рассмотрены следующие движки:
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.