Переход с Cannon.js на Cannon-es

Cannon.js — популярная библиотека физики для браузера, широко использовавшаяся в сочетании с WebGL и Three.js. Однако её развитие остановилось, а современная альтернатива — Cannon-es — представляет собой модульную версию Cannon.js с поддержкой ES6 и улучшенной совместимостью с современными сборщиками. Переход на Cannon-es позволяет использовать импорт через модули, улучшает поддержку TypeScript и делает код более структурированным.


Установка и импорт

В Cannon.js обычно использовался глобальный объект CANNON. В Cannon-es применяется модульная структура ES6:

// Cannon.js
<script src="cannon.js"></script>
var world = new CANNON.World();

// Cannon-es
import * as CANNON from 'cannon-es';
const world = new CANNON.World();

Ключевые изменения:

  • Отсутствие глобального объекта: всё импортируется из пакета.
  • Поддержка Tree Shaking: можно импортировать только нужные классы.
  • Совместимость с современными сборщиками (Webpack, Vite, Rollup).

Создание мира и базовые настройки

В Cannon.js создание мира выглядело так:

const world = new CANNON.World();
world.gravity.set(0, -9.82, 0);
world.broadphase = new CANNON.NaiveBroadphase();
world.solver.iterations = 10;

В Cannon-es синтаксис практически идентичен:

import { World, NaiveBroadphase, Vec3 } from 'cannon-es';

const world = new World();
world.gravity.set(0, -9.82, 0);
world.broadphase = new NaiveBroadphase();
world.solver.iterations = 10;

Особенности:

  • Все вспомогательные объекты (например, Vec3) теперь импортируются отдельно.
  • Методы addBody и removeBody остаются без изменений.
  • Поддержка современных возможностей JavaScript позволяет создавать конфигурации динамически и использовать классы для расширения.

Работа с телами и формами

Cannon-es полностью сохраняет структуру классов Body, Shape, Box, Sphere, но изменяет способ импорта:

import { Body, Box, Sphere, Vec3 } from 'cannon-es';

// Создание тела с кубической формой
const boxShape = new Box(new Vec3(1, 1, 1));
const boxBody = new Body({
    mass: 5,
    position: new Vec3(0, 10, 0),
    shape: boxShape
});

world.addBody(boxBody);

// Создание сферического тела
const sphereShape = new Sphere(1);
const sphereBody = new Body({
    mass: 2,
    position: new Vec3(2, 10, 0),
    shape: sphereShape
});

world.addBody(sphereBody);

Изменения по сравнению с Cannon.js:

  • Все классы теперь строго импортируются.
  • Можно использовать ES6 синтаксис для наследования, например:
class CustomBody extends Body {
    constructor(options) {
        super(options);
        // дополнительные свойства
    }
}

Материалы и контактные свойства

В Cannon-es полностью сохранён функционал материалов и контактных свойств:

import { Material, ContactMaterial } from 'cannon-es';

const groundMaterial = new Material('ground');
const boxMaterial = new Material('box');

const contactMaterial = new ContactMaterial(groundMaterial, boxMaterial, {
    friction: 0.4,
    restitution: 0.3
});

world.addContactMaterial(contactMaterial);

Особенности:

  • Конструкторы не изменились.
  • Поддерживается множество контактных материалов.
  • Методы addContactMaterial и removeContactMaterial работают как и раньше.

Симуляция и шаг времени

Шаг времени в Cannon-es аналогичен Cannon.js, но рекомендуется использовать фиксированный шаг с переменной дельтой для более стабильной симуляции:

const timeStep = 1.0 / 60.0; // 60 FPS

function update(deltaTime) {
    world.step(timeStep, deltaTime);
}

Изменения:

  • Cannon-es корректно обрабатывает частичные шаги времени (deltaTime) для плавного движения.
  • Можно интегрировать с requestAnimationFrame и Three.js синхронно:
function animate(time) {
    requestAnimationFrame(animate);
    const deltaTime = (time - lastTime) / 1000;
    world.step(timeStep, deltaTime);
    lastTime = time;
    // обновление визуализации
}
let lastTime = performance.now();
animate(lastTime);

Коллизии и события

Cannon-es поддерживает события collide, collideBegin, collideEnd:

boxBody.addEventListener('collide', (event) => {
    console.log('Box столкнулся с', event.body);
});

Отличия от Cannon.js:

  • События обрабатываются через стандартный метод addEventListener, аналогичный DOM.
  • event.contact предоставляет доступ к объекту контакта с подробными данными (позиция, нормаль, сила).

Совместимость с Three.js

Cannon-es идеально интегрируется с Three.js. Основная схема синхронизации:

mesh.position.copy(body.position);
mesh.quaternion.copy(body.quaternion);
  • Vec3 и Quaternion Cannon-es легко конвертируются в объекты Three.js (THREE.Vector3, THREE.Quaternion) через методы copy.
  • Можно использовать один общий цикл анимации для визуализации и физики.

Различия при миграции с Cannon.js

  1. Модули ES6 — отказ от глобального объекта CANNON.
  2. Строгий импорт классов — нужно явно указывать, какие классы используются.
  3. Поддержка Tree Shaking — уменьшает размер бандла.
  4. События через addEventListener — единый подход к обработке столкновений.
  5. Полная совместимость с современными сборщиками и TypeScript — интерфейсы и типы доступны прямо из пакета.
  6. Небольшие изменения в производительности — более стабильная интеграция шагов времени и расчётов контактов.

Советы по миграции

  • Проверить все глобальные вызовы CANNON.* и заменить на импортируемые классы.
  • Обновить систему импорта в сборщиках (Webpack, Vite) для поддержки ES6 модулей.
  • Использовать фиксированные шаги времени для стабильной симуляции.
  • При необходимости переписать обработку событий с .addEventListener.
  • Проверить материалы и контактные свойства, чтобы убедиться в идентичном поведении физики.

Переход на Cannon-es позволяет сохранить весь функционал Cannon.js, при этом использовать современные возможности JavaScript, улучшенную модульность и совместимость с современными инструментами разработки.