Класс-обёртка для пары Body–Mesh

В библиотеке Cannon.js для имитации физики 3D-объектов используется класс Body, который отвечает за физическое поведение объектов: массу, инерцию, силы и столкновения. Для визуализации этих объектов в сцене обычно применяется Three.js или аналогичная графическая библиотека. Связка Body–Mesh обеспечивает синхронизацию физики и визуальной модели, создавая удобный интерфейс для работы с динамическими объектами.


Основные принципы работы Body и Mesh

  • Body — это объект физического мира. У него есть свойства:

    • mass — масса тела.
    • position — текущее положение в пространстве.
    • quaternion — ориентация в виде кватерниона.
    • velocity и angularVelocity — линейная и угловая скорость.
    • shape — геометрическая форма для столкновений (Box, Sphere, Plane, ConvexPolyhedron и др.).
  • Mesh — визуальное представление объекта. В Three.js это THREE.Mesh с геометрией и материалом. Mesh не участвует в расчетах физики, он лишь отображает состояние Body.

Ключевой момент: Body является источником истины для позиции и ориентации объекта, Mesh просто повторяет эти параметры.


Создание класса-обёртки

Класс-обёртка объединяет Body и Mesh, предоставляя единый интерфейс для управления объектом.

class PhysicsObject {
    constructor(mesh, shape, mass = 1) {
        this.mesh = mesh;

        this.body = new CANNON.Body({
            mass: mass,
            shape: shape,
            position: new CANNON.Vec3(mesh.position.x, mesh.position.y, mesh.position.z)
        });
    }

    updateMesh() {
        // Обновление позиции и ориентации Mesh на основе Body
        this.mesh.position.copy(this.body.position);
        this.mesh.quaternion.copy(this.body.quaternion);
    }

    applyForce(forceVector, relativePoint) {
        this.body.applyForce(
            new CANNON.Vec3(forceVector.x, forceVector.y, forceVector.z),
            new CANNON.Vec3(relativePoint.x, relativePoint.y, relativePoint.z)
        );
    }

    applyImpulse(impulseVector, relativePoint) {
        this.body.applyImpulse(
            new CANNON.Vec3(impulseVector.x, impulseVector.y, impulseVector.z),
            new CANNON.Vec3(relativePoint.x, relativePoint.y, relativePoint.z)
        );
    }
}

Особенности класса:

  1. Синхронизация — метод updateMesh должен вызываться на каждом кадре после шага симуляции Cannon.js.
  2. Гибкость — методы applyForce и applyImpulse позволяют реализовать взаимодействие с телами динамически.
  3. Независимость от визуализации — класс не привязан к конкретной графической библиотеке, важно лишь, чтобы Mesh имел position и quaternion.

Инициализация и добавление в мир

const sphereGeometry = new THREE.SphereGeometry(1, 32, 32);
const sphereMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 });
const sphereMesh = new THREE.Mesh(sphereGeometry, sphereMaterial);

const sphereShape = new CANNON.Sphere(1);
const physicsSphere = new PhysicsObject(sphereMesh, sphereShape, 5);

world.addBody(physicsSphere.body);
scene.add(physicsSphere.mesh);

После этого объект начинает взаимодействовать с другими телами и гравитацией в Cannon.js, а визуальная часть отображает текущее состояние физики.


Обновление на каждом кадре

Для корректной работы необходимо синхронизировать Mesh с Body после шага физического мира:

function animate() {
    requestAnimationFrame(animate);

    // Шаг физического мира
    world.step(1/60);

    // Синхронизация Mesh с Body
    physicsSphere.updateMesh();

    renderer.render(scene, camera);
}

animate();

Расширенные возможности

  1. Создание составных объектов: класс-обёртка может содержать несколько Body–Mesh пар, объединённых в одну группу, с управлением через общий интерфейс.
  2. Добавление колбэков столкновений: можно подписываться на события body.addEventListener('collide', callback) и обрабатывать физические контакты.
  3. Динамическая смена формы: замена body.shape позволяет изменять физические свойства без пересоздания объекта, важно лишь обновить инерцию через body.updateMassProperties().

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

  • Всегда создавать совпадающие геометрии Body и Mesh по размеру, иначе столкновения и визуализация будут рассинхронизированы.
  • Для объектов с нулевой массой (mass = 0) Body становится статическим и не подвержен силам.
  • Для точной симуляции мелких объектов использовать меньшие шаги мира: world.step(fixedTimeStep, deltaTime, maxSubSteps).

Пример расширенной обёртки с группой тел

class PhysicsGroup {
    constructor() {
        this.objects = [];
    }

    add(mesh, shape, mass) {
        const obj = new PhysicsObject(mesh, shape, mass);
        this.objects.push(obj);
        world.addBody(obj.body);
        scene.add(obj.mesh);
        return obj;
    }

    update() {
        this.objects.forEach(obj => obj.updateMesh());
    }
}

Использование PhysicsGroup упрощает управление множеством динамических объектов и их обновление на каждом кадре.


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