Динамическое удаление далёких тел

Cannon.js — это физический движок для JavaScript, обеспечивающий реалистичное моделирование тел в 3D-пространстве. Он поддерживает твердотельную динамику, коллизии, силы, трение и инерцию. Каждое тело в сцене представлено объектом типа Body, который имеет массу, форму (Shape), положение (position) и ориентацию (quaternion). Для управления симуляцией используется объект World, отвечающий за интеграцию законов движения и обработку столкновений.

const world = new CANNON.World();
world.gravity.set(0, -9.82, 0); // Земная гравитация
world.broadphase = new CANNON.NaiveBroadphase();
world.solver.iterations = 10;

Создание тел и форм

Каждое физическое тело состоит из массы, формы и позиции. Формы (Shape) определяют коллизионные границы: Box, Sphere, Cylinder, Plane, ConvexPolyhedron и другие.

const shape = new CANNON.Sphere(1); // Сфера радиусом 1
const body = new CANNON.Body({
    mass: 5,
    shape: shape,
    position: new CANNON.Vec3(0, 10, 0)
});
world.addBody(body);

Важно помнить, что форма тела не всегда совпадает с визуальным представлением. Для синхронизации с графикой необходимо отдельно обновлять трехмерный объект, например, в Three.js.

Механизм интеграции и обновления

Cannon.js обновляет симуляцию с помощью метода world.step(timeStep), где timeStep задаёт интервал времени для расчёта физических взаимодействий.

const timeStep = 1/60; // 60 FPS
function updatePhysics() {
    world.step(timeStep);
}

После вызова step необходимо обновлять позиции и ориентации визуальных объектов:

mesh.position.copy(body.position);
mesh.quaternion.copy(body.quaternion);

Динамическое удаление тел

При создании больших сцен с множеством объектов возникает необходимость удалять тела, которые находятся далеко от камеры или игрока, чтобы оптимизировать производительность. Основная стратегия — проверка дистанции между телом и точкой интереса и удаление при превышении порогового значения.

Вычисление дистанции

function distance(a, b) {
    const dx = a.x - b.x;
    const dy = a.y - b.y;
    const dz = a.z - b.z;
    return Math.sqrt(dx*dx + dy*dy + dz*dz);
}

Определение порога и удаление

const removalDistance = 50;

function removeDistantBodies(referencePoint) {
    for (let i = world.bodies.length - 1; i >= 0; i--) {
        const body = world.bodies[i];
        if (distance(body.position, referencePoint) > removalDistance) {
            world.removeBody(body);
        }
    }
}

Использование цикла в обратном порядке предотвращает пропуск элементов при удалении из массива world.bodies.

Оптимизация удаления

  1. Интервалы проверки: нет необходимости проверять все тела каждый кадр. Достаточно делать проверку раз в несколько шагов симуляции.
  2. Группировка объектов: объединение удаляемых тел в массивы или списки позволяет быстро и безопасно очищать сцены.
  3. Визуальные объекты: вместе с телом необходимо удалять или скрывать связанные трехмерные меши, чтобы не оставались «призраки» в графике.
function removeDistantBodiesWithMeshes(referencePoint) {
    for (let i = world.bodies.length - 1; i >= 0; i--) {
        const body = world.bodies[i];
        if (distance(body.position, referencePoint) > removalDistance) {
            scene.remove(body.mesh); // удаление визуала
            world.removeBody(body);
        }
    }
}

Управление большим количеством объектов

Для сцен с тысячами тел критически важно минимизировать накладные расходы на цикл проверки и удаление:

  • Использование квадродеревьев или октодеревьев для пространственной сегментации позволяет быстро находить объекты за пределами зоны интереса.
  • Ведение списка активных тел уменьшает количество проверок, обходя только те объекты, которые реально могут выйти за границы.
  • Избегать частого создания и удаления тел: при возможности использовать пул объектов, перезаписывая состояние вместо уничтожения.

Влияние удаления на физику

Удаление тел влияет на симуляцию:

  • Коллизии с удалёнными объектами больше не учитываются.
  • Если удаляется тело с соединениями (Constraint), необходимо также удалять связанные ограничения.
if (body.constraint) {
    world.removeConstraint(body.constraint);
}
world.removeBody(body);

Практические рекомендации

  • Использовать динамический порог, зависящий от скорости камеры или объекта, чтобы предсказывать необходимость удаления заранее.
  • Сохранять стабильность симуляции: резкое исчезновение большого числа тел может вызвать визуальные артефакты.
  • Объединять методы удаления с LOD для графики, снижая качество деталей у дальних объектов вместо полного удаления, когда это возможно.

Эти подходы позволяют поддерживать высокую производительность Cannon.js даже в сложных сценах с тысячами объектов, сохраняя точность физической симуляции и согласованность визуального представления.