Протокол обмена сообщениями: команды и обновления

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

const world = new OIMO.World({
    timestep: 1/60,
    iterations: 8,
    broadphase: 2,
    worldscale: 1,
    random: true,
    info: false
});

Параметры timestep и iterations определяют точность и стабильность симуляции. broadphase отвечает за алгоритм предварительного определения возможных столкновений. worldscale масштабирует размеры всех тел в мире, что важно при работе с мелкими объектами.

Создание тел выполняется через RigidBody. Простейший пример:

const box = world.add({
    type: "box",
    size: [1, 1, 1],
    pos: [0, 5, 0],
    move: true,
    density: 1
});

Ключевые параметры:

  • type — форма тела (box, sphere, cylinder и др.).
  • size — массив размеров вдоль осей X, Y, Z.
  • pos — начальная позиция в мире.
  • move — указывает, подвижное тело или статическое.
  • density — плотность, влияющая на массу тела.

Система команд и обновлений

Oimo.js использует структуру команд, которая позволяет управлять телами и получать их состояние после каждого шага симуляции. Все изменения тела происходят через методы set* и get* или через прямое обновление свойств объекта RigidBody.

Основные команды

  • setPosition(x, y, z) — установка позиции тела.
  • setQuaternion(x, y, z, w) — установка ориентации через кватернион.
  • setLinearVelocity(x, y, z) — установка линейной скорости.
  • setAngularVelocity(x, y, z) — установка угловой скорости.
  • applyImpulse(x, y, z, px, py, pz) — применение импульса в точке тела.

Пример применения команды импульса:

box.applyImpulse([0, 5, 0], [0, 0, 0]); // подталкивание вверх в центре

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

Обновления и шаги симуляции

Симуляция мира выполняется методом step(), который рассчитывает новое состояние всех тел:

world.step();

Метод можно вызывать с фиксированным временным шагом или с указанием delta-time:

world.step(deltaTimeInSeconds);

После вызова step() все тела имеют обновлённые позиции и ориентации, доступные через:

const pos = box.getPosition(); // [x, y, z]
const rot = box.getQuaternion(); // [x, y, z, w]

Для постоянного обновления чаще всего используется цикл:

function update() {
    world.step();
    requestAnimationFrame(update);
}
update();

События столкновений

Oimo.js позволяет отслеживать коллизии через ContactManager и отдельные события тела. Для каждого тела можно определить коллбэк на столкновение:

box.onColl ide = function(otherBody) {
    console.log("Столкновение с телом:", otherBody);
};

Эта система обеспечивает протокол обмена информацией между телами и внешней логикой.

Связи и ограничения

Oimo.js поддерживает несколько типов соединений между телами:

  • HingeJoint — шарнир с одной осью вращения.
  • SliderJoint — скользящий соединитель, ограничивающий движение по одной оси.
  • DistanceJoint — поддерживает фиксированное расстояние между телами.

Пример шарнирного соединения:

const hinge = new OIMO.HingeJoint(box1, box2, {
    pos1: [0, 1, 0],
    pos2: [0, -1, 0],
    axis1: [0, 0, 1],
    axis2: [0, 0, 1],
    collideConnected: false
});
world.addJoint(hinge);

Каждое соединение можно настраивать динамически, изменяя параметры углов, ограничения вращения и силы пружины.

Оптимизация обмена данными

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

  1. Пакетное обновление тел — применение одного цикла для всех тел.
  2. Буферы состояния — хранение позиций и ориентаций в массиве для быстрого доступа и передачи в рендерер.
  3. События только при необходимости — коллизии и контакты обрабатываются выборочно, чтобы не перегружать основной цикл.

Пример буферного подхода:

const positions = [];
for (let i = 0; i < world.rigidBodies.length; i++) {
    const b = world.rigidBodies[i];
    positions.push(b.getPosition());
}

Этот метод позволяет интегрировать Oimo.js с WebGL или Three.js, где позиции объектов обновляются один раз за кадр на основе состояния физики.

Работа с динамическими сценами

Добавление и удаление объектов в процессе симуляции происходит через методы add() и remove():

world.add({ type: "sphere", size: [1], pos: [0,10,0], move: true });
world.remove(box);

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


Эта архитектура команд и обновлений формирует протокол обмена сообщениями внутри Oimo.js: тела отправляют свои состояния миру, мир обновляет физику и передаёт новые состояния обратно, внешняя логика реагирует через события и интерфейсы управления. Такой подход обеспечивает стабильную, гибкую и производительную симуляцию физических взаимодействий в JavaScript.