При создании сложных физически интерактивных сцен на основе Oimo.js часто возникает необходимость работать с сотнями и тысячами объектов одновременно. Простое создание новых тел каждый раз приводит к резкому снижению производительности, так как выделение памяти и инициализация физического тела — ресурсоёмкая операция. Решением становится пулинг объектов и их переиспользование.
Пулинг объектов заключается в предварительном создании фиксированного числа тел и хранении их в очереди доступных объектов. Когда тело требуется в сцене, оно берётся из пула, настраивается для текущей задачи, а после завершения использования возвращается в пул.
Основные преимущества пулинга:
Простейшая структура пула:
class ObjectPool {
constructor(createFunc, size = 100) {
this.pool = [];
this.createFunc = createFunc;
for (let i = 0; i < size; i++) {
this.pool.push(createFunc());
}
}
acquire() {
return this.pool.length > 0 ? this.pool.pop() : this.createFunc();
}
release(obj) {
this.pool.push(obj);
}
}
Здесь createFunc — функция создания нового тела Oimo.js,
а size — начальный размер пула. Метод
acquire() берёт объект из пула, а release()
возвращает его обратно.
При работе с физическими телами важно учитывать тип тела и его параметры. Пример функции для создания кубического тела:
function createBoxBody(world) {
const body = new OIMO.Body({
type: "dynamic",
size: [1, 1, 1],
pos: [0, 0, 0],
move: true
});
world.addBody(body);
body.sleep(); // Изначально тело в "сне"
return body;
}
Использование sleep() позволяет телу не участвовать в
расчётах до момента активации, что экономит ресурсы.
При извлечении тела из пула нужно:
awake().function activateBody(body, position, rotation) {
body.setPosition(position);
body.setQuaternion(rotation);
body.awake();
}
После использования тело можно деактивировать и вернуть в пул:
function deactivateBody(pool, body) {
body.sleep();
pool.release(body);
}
Для сцен с большим числом однотипных объектов создаётся общий пул, из которого берутся тела по мере необходимости. Например, пул для пуль в шутере:
const bulletPool = new ObjectPool(() => createBoxBody(world), 200);
function spawnBullet(position, velocity) {
const bullet = bulletPool.acquire();
activateBody(bullet, position, new OIMO.Quat());
bullet.setLinearVelocity(velocity);
activeBullets.push(bullet);
}
function updateBullets(deltaTime) {
for (let i = activeBullets.length - 1; i >= 0; i--) {
const bullet = activeBullets[i];
if (bullet.getPosition()[1] < -10) { // за пределами сцены
deactivateBody(bulletPool, bullet);
activeBullets.splice(i, 1);
}
}
}
Такой подход позволяет поддерживать десятки или сотни активных объектов, не создавая новые тела каждый кадр.
Для более сложных объектов можно создать пул для каждого типа тела:
куб, сфера, цилиндр и т.д. Для динамического изменения формы
используется метод setShape(), но стоит помнить, что частая
смена формы может быть дороже, чем отдельный пул для каждого типа.
const spherePool = new ObjectPool(() => {
const body = new OIMO.Body({
type: "dynamic",
size: [1, 1, 1],
shape: "sphere",
move: true
});
world.addBody(body);
body.sleep();
return body;
}, 100);
sleep() и awake()
экономит вычислительные ресурсы.move и sleep для контроля нагрузки на
движок.Эти методы позволяют масштабировать сцену с тысячами динамических объектов и сохранять плавность симуляции при высоких нагрузках.