Oimo.js строится вокруг концепции World — контейнера для всех физических объектов и взаимодействий. Основная роль мира — управлять шагами симуляции, глобальными параметрами гравитации и временем.
const world = new OIMO.World({
timestep: 1/60, // шаг симуляции в секундах
iterations: 8, // количество итераций для решения ограничений
broadphase: 2, // алгоритм broadphase: 1 - brute force, 2 - sweep & prune
gravity: [0, -9.8, 0] // вектор гравитации
});
Ключевые параметры:
timestep — определяет точность и стабильность
симуляции; слишком большой шаг приведет к нестабильности.iterations — количество итераций для решения
ограничений; большее число повышает точность столкновений, но снижает
производительность.broadphase — алгоритм предварительного определения
потенциальных столкновений.В Oimo.js физические объекты представлены через RigidBody. Тела бывают dynamic, static и kinematic.
const box = world.add({
type: 'box',
size: [1, 1, 1],
pos: [0, 5, 0],
rot: [0, 0, 0],
move: true, // true для dynamic, false для static
density: 1
});
Основные параметры:
size — размеры в метрах по осям X, Y, Z.pos — начальная позиция.rot — угол вращения в радианах.move — определяет динамическое поведение.density — плотность, влияющая на массу тела.Для sphere или cylinder параметры аналогичны, меняется только геометрическая форма.
Oimo.js позволяет отслеживать столкновения через контакты. Каждое тело хранит массив текущих контактов:
world.step();
box.contacts.forEach(contact => {
console.log('Контакт с телом', contact.body.name);
});
Для оптимизации можно использовать callback функции при начале и окончании контакта:
box.onCollisionEn ter = function(other) {
console.log('Начало столкновения с', other.name);
};
box.onCollisionE xit = function(other) {
console.log('Окончание столкновения с', other.name);
};
Для реагирования на нажатия клавиш применяют события
keydown и keyup. Основная задача — изменить
физические параметры тела: скорость или силу.
const velocity = new OIMO.Vec3();
document.addEventListener('keydown', (e) => {
switch(e.code) {
case 'ArrowUp': velocity.z = -5; break;
case 'ArrowDown': velocity.z = 5; break;
case 'ArrowLeft': velocity.x = -5; break;
case 'ArrowRight': velocity.x = 5; break;
}
box.linearVelocity = velocity;
});
document.addEventListener('keyup', (e) => {
velocity.set(0, 0, 0);
box.linearVelocity = velocity;
});
Ключевой момент: Oimo.js использует linearVelocity и angularVelocity, а не прямое изменение позиции. Это обеспечивает корректное взаимодействие с физикой.
Для более реалистичного движения используют методы
applyImpulse и applyForce:
const impulse = new OIMO.Vec3(0, 10, 0);
box.applyImpulse(impulse);
applyForce действует постепенно, учитывая массу.applyImpulse мгновенно изменяет скорость тела.Такой подход полезен для прыжков, толчков или мгновенных эффектов от столкновений.
Физика работает отдельно от рендеринга. После каждого шага симуляции нужно обновлять объекты на сцене:
function animate() {
requestAnimationFrame(animate);
world.step();
mesh.position.set(box.position.x, box.position.y, box.position.z);
mesh.quaternion.set(box.rotation.x, box.rotation.y, box.rotation.z, box.rotation.w);
renderer.render(scene, camera);
}
animate();
Принцип: перемещение визуального объекта следует линейной и угловой скорости тела, а не изменению позиции вручную.
Для управления несколькими телами можно хранить их в массиве и применять одинаковую обработку ввода:
const objects = [box1, box2, box3];
document.addEventListener('keydown', e => {
objects.forEach(obj => {
if(e.code === 'ArrowUp') obj.linearVelocity.z = -5;
});
});
Такой подход позволяет реализовать управление несколькими объектами или синхронизацию действий между ними.
Oimo.js поддерживает Joints для соединения тел:
const hinge = world.add({
type: 'joint',
jointType: 'hinge',
body1: box1,
body2: box2,
pos1: [0, 0.5, 0],
pos2: [0, -0.5, 0],
axis: [0, 0, 1]
});
Joints интегрируются с физикой так же, как и силы, поэтому пользовательский ввод может управлять не только отдельными телами, но и их связями.
Все изменения тел, вызванные пользователем, следует делать в основном потоке перед шагом симуляции. Это обеспечивает корректное взаимодействие между физикой и визуализацией.
function mainLoop() {
handleInput(); // изменение velocity, applyImpulse
world.step();
updateMeshes();
requestAnimationFrame(mainLoop);
}
mainLoop();
Суть: любое вмешательство в физику вне основного цикла может привести к расхождению симуляции и визуализации, поэтому строго соблюдается порядок:
Oimo.js позволяет реализовать полноценное взаимодействие пользователя
с физическим миром через linearVelocity,
angularVelocity, силы и импульсы. Управление должно
быть синхронизировано с шагами симуляции, а все визуальные объекты —
соответствовать состоянию физических тел. Использование событий
клавиатуры, мыши или сенсоров возможно при условии обновления параметров
тел только в основном потоке перед world.step().