Взрыв в игровой физике — это кратковременное радиальное воздействие, создающее импульс, распространяющийся от центра к окружающим телам. В библиотеке Ammo.js нет встроенного объекта «взрыв», поэтому его поведение моделируется вручную через применение импульсов к телам, попадающим в радиус воздействия.
Ключевые особенности реализации:
Взрыв характеризуется следующими параметрами:
center — точка эпицентра;radius — радиус действия;force — базовая сила;falloff — функция затухания.Алгоритм расчёта:
В Ammo.js физический мир представлен объектом
btDiscreteDynamicsWorld. Доступ к телам осуществляется
через список коллизий или хранение собственных ссылок на
btRigidBody.
Пример перебора всех объектов:
function applyExplosion(world, center, radius, force) {
const numObjects = world.getNumCollisionObjects();
for (let i = 0; i < numObjects; i++) {
const obj = world.getCollisionObjectArray().at(i);
const body = Ammo.castObject(obj, Ammo.btRigidBody);
if (!body || body.isStaticObject()) continue;
applyExplosionToBody(body, center, radius, force);
}
}
Позиция тела извлекается через его motionState:
function getBodyPosition(body) {
const transform = new Ammo.btTransform();
body.getMotionState().getWorldTransform(transform);
return transform.getOrigin();
}
Далее рассчитывается вектор смещения:
function applyExplosionToBody(body, center, radius, force) {
const position = getBodyPosition(body);
const dx = position.x() - center.x();
const dy = position.y() - center.y();
const dz = position.z() - center.z();
const distance = Math.sqrt(dx*dx + dy*dy + dz*dz);
if (distance > radius) return;
Существует несколько моделей ослабления импульса.
[ F = F_0 (1 - )]
const strength = force * (1 - distance / radius);
[ F = F_0 (1 - ()^2)]
Создаёт более мощный эффект у центра и слабый на границе.
Физически более реалистичная модель:
[ F = ]
Однако требует ограничения максимальной силы во избежание экстремальных значений.
В Ammo.js импульс применяется методом:
body.applyCentralImpulse(vector);
Полная реализация:
if (distance > 0.0001) {
const nx = dx / distance;
const ny = dy / distance;
const nz = dz / distance;
const strength = force * (1 - distance / radius);
const impulse = new Ammo.btVector3(
nx * strength,
ny * strength,
nz * strength
);
body.activate();
body.applyCentralImpulse(impulse);
}
Метод activate() необходим для пробуждения тел,
находящихся в состоянии сна.
Импульс в физике связан с изменением скорости:
[ v = ]
В Ammo.js applyCentralImpulse автоматически учитывает
массу тела.
Для компенсации чрезмерного влияния массы возможна нормализация:
const mass = 1 / body.getInvMass();
const adjustedStrength = strength * mass;
Однако это изменяет физическую правдоподобность поведения.
Для более реалистичного эффекта используется точечный импульс:
body.applyImpulse(impulse, relativePosition);
relativePosition — вектор от центра масс тела до точки
приложения силы.
Это создаёт:
Полный перебор всех объектов неэффективен при большом количестве тел. Возможные решения:
Создание временного btSphereShape и проверка пересечений
через broadphase.
Временный btGhostObject с формой сферы позволяет
получить список перекрывающихся объектов:
const sphere = new Ammo.btSphereShape(radius);
const ghost = new Ammo.btGhostObject();
ghost.setCollisionShape(sphere);
После добавления в мир можно извлечь
getNumOverlappingObjects().
Иногда требуется не мгновенный импульс, а кратковременная ударная волна.
Реализация:
Это создаёт эффект давления, а не мгновенного толчка.
После импульса движение тел определяется:
Для усиления эффекта:
body.setDamping(0.05, 0.05);
Меньшее демпфирование позволяет объектам разлетаться дальше.
Для более зрелищного результата вектор импульса можно модифицировать:
ny += 0.3;
Это создаёт лёгкий подъём тел вверх, имитируя давление горячего газа.
В сочетании с:
можно реализовать разрушение.
Пример:
if (strength > breakThreshold) {
world.removeRigidBody(body);
}
Объекты btVector3, btTransform,
btSphereShape требуют ручного освобождения:
Ammo.destroy(impulse);
Ammo.destroy(transform);
При частых взрывах необходимо избегать утечек памяти.
Взрыв обычно синхронизируется с:
Физика должна выполняться в том же кадре, что и визуальный эффект, до
вызова world.stepSimulation().
Основные параметры для настройки:
Избыточная сила приводит к нестабильности симуляции и «выбросу» тел с огромной скоростью.
Оптимальный диапазон силы зависит от масштаба мира и массы объектов.
function explode(world, center, radius, force) {
const numObjects = world.getNumCollisionObjects();
for (let i = 0; i < numObjects; i++) {
const obj = world.getCollisionObjectArray().at(i);
const body = Ammo.castObject(obj, Ammo.btRigidBody);
if (!body || body.isStaticObject()) continue;
const transform = new Ammo.btTransform();
body.getMotionState().getWorldTransform(transform);
const pos = transform.getOrigin();
const dx = pos.x() - center.x();
const dy = pos.y() - center.y();
const dz = pos.z() - center.z();
const distance = Math.sqrt(dx*dx + dy*dy + dz*dz);
if (distance > radius || distance === 0) {
Ammo.destroy(transform);
continue;
}
const nx = dx / distance;
const ny = dy / distance;
const nz = dz / distance;
const strength = force * (1 - distance / radius);
const impulse = new Ammo.btVector3(
nx * strength,
ny * strength,
nz * strength
);
body.activate();
body.applyCentralImpulse(impulse);
Ammo.destroy(impulse);
Ammo.destroy(transform);
}
}
Такая реализация создаёт управляемый, масштабируемый и физически корректный эффект взрыва с радиальным импульсом в среде Ammo.js.