Первый рабочий пример за пять минут

Oimo.js — физический движок для JavaScript, ориентированный на 3D-симуляции твёрдых тел в браузере. Он реализует расчёт столкновений, гравитацию, импульсы и интеграцию движения. Библиотека хорошо подходит для связки с WebGL-рендерами, например с Three.js.

Минимальный рабочий пример требует:

  1. Подключения Oimo.js.
  2. Создания физического мира.
  3. Добавления тел.
  4. Запуска шага симуляции.
  5. (Опционально) синхронизации с графическим движком.

Подключение через CDN:

<script src="https://cdn.jsdelivr.net/npm/oimo@1.0.9/build/oimo.min.js"></script>

После загрузки становится доступен глобальный объект OIMO.


Создание физического мира

Физический мир (World) — центральный объект движка. Он управляет:

  • гравитацией
  • шагом интеграции
  • обнаружением столкновений
  • разрешением контактов

Базовая инициализация:

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

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

  • timestep — шаг симуляции (обычно 1/60).
  • iterations — количество итераций решателя столкновений.
  • broadphase — алгоритм грубой фазы (2 — оптимальный для большинства задач).
  • gravity — вектор гравитации.

Гравитация задаётся в виде массива [x, y, z].


Добавление тел в мир

В Oimo.js все объекты являются физическими телами (RigidBody). Тела бывают:

  • static — неподвижные
  • dynamic — подверженные силам
  • kinematic — управляемые вручную

Создание статической поверхности (пола)

const ground = world.add({
    type: "box",
    size: [10, 1, 10],
    pos: [0, -0.5, 0],
    move: false
});

Параметр move: false делает тело статическим.

Создание динамического куба

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

Здесь:

  • type — форма (box, sphere, cylinder)
  • size — размеры
  • pos — начальная позиция
  • density — плотность (влияет на массу)

После добавления объект автоматически участвует в симуляции.


Запуск шага симуляции

Для расчёта физики необходимо регулярно вызывать world.step().

Минимальный цикл:

function upd ate() {
    world.step();

    const position = box.getPosition();
    const rotation = box.getQuaternion();

    console.log(position);

    requestAnimationFrame(upd ate);
}

update();

Что происходит в step()

  1. Интеграция скоростей.
  2. Обнаружение столкновений.
  3. Разрешение контактов.
  4. Обновление позиций.

Без вызова step() тела не будут двигаться.


Получение позиции и вращения

После шага симуляции можно получить актуальные данные:

const position = box.getPosition();
const quaternion = box.getQuaternion();

Методы возвращают объекты с полями:

  • position.x, position.y, position.z
  • quaternion.x, quaternion.y, quaternion.z, quaternion.w

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


Связка с Three.js

На практике Oimo.js используется вместе с Three.js.

Пример синхронизации

Создание визуального куба:

const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshNormalMaterial();
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

В цикле обновления:

function update() {
    world.step();

    const pos = box.getPosition();
    const quat = box.getQuaternion();

    mesh.position.se t(pos.x, pos.y, pos.z);
    mesh.quaternion.se t(quat.x, quat.y, quat.z, quat.w);

    renderer.render(scene, camera);
    requestAnimationFrame(update);
}

Таким образом:

  • Oimo.js отвечает за физику.
  • Three.js отвечает за визуализацию.

Полный минимальный пример

HTML-структура:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Oimo Example</title>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/oimo@1.0.9/build/oimo.min.js"></script>
<script>
const world = new OIMO.World({
    timestep: 1/60,
    gravity: [0, -9.8, 0]
});

world.add({
    type: "box",
    size: [10, 1, 10],
    pos: [0, -0.5, 0],
    move: false
});

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

function update() {
    world.step();

    const pos = box.getPosition();
    console.log("Y:", pos.y);

    requestAnimationFrame(update);
}

update();
</script>
</body>
</html>

После запуска куб падает под действием гравитации и останавливается при столкновении с поверхностью.


Разбор происходящего

1. Создан мир с гравитацией

Все динамические тела получают ускорение по оси Y.

2. Добавлена статическая поверхность

Она участвует в столкновениях, но не двигается.

3. Добавлен динамический объект

Его масса вычисляется из плотности и размеров.

4. Запущен цикл симуляции

Каждый кадр пересчитывает физику.


Типичные ошибки при первом запуске

Отсутствие вызова world.step() Физика не обновляется.

Неправильный тип тела Если move: false, объект не будет падать.

Слишком большой timestep Приводит к нестабильности и «протеканию» объектов.

Отсутствие плотности Может привести к нулевой массе динамического тела.


Минимальная архитектура проекта

Базовая структура:

index.html
js/
  physics.js
  render.js
  • physics.js — создание мира и тел.
  • render.js — визуализация.
  • главный цикл объединяет оба модуля.

Разделение логики физики и рендера упрощает масштабирование проекта.


Что уже реализовано в первом примере

  • Гравитация
  • Обнаружение столкновений
  • Реакция на столкновения
  • Интеграция движения
  • Получение трансформаций тел

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

Следующий шаг обычно включает добавление нескольких тел, эксперименты с плотностью, трением и упругостью, а также использование различных типов форм (sphere, cylinder).