Алгоритм определения покоящихся тел

Oimo.js — это лёгкая библиотека для симуляции физики твёрдых тел в JavaScript. Она предназначена для работы с динамическими объектами, расчёта столкновений, гравитации и инерции. Центральной частью любого физического симулятора является определение состояния объектов: движутся они или находятся в покое. Этот аспект критичен для оптимизации, корректной обработки коллизий и реалистичной физики.

Концепция покоящегося тела

Покоящееся тело (sleeping body) — объект, который временно не участвует в активных вычислениях симулятора, так как его скорость и ускорение находятся ниже заданного порога. Использование режима покоя позволяет значительно снизить нагрузку на процессор, особенно при большом количестве объектов.

В Oimo.js каждое тело (RigidBody) имеет следующие ключевые свойства для определения покоя:

  • linearVelocity — вектор линейной скорости тела.
  • angularVelocity — вектор угловой скорости.
  • sleeping — булев флаг, указывающий, находится ли тело в покое.
  • sleepTime — время, прошедшее с момента, когда тело перестало активно двигаться.

Пороговые значения и критерии сна

Для определения покоя используются пороговые значения:

const linearThreshold = 0.01;  // минимальная линейная скорость
const angularThreshold = 0.01; // минимальная угловая скорость
const sleepTimeThreshold = 60; // количество шагов симуляции для сна

Логика проверки состояния тела строится следующим образом:

  1. Если абсолютные значения компонентов linearVelocity и angularVelocity меньше соответствующих порогов.
  2. Увеличивается счётчик времени покоя sleepTime.
  3. Если sleepTime превышает sleepTimeThreshold, тело переводится в состояние сна (sleeping = true).
  4. Любое значимое воздействие (например, столкновение) сбрасывает sleepTime и выводит тело из сна.

Алгоритм определения покоя

Процесс вычисления состояния тела делится на несколько этапов:

1. Расчёт скорости

Сначала обновляются линейная и угловая скорости на основе приложенных сил и моментов:

body.linearVelocity.add(body.force.scale(body.inverseMass));
body.angularVelocity.add(body.torque.applyMatrix3(body.inverseInertia));

2. Сравнение с порогами

Вычисляются нормы скорости:

const linearSpeed = body.linearVelocity.length();
const angularSpeed = body.angularVelocity.length();

Если обе скорости меньше порогов, тело считается потенциально покоящимся.

3. Управление счётчиком сна

if (linearSpeed < linearThreshold && angularSpeed < angularThreshold) {
    body.sleepTime++;
    if (body.sleepTime >= sleepTimeThreshold) {
        body.sleeping = true;
    }
} else {
    body.sleepTime = 0;
    body.sleeping = false;
}

4. Реакция на внешние воздействия

Любое внешнее воздействие, которое изменяет скорости тела выше порогов, автоматически выводит его из сна:

body.linearVelocity.add(externalImpulse);
body.angularVelocity.add(externalTorque);
body.sleeping = false;
body.sleepTime = 0;

Особенности реализации в Oimo.js

  • Индивидуальные пороги. Для каждого тела можно задать собственные значения linearThreshold и angularThreshold, что позволяет точнее контролировать поведение мелких или тяжёлых объектов.
  • Массив активных тел. Симулятор поддерживает отдельный список активных тел, что ускоряет интеграцию и расчёт столкновений.
  • Оптимизация. Тела в состоянии сна не участвуют в расчётах коллизий и интеграции, что снижает вычислительные затраты.

Пример интеграции сна в основной цикл симуляции

function stepSimulation(world) {
    const bodies = world.bodies;
    for (let i = 0; i < bodies.length; i++) {
        const body = bodies[i];
        if (!body.sleeping) {
            body.integrate();   // обновление позиции и скорости
            body.checkSleep();  // проверка условий покоя
        }
    }
}

Метод checkSleep() внутри RigidBody инкапсулирует весь алгоритм проверки порогов и перевода в состояние сна.

Влияние на физическую точность

Использование режима сна повышает производительность, но требует аккуратного выбора порогов. Слишком высокие значения могут привести к «залипанию» объектов и визуальным артефактам, особенно для мелких и лёгких тел. Слишком низкие — увеличивают нагрузку, так как почти все объекты остаются активными.

Управление снаружи

Для динамического контроля можно принудительно разбудить тело:

body.sleeping = false;
body.sleepTime = 0;

Это используется, когда объекты сталкиваются с внешними воздействиями, анимацией или игровыми событиями.

Итоговая структура RigidBody для сна

  • Свойства: linearVelocity, angularVelocity, sleeping, sleepTime, linearThreshold, angularThreshold.
  • Методы: integrate(), applyForce(), applyTorque(), checkSleep().
  • Алгоритм: проверка порогов → увеличение счётчика → перевод в сон → реакция на внешние воздействия.

Этот подход позволяет поддерживать баланс между физической реалистичностью и производительностью симуляции в Oimo.js. Он лежит в основе управления активными объектами и эффективной обработки динамических сцен.