Управление временным шагом и интерполяция

Cannon.js использует дискретную симуляцию физики, где состояние мира обновляется шаг за шагом. Каждый шаг называется временным шагом (time step), и его величина критически влияет на точность и стабильность симуляции.

const timeStep = 1 / 60; // 60 кадров в секунду
world.step(timeStep);
  • Фиксированный шаг гарантирует предсказуемую симуляцию: объекты движутся одинаково на любой машине при одинаковых параметрах.
  • Больший шаг (например, 1/30) ускоряет вычисления, но может привести к нестабильности: тела могут “пролетать” через друг друга или дестабилизировать соединения.
  • Малый шаг повышает точность, но требует больше ресурсов.

Реализация адаптивного временного шага

Для плавного взаимодействия с разными частотами обновления экрана используется адаптивный шаг. Он позволяет разделить реальное прошедшее время на несколько фиксированных шагов симуляции:

let lastTime;
function animate(time) {
    if (!lastTime) lastTime = time;
    const dt = (time - lastTime) / 1000; // конвертация в секунды
    lastTime = time;

    const fixedTimeStep = 1 / 60;
    const maxSubSteps = 3;

    world.step(fixedTimeStep, dt, maxSubSteps);

    requestAnimationFrame(animate);
}
requestAnimationFrame(animate);

Параметры функции world.step(fixedTimeStep, dt, maxSubSteps):

  • fixedTimeStep — основной шаг симуляции.
  • dt — реальное время, прошедшее с предыдущего кадра.
  • maxSubSteps — максимальное число шагов, которые можно сделать за один кадр для компенсации задержек.

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

  • Если dt меньше fixedTimeStep, симуляция пропускает шаг.
  • Если dt больше, Cannon.js делает несколько шагов, пока не достигнет актуального времени.
  • Ограничение maxSubSteps предотвращает перегрузку CPU.

Интерполяция состояния

Поскольку симуляция и рендер работают с разными частотами, появляется разрыв между текущим состоянием объектов и кадром. Для сглаживания движения используется интерполяция.

const alpha = (dt % fixedTimeStep) / fixedTimeStep;

mesh.position.lerp(body.previousPosition, body.position, alpha);
mesh.quaternion.slerp(body.previousQuaternion, body.quaternion, alpha);
  • alpha — доля между предыдущим и текущим состоянием.
  • lerp (Linear Interpolation) — линейная интерполяция позиции.
  • slerp (Spherical Linear Interpolation) — интерполяция ориентации (кватернионов).

Эффект интерполяции:

  • Смягчает визуальные “рывки”, возникающие при пропуске кадров.
  • Позволяет использовать фиксированный шаг для стабильной симуляции, при этом рендер остаётся плавным.

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

  1. Фиксированный шаг 1/60 является оптимальным для большинства приложений.
  2. Максимум подшагов (maxSubSteps) лучше ограничивать 3–5, чтобы избежать резких скачков при падении FPS.
  3. Сохранение предыдущего состояния тела (previousPosition, previousQuaternion) обязательно для корректной интерполяции.
  4. Для объектов с высокой скоростью или сложными соединениями рекомендуется уменьшать fixedTimeStep до 1/120 или 1/240.
  5. Интерполяцию использовать только для рендера, не вмешиваясь в физические расчёты — это сохраняет консистентность симуляции.

Взаимодействие с реальным временем

Cannon.js позволяет синхронизировать физику с реальным временем, что важно для сетевых игр и VR:

  • Отставание физики компенсируется дополнительными подшагами.
  • Перепрыгивание кадров предотвращается фиксированным шагом и maxSubSteps.
  • Плавность движения обеспечивается интерполяцией на рендер-кадрах между физическими шагами.

Использование с графическими движками

При интеграции с Three.js или другими рендеринг-библиотеками:

function updateMeshFromBody(mesh, body, alpha) {
    mesh.position.copy(body.previousPosition).lerp(body.position, alpha);
    mesh.quaternion.copy(body.previousQuaternion).slerp(body.quaternion, alpha);
}
  • Обновление позиции и ориентации объекта происходит до рендера каждого кадра.
  • Сохраняется стабильность физики при любых колебаниях FPS.

Особенности соединений и сложных объектов

Для соединений (constraints) и многочастичных тел:

  • Малый шаг необходим для точного расчёта силы и момента.
  • Интерполяция работает только визуально, физика остаётся точной.
  • При увеличении maxSubSteps улучшается стабильность соединений при резких движениях.

Использование фиксированного шага, адаптивного обновления и интерполяции обеспечивает одновременно точность физической модели и плавность визуализации, что делает Cannon.js надёжным инструментом для интерактивных 3D-приложений.