В Cannon.js физический движок работает независимо от рендеринга. Основной принцип построен на разделении физического шага и визуального обновления, что позволяет добиться стабильности симуляции независимо от частоты кадров.
Функция requestAnimationFrame используется для
организации визуального цикла. Она вызывает указанный
колбэк перед следующим перерисовыванием экрана, синхронизируясь с
частотой обновления дисплея (обычно 60 FPS). Пример базового цикла:
function renderLoop() {
requestAnimationFrame(renderLoop);
renderer.render(scene, camera);
}
renderLoop();
Главное свойство requestAnimationFrame —
адаптивность к производительности: если браузер не
успевает отрисовать кадр, вызов будет отложен, без перегрузки
процессора.
Cannon.js управляет физикой через объект World. Для
прогонки симуляции используется метод world.step:
const fixedTimeStep = 1.0 / 60.0; // фиксированный шаг 60 Гц
const maxSubSteps = 3; // максимум подшагов для компенсации просроченных кадров
world.step(fixedTimeStep, deltaTime, maxSubSteps);
Параметры:
fixedTimeStep — фиксированная длительность шага
симуляции (секунды). Обычно 1/60.deltaTime — фактическое время, прошедшее с предыдущего
кадра.maxSubSteps — ограничение числа подшагов, если
deltaTime больше фиксированного шага.Физический шаг и рендеринг могут идти с разной частотой. Для стабильности рекомендуется фиксированный шаг для физики и адаптивный рендеринг:
let lastTime;
function animate(time){
requestAnimationFrame(animate);
if(!lastTime) lastTime = time;
const deltaTime = (time - lastTime) / 1000; // перевод в секунды
lastTime = time;
world.step(fixedTimeStep, deltaTime, maxSubSteps);
// обновление визуальных объектов
mesh.position.copy(body.position);
mesh.quaternion.copy(body.quaternion);
renderer.render(scene, camera);
}
animate();
Здесь deltaTime — реальное время между кадрами, а
fixedTimeStep — фиксированное значение, гарантирующее
точность физики.
Если кадр сильно задерживается, Cannon.js автоматически прогоняет несколько подшагов:
fixedTimeStep.maxSubSteps.Это особенно важно для сложных симуляций с множеством столкновений и динамических объектов.
После вызова world.step необходимо обновить
визуальные объекты:
for (let i = 0; i < bodies.length; i++) {
meshes[i].position.copy(bodies[i].position);
meshes[i].quaternion.copy(bodies[i].quaternion);
}
position — трехмерная координата тела.quaternion — ориентация тела в пространстве.Использование кватернионов обеспечивает корректное вращение без артефактов типа “Гимбл лок”.
requestAnimationFrame) может быть
меньше или больше частоты физики, важно лишь обновлять позиции объектов
после world.step.const world = new CANNON.World();
world.gravity.set(0, -9.82, 0);
const fixedTimeStep = 1.0 / 60.0;
const maxSubSteps = 5;
function animate(time) {
requestAnimationFrame(animate);
const deltaTime = (time - (animate.lastTime || time)) / 1000;
animate.lastTime = time;
world.step(fixedTimeStep, deltaTime, maxSubSteps);
for (let i = 0; i < bodies.length; i++) {
meshes[i].position.copy(bodies[i].position);
meshes[i].quaternion.copy(bodies[i].quaternion);
}
renderer.render(scene, camera);
}
animate();
В этом примере реализуется корректная синхронизация физики и визуализации, обеспечивая стабильное поведение объектов даже при просадке FPS или изменении частоты обновления дисплея.
Хотя Cannon.js предоставляет низкоуровневые инструменты, правильная организация requestAnimationFrame + world.step является ключом к плавной и точной симуляции динамических сцен в браузере.