Библиотека Ammo.js распространяется в виде npm-пакета и может использоваться в современных проектах на базе модульных сборщиков. Пакет представляет собой порт физического движка Bullet Physics, скомпилированный в WebAssembly или asm.js с помощью Emscripten.
Установка выполняется стандартным способом:
npm install ammo.js
или при использовании Yarn:
yarn add ammo.js
После установки пакет появляется в директории
node_modules и может быть импортирован в код.
Ammo.js не является обычным ES-модулем. Библиотека экспортирует фабричную функцию, возвращающую промис, который разрешается в объект модуля. Это связано с асинхронной инициализацией WebAssembly.
Типичная структура использования:
import Ammo from "ammo.js";
Ammo().then((AmmoLib) => {
const collisionConfig = new AmmoLib.btDefaultCollisionConfiguration();
const dispatcher = new AmmoLib.btCollisionDispatcher(collisionConfig);
});
В отличие от большинства npm-пакетов, Ammo.js:
.wasm файл;Поэтому доступ к API возможен только после завершения инициализации.
Webpack корректно работает с Ammo.js без дополнительной настройки в большинстве случаев. Однако важно учитывать несколько аспектов:
Webpack 5 поддерживает WebAssembly из коробки. Если используется Webpack 4, требуется включение соответствующих опций:
module.exports = {
experiments: {
asyncWebAssembly: true
}
};
Иногда требуется явно указать тип ресурса:
module.exports = {
module: {
rules: [
{
test: /\.wasm$/,
type: "webassembly/async"
}
]
}
};
В современных конфигурациях это обычно не требуется.
Vite работает с WebAssembly значительно проще благодаря нативной поддержке ES-модулей.
Установка стандартная:
npm install ammo.js
Импорт аналогичен:
import Ammo from "ammo.js";
const AmmoLib = await Ammo();
Во время разработки Vite корректно обрабатывает .wasm
как статический ресурс. Дополнительной конфигурации обычно не
требуется.
Если возникают ошибки MIME-типа, необходимо убедиться, что сервер возвращает:
Content-Type: application/wasm
Rollup требует подключения плагина для работы с WebAssembly:
npm install @rollup/plugin-wasm --save-dev
Конфигурация:
import wasm from "@rollup/plugin-wasm";
export default {
plugins: [
wasm()
]
};
После этого Ammo.js будет корректно подключаться как асинхронный модуль.
Пакет может содержать несколько вариантов сборки:
Современные браузеры используют WebAssembly по умолчанию. Проверка поддержки:
if (typeof WebAssembly === "object") {
// используется wasm
}
WebAssembly-версия обеспечивает:
В крупных проектах целесообразно загружать Ammo.js только при необходимости.
async function loadPhysics() {
const AmmoModule = await import("ammo.js");
const AmmoLib = await AmmoModule.default();
return AmmoLib;
}
Такой подход:
Иногда сборщик размещает .wasm в отдельной директории. В
таком случае требуется указать путь вручную:
import Ammo from "ammo.js";
Ammo({
locateFile: (path) => {
if (path.endsWith(".wasm")) {
return "/assets/wasm/" + path;
}
return path;
}
}).then((AmmoLib) => {
// работа с библиотекой
});
Параметр locateFile используется рантаймом Emscripten
для поиска бинарных ресурсов.
Ammo.js не всегда поставляется с полноценными типами. Возможны два варианта:
npm install --save-dev @types/ammo.js
Создание файла ammo.d.ts:
declare module "ammo.js" {
export default function Ammo(config?: any): Promise<any>;
}
Для строгой типизации рекомендуется описывать используемые классы вручную, например:
type AmmoType = Awaited<ReturnType<typeof Ammo>>;
Ammo.js часто используется совместно с Three.js для реализации физики в браузерных 3D-приложениях.
Типичная схема инициализации:
import * as THREE from "three";
import Ammo from "ammo.js";
async function init() {
const AmmoLib = await Ammo();
const physicsWorld = new AmmoLib.btDiscreteDynamicsWorld(
new AmmoLib.btCollisionDispatcher(
new AmmoLib.btDefaultCollisionConfiguration()
),
new AmmoLib.btDbvtBroadphase(),
new AmmoLib.btSequentialImpulseConstraintSolver(),
new AmmoLib.btDefaultCollisionConfiguration()
);
}
Разделение графики и физики позволяет:
Ammo.js может использоваться в среде Node.js, если требуется серверная физическая симуляция.
Пример:
const Ammo = require("ammo.js");
Ammo().then((AmmoLib) => {
const world = new AmmoLib.btDiscreteDynamicsWorld(...);
});
Следует учитывать:
Ammo.js использует модель ручного управления памятью, унаследованную от C++.
Важно:
const vec = new AmmoLib.btVector3(0, 10, 0);
// использование
AmmoLib.destroy(vec);
Сборщики модулей не влияют на жизненный цикл объектов внутри
WebAssembly. Утечки памяти возможны при отсутствии вызова
destroy.
Ammo.js — тяжёлый модуль (несколько мегабайт). Для оптимизации:
Пример для Webpack:
webpack --mode production
В production-режиме происходит:
Причины:
.wasm;Решение — настройка сервера для выдачи:
application/wasm
Ammo.js нельзя вызывать повторно без необходимости. Рекомендуется хранить инстанс:
let ammoInstance;
async function getAmmo() {
if (!ammoInstance) {
ammoInstance = await Ammo();
}
return ammoInstance;
}
При использовании Ammo.js в проекте со сборщиком модулей рекомендуется:
physics.js;AmmoLib;Такая структура облегчает масштабирование проекта и упрощает тестирование.
Ammo()..wasm.Корректная интеграция Ammo.js через современные сборщики модулей позволяет использовать производительную физику WebAssembly в крупных веб-приложениях без нарушения архитектурной чистоты проекта.