Ammo.js — это JavaScript-порт физического движка Bullet Physics. Он
использует WebAssembly, что позволяет создавать
высокопроизводительные симуляции. Одним из ключевых аспектов работы с
этим движком является управление памятью объектов,
созданных через API. В отличие от обычных объектов JavaScript, объекты
Ammo.js не очищаются автоматически сборщиком мусора,
поэтому требуется явный вызов Ammo.destroy() для
освобождения памяти.
Каждый вызов Ammo.destroy() имеет накладные
расходы, особенно если уничтожается большое количество объектов
за кадр. Избыточные вызовы могут значительно замедлить
производительность симуляции. Минимизация этих вызовов позволяет
ускорить рендеринг и уменьшить нагрузку на основной поток.
Наиболее эффективная стратегия — создание пулов объектов. Пул объектов представляет собой заранее выделенный набор экземпляров для повторного использования. Вместо создания и уничтожения нового объекта при каждом кадре, используется уже существующий объект из пула.
Пример пула для btVector3:
const vectorPool = [];
function getVector(x = 0, y = 0, z = 0) {
let vec;
if (vectorPool.length) {
vec = vectorPool.pop();
vec.setValue(x, y, z);
} else {
vec = new Ammo.btVector3(x, y, z);
}
return vec;
}
function releaseVector(vec) {
vectorPool.push(vec);
}
Ключевой момент: объект не уничтожается, а возвращается в пул. Это позволяет многократно использовать одну и ту же память.
При симуляции физических взаимодействий часто создаются временные
объекты, например в коллизиях, при вычислении импульсов или
преобразовании координат. Чтобы минимизировать количество вызовов
Ammo.destroy(), следует:
Пример использования одного временного вектора:
const tempVec = new Ammo.btVector3(0, 0, 0);
function applyImpulse(body, impulse, relPos) {
tempVec.setValue(impulse.x, impulse.y, impulse.z);
body.applyImpulse(tempVec, relPos);
}
После завершения симуляции можно уничтожить tempVec
один раз, а не при каждом вызове
applyImpulse.
Для больших симуляций освобождение памяти должно быть структурированным. Рекомендуется:
bodies, shapes,
constraints).Ammo.destroy() для каждого объекта.Ammo.destroy() очищать ссылки на объекты,
чтобы избежать “висячих” ссылок.Пример организации:
const rigidBodies = [];
const collisionShapes = [];
// Создание объектов
const boxShape = new Ammo.btBoxShape(new Ammo.btVector3(1,1,1));
collisionShapes.push(boxShape);
const body = createRigidBody(boxShape, mass, position);
rigidBodies.push(body);
// Очистка
function cleanupPhysics() {
for (const body of rigidBodies) {
physicsWorld.removeRigidBody(body);
Ammo.destroy(body);
}
rigidBodies.length = 0;
for (const shape of collisionShapes) {
Ammo.destroy(shape);
}
collisionShapes.length = 0;
}
Такой подход гарантирует, что вызовы Ammo.destroy()
минимизированы и централизованы, что снижает риск
утечек памяти.
btTransform и матрицамиbtTransform и аналогичные структуры часто создаются для
позиционирования объектов. Чтобы минимизировать вызовы
Ammo.destroy():
btTransform, который
используется для всех операций на кадр.const tempTransform = new Ammo.btTransform();
function updateBodyPosition(body, position, rotation) {
tempTransform.setOrigin(new Ammo.btVector3(position.x, position.y, position.z));
tempTransform.setRotation(new Ammo.btQuaternion(rotation.x, rotation.y, rotation.z, rotation.w));
body.setWorldTransform(tempTransform);
}
Здесь важно не создавать новые btVector3 и btQuaternion каждый кадр, а использовать либо временные объекты из пула, либо переиспользовать один глобальный объект.
Минимизация вызовов Ammo.destroy() требует контроля
использования памяти:
btVector3, btTransform,
btRigidBody).Пример счетчика:
let vectorCount = 0;
function createVector(x, y, z) {
vectorCount++;
return new Ammo.btVector3(x, y, z);
}
function destroyVector(vec) {
Ammo.destroy(vec);
vectorCount--;
}
Это позволяет точно видеть, где объекты не возвращаются в пул или не уничтожаются.
Ammo.destroy() внутри
высокочастотных функций (коллизии, импульсы, позиции объектов).Такой подход обеспечивает оптимальное управление памятью, снижает нагрузку на сборщик мусора и поддерживает высокую производительность физического движка в браузере.