Ammo.js — это порт библиотеки Bullet Physics на JavaScript через Emscripten. Основная задача при интеграции — корректная загрузка WASM-модуля и обеспечение его доступности для физического движка. В современных сборщиках, таких как Webpack и Vite, важно правильно настроить обработку статических файлов и асинхронную инициализацию.
Варианты подключения:
npm install ammo.js
После установки можно импортировать модуль:
import Ammo from 'ammo.js';
При этом Ammo() возвращает Promise,
который резолвится после загрузки WASM-движка:
Ammo().then((AmmoLib) => {
const dynamicsWorld = new AmmoLib.btDiscreteDynamicsWorld(...);
});
<script src="https://cdn.jsdelivr.net/npm/ammo.js"></script>
<script>
Ammo().then((AmmoLib) => {
const collisionConfiguration = new AmmoLib.btDefaultCollisionConfiguration();
...
});
</script>
Для Webpack и Vite предпочтителен первый вариант, так как он позволяет оптимизировать загрузку и интеграцию в модульную систему.
При использовании Webpack, нужно убедиться, что WASM-файл правильно обрабатывается. Для этого:
webpack.config.js необходимо разрешить загрузку
.wasm:module.exports = {
mode: 'development',
module: {
rules: [
{
test: /\.wasm$/,
type: 'webassembly/async'
}
]
},
experiments: {
asyncWebAssembly: true
}
};
import Ammo from 'ammo.js';
async function initPhysics() {
const AmmoLib = await Ammo();
const collisionConfiguration = new AmmoLib.btDefaultCollisionConfiguration();
const dispatcher = new AmmoLib.btCollisionDispatcher(collisionConfiguration);
const overlappingPairCache = new AmmoLib.btDbvtBroadphase();
const solver = new AmmoLib.btSequentialImpulseConstraintSolver();
const dynamicsWorld = new AmmoLib.btDiscreteDynamicsWorld(
dispatcher,
overlappingPairCache,
solver,
collisionConfiguration
);
dynamicsWorld.setGravity(new AmmoLib.btVector3(0, -9.8, 0));
return dynamicsWorld;
}
Важно использовать async/await или then,
так как модуль загружается асинхронно. Без этого попытка создать объекты
Ammo.js приведет к ошибкам undefined.
public или обрабатывать через
file-loader.Vite поддерживает ES-модули и WASM из коробки, что упрощает интеграцию Ammo.js:
npm install ammo.js
import Ammo from 'ammo.js';
let physicsWorld;
async function setupPhysics() {
const AmmoLib = await Ammo();
const collisionConfig = new AmmoLib.btDefaultCollisionConfiguration();
const dispatcher = new AmmoLib.btCollisionDispatcher(collisionConfig);
const broadphase = new AmmoLib.btDbvtBroadphase();
const solver = new AmmoLib.btSequentialImpulseConstraintSolver();
physicsWorld = new AmmoLib.btDiscreteDynamicsWorld(
dispatcher,
broadphase,
solver,
collisionConfig
);
physicsWorld.setGravity(new AmmoLib.btVector3(0, -9.8, 0));
}
import. При использовании кастомных
файлов достаточно положить их в public/ и указать
путь:const AmmoLib = await Ammo({ locateFile: f => `/wasm/${f}` });
Это позволяет кастомизировать путь к WASM без изменения сборки.
new Ammo.btVector3(...). Для избежания утечек необходимо
удалять объекты вручную: Ammo.destroy(obj).window.AmmoLib = AmmoLib;.declare module 'ammo.js' {
const Ammo: any;
export default Ammo;
}
btDiscreteDynamicsWorld.Организация кода по модулям облегчает масштабирование и интеграцию с рендерингом на Three.js или Babylon.js.
new Ammo.bt....experiments.asyncWebAssembly.locateFile.let deltaTime = 1 / 60;
function updatePhysics() {
if (!physicsWorld) return;
physicsWorld.stepSimulation(deltaTime, 10);
// Обновление объектов сцены на основе rigid bodies
for (let obj of rigidBodies) {
const ms = obj.getMotionState();
if (ms) {
const transform = new AmmoLib.btTransform();
ms.getWorldTransform(transform);
const p = transform.getOrigin();
const q = transform.getRotation();
obj.mesh.position.set(p.x(), p.y(), p.z());
obj.mesh.quaternion.set(q.x(), q.y(), q.z(), q.w());
Ammo.destroy(transform);
}
}
}
Такая структура позволяет плавно интегрировать Ammo.js с любым рендерером, обеспечивая независимость физики от визуальной части и корректное управление памятью.