Вспомогательный цикл: requestAnimationFrame и физический шаг

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

requestAnimationFrame

Функция requestAnimationFrame используется для организации визуального цикла. Она вызывает указанный колбэк перед следующим перерисовыванием экрана, синхронизируясь с частотой обновления дисплея (обычно 60 FPS). Пример базового цикла:

function renderLoop() {
    requestAnimationFrame(renderLoop);
    renderer.render(scene, camera);
}
renderLoop();

Главное свойство requestAnimationFrameадаптивность к производительности: если браузер не успевает отрисовать кадр, вызов будет отложен, без перегрузки процессора.

Физический шаг (world.step)

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 — ориентация тела в пространстве.

Использование кватернионов обеспечивает корректное вращение без артефактов типа “Гимбл лок”.

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

  • Фиксированный шаг должен быть достаточно малым для точности, но не слишком маленьким, чтобы не перегружать процессор.
  • maxSubSteps позволяет компенсировать пропуски кадров, избегая пропусков коллизий.
  • Визуальная частота (requestAnimationFrame) может быть меньше или больше частоты физики, важно лишь обновлять позиции объектов после world.step.

Пример интеграции с Three.js

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 является ключом к плавной и точной симуляции динамических сцен в браузере.