В библиотеке Oimo.js взрыв моделируется как мгновенное или кратковременное воздействие импульса на группу тел в заданной области пространства. Основная задача — вычислить величину импульса в зависимости от расстояния до центра взрыва и корректно применить его к физическим телам.
Взрывной импульс отличается от постоянной силы:
В Oimo.js импульс применяется напрямую через изменение линейной скорости или через метод добавления импульса к телу.
Пусть:
Условие действия: [ d R]
Наиболее распространённые модели затухания:
[ F(d) = F_0 (1 - )]
Преимущества:
Недостатки:
[ F(d) = ]
Где ( ) — малое число для защиты от деления на ноль.
Более физически корректная модель, имитирующая распространение энергии в пространстве.
[ t = ] [ F(d) = F_0 (1 - t)^2]
Даёт мягкое и реалистичное затухание.
В Oimo.js мир физики представлен объектом
World. Тела хранятся в списке, который можно перебрать.
Пример поиска тел:
function getBodiesInRadius(world, center, radius) {
const bodies = [];
let body = world.getBodyList();
while (body) {
const pos = body.getPosition();
const dx = pos.x - center.x;
const dy = pos.y - center.y;
const dz = pos.z - center.z;
const distanceSq = dx*dx + dy*dy + dz*dz;
if (distanceSq <= radius * radius) {
bodies.push(body);
}
body = body.getNext();
}
return bodies;
}
Использование квадрата расстояния избавляет от лишнего вычисления квадратного корня.
Импульс должен быть направлен от центра взрыва к телу.
[ = ]
Нормализация обязательна для корректной масштабируемости силы.
function computeDirection(bodyPos, explosionPos) {
const dx = bodyPos.x - explosionPos.x;
const dy = bodyPos.y - explosionPos.y;
const dz = bodyPos.z - explosionPos.z;
const length = Math.sqrt(dx*dx + dy*dy + dz*dz) || 1;
return {
x: dx / length,
y: dy / length,
z: dz / length,
distance: length
};
}
Полная схема:
function computeExplosionImpulse(distance, radius, maxForce) {
if (distance > radius) return 0;
const t = distance / radius;
return maxForce * (1 - t) * (1 - t);
}
В Oimo.js можно использовать:
applyImpulseКорректный способ:
function applyExplosion(body, explosionPos, radius, maxForce) {
const bodyPos = body.getPosition();
const result = computeDirection(bodyPos, explosionPos);
if (result.distance > radius) return;
const force = computeExplosionImpulse(
result.distance,
radius,
maxForce
);
const impulse = new OIMO.Vec3(
result.x * force,
result.y * force,
result.z * force
);
body.applyImpulse(bodyPos, impulse);
}
Импульс прикладывается в точке центра массы, если передать позицию тела.
Импульс связан с изменением скорости:
[ v = ]
В Oimo.js внутренняя система автоматически учитывает массу при применении импульса. Однако для аркадных эффектов возможно дополнительное масштабирование:
const mass = body.getMass();
const scaledForce = baseForce / Math.max(mass, 0.1);
Это позволяет лёгким объектам отлетать сильнее.
Для создания «подбрасывающего» эффекта добавляют смещение по оси Y:
impulse.y += liftFactor;
Или модифицируют направление:
result.y += 0.5;
После этого вектор нужно снова нормализовать.
Взрыв может придать телам чрезмерную скорость. Ограничение:
const maxVelocity = 50;
const velocity = body.getLinearVelocity();
if (velocity.length() > maxVelocity) {
velocity.normalize().scaleEq(maxVelocity);
body.setLinearVelocity(velocity);
}
Это предотвращает нестабильность симуляции.
Для более реалистичной анимации используют сглаживающую функцию:
[ F(d) = F_0 smoothstep(1 - t)]
Где:
function smoothstep(x) {
return x * x * (3 - 2 * x);
}
Использование сглаживания устраняет резкий обрыв силы на границе радиуса.
Если взрыв реализуется как сила в течение нескольких кадров:
[ F_{per_frame} = ]
Где Δt — шаг симуляции world.step(dt).
При малом dt сила должна быть больше, иначе эффект
ослабнет.
Для реалистичной симуляции требуется проверка, не перекрыт ли объект стеной. Используется RayCast:
Это предотвращает прохождение взрывной волны сквозь препятствия.
function explode(world, center, radius, maxForce) {
let body = world.getBodyList();
while (body) {
if (body.isStatic()) {
body = body.getNext();
continue;
}
applyExplosion(body, center, radius, maxForce);
body = body.getNext();
}
}
Такая архитектура позволяет:
Для предотвращения ошибок:
distanceSq при проверке радиусаПри слишком больших силах возможны:
Наиболее производительная схема:
Такая реализация обеспечивает стабильную работу сцены с десятками и сотнями тел без значительного падения FPS.
Расчёт взрывного импульса в Oimo.js сводится к трём ключевым этапам: корректное определение расстояния, выбор модели затухания и физически грамотное применение импульса к телу. Правильная реализация обеспечивает устойчивость симуляции, контролируемость игрового процесса и предсказуемую динамику объектов.