Cannon.js предоставляет мощный движок для симуляции физики в JavaScript, полностью отделённый от визуализации. Создание мира начинается с инициализации объекта World, который управляет всеми физическими объектами, телами и контактами.
const world = new CANNON.World();
world.gravity.set(0, -9.82, 0); // Земная гравитация по оси Y
world.broadphase = new CANNON.NaiveBroadphase();
world.solver.iterations = 10;
NaiveBroadphase,
для больших — SAPBroadphase.Физические объекты в Cannon.js разделены на Body и Shape. Body отвечает за массу, положение и динамику, Shape — за форму для столкновений.
const sphereShape = new CANNON.Sphere(1); // радиус 1
const sphereBody = new CANNON.Body({
mass: 5,
position: new CANNON.Vec3(0, 10, 0),
shape: sphereShape
});
world.addBody(sphereBody);
const groundShape = new CANNON.Plane();
const groundBody = new CANNON.Body({
mass: 0,
shape: groundShape
});
groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0);
world.addBody(groundBody);
0 делает тело статическим, оно
не двигается под действием сил.setFromEuler задаёт ориентацию плоскости в
пространстве.Cannon.js не требует визуализации для тестирования. Основной метод — step, который шаг за шагом продвигает мир вперёд во времени.
const timeStep = 1.0 / 60.0; // 60 FPS
for (let i = 0; i < 300; i++) { // симуляция 5 секунд
world.step(timeStep);
console.log(`Время: ${(i+1)*timeStep}s, Позиция шара:`, sphereBody.position);
}
step(dt) обновляет физический мир на dt
секунд.position, velocity и
quaternion.Cannon.js позволяет отслеживать коллизии между телами без графики. Для этого используется событие collide.
sphereBody.addEventListener('collide', function(event) {
console.log('Столкновение с телом:', event.body.id, 'Сила удара:', event.contact.getImpactVelocityAlongNormal());
});
event.body — тело, с которым произошло
столкновение.contact.getImpactVelocityAlongNormal() возвращает
величину удара по нормали контакта.Для тестирования реакции тел можно добавлять силы и импульсы напрямую.
// Сила постоянного действия
const force = new CANNON.Vec3(0, 100, 0);
sphereBody.applyForce(force, sphereBody.position);
// Мгновенный импульс
const impulse = new CANNON.Vec3(50, 0, 0);
sphereBody.applyImpulse(impulse, sphereBody.position);
applyForce(force, point) — добавляет силу в указанной
точке, учитывая момент силы.applyImpulse(impulse, point) — мгновенно изменяет
скорость тела.Для больших сцен удобно управлять телами через массивы и циклы, что упрощает тестирование сценариев.
const boxes = [];
for (let i = 0; i < 10; i++) {
const box = new CANNON.Body({
mass: 1,
shape: new CANNON.Box(new CANNON.Vec3(1,1,1)),
position: new CANNON.Vec3(i*2, 10, 0)
});
world.addBody(box);
boxes.push(box);
}
// Симуляция и проверка всех коробок
for (let i = 0; i < 600; i++) {
world.step(timeStep);
boxes.forEach((box, idx) => {
if (box.position.y < 0) console.log(`Коробка ${idx} упала`);
});
}
Для анализа поведения тел без визуализации можно вычислять скорость, ускорение и энергию:
boxes.forEach(box => {
const kineticEnergy = 0.5 * box.mass * box.velocity.lengthSquared();
console.log(`Кинетическая энергия: ${kineticEnergy.toFixed(2)}`);
});
velocity.lengthSquared() возвращает квадрат скорости,
что упрощает вычисление кинетической энергии.mass * g * height.Cannon.js легко интегрируется с математическими и тестовыми библиотеками, такими как Mocha, Jest или Chai, для автоматизированного тестирования сценариев:
import { expect } from 'chai';
world.step(timeStep);
expect(sphereBody.position.y).to.be.closeTo(9.83, 0.01);
Даже без визуализации Cannon.js поддерживает:
const hinge = new CANNON.HingeConstraint(boxes[0], boxes[1], {
pivotA: new CANNON.Vec3(1,0,0),
axisA: new CANNON.Vec3(0,0,1)
});
world.addConstraint(hinge);
Для ускорения симуляции без рендерера:
timeStep для быстрых сценариев,
сохраняя стабильность.solver.iterations при малых массах и
небольших скоростях.Эти приёмы позволяют эффективно тестировать физику, выявлять ошибки логики, проверять стабильность алгоритмов и оценивать взаимодействия между объектами полностью без визуальной нагрузки.