Ammo.js является JavaScript-портом движка Bullet Physics, реализованного на C++ с помощью Emscripten. Он предоставляет реалистичную физическую симуляцию, включая столкновения, динамику твёрдых тел, констрейнты и мягкие тела. Для полноценного контроля над симуляцией важно понимать понятия детерминизма и воспроизводимости, так как физические расчёты часто подвержены численным погрешностям.
Детерминизм означает, что при одинаковых начальных условиях, одинаковых шагах интеграции и идентичных параметрах симуляции система выдаёт точно одинаковые результаты. В контексте Ammo.js это критично для сетевых игр, повторов и тестирования.
Ammo.js использует интегратор явного типа, основанный на методах Эйлера и Verlet, для расчёта движения твёрдых тел. Основные параметры, влияющие на воспроизводимость:
timeStep — величина шага времени в функции
stepSimulation.maxSubSteps — максимальное количество внутренних шагов
для сглаживания больших временных дельт.fixedTimeStep — фиксированное время между внутренними
шагами, определяющее частоту обновлений физики.physicsWorld.stepSimulation(deltaTime, maxSubSteps, fixedTimeStep);
При использовании нефиксированного шага времени
(deltaTime варьируется каждый кадр) результаты симуляции
могут отличаться при повторных запусках, даже при одинаковых начальных
условиях. Для гарантированного детерминизма рекомендуется использовать
фиксированный шаг времени, например, 1/60
секунды.
Каждое физическое тело в Ammo.js создаётся с помощью конструктора
btRigidBody, которому передаются:
mass — масса объекта.collisionShape — форма столкновения
(btBoxShape, btSphereShape и др.).position и rotation — начальная
трансформация.linearVelocity и angularVelocity —
начальные скорости.Для полного детерминизма необходимо задавать все параметры явно, включая скорости и позиции, а также использовать фиксированные значения рандома, если генерация случайных чисел влияет на начальное состояние:
const startPos = new Ammo.btVector3(0, 10, 0);
const startRot = new Ammo.btQuaternion(0, 0, 0, 1);
Любая случайная генерация без фиксированного seed
приведёт к различным симуляциям при повторном
запуске.
Ammo.js работает с числами с плавающей запятой двойной
точности, но в JavaScript они представляются как
Number (64-битные IEEE 754). Это накладывает
ограничения:
Для минимизации эффекта рекомендуется:
collisionMargin,
contactBreakingThreshold).Констрейнты (ограничения движения, например,
btHingeConstraint или btPoint2PointConstraint)
могут быть источником непредсказуемости. Их точность зависит от:
erp (error reduction parameter) — коэффициент
исправления ошибки.cfm (constraint force mixing) — коэффициент
сглаживания силы констрейнта.Неправильная комбинация этих параметров и переменного
deltaTime приводит к разным результатам между
запусками.
const hinge = new Ammo.btHingeConstraint(bodyA, bodyB, pivotA, pivotB, axisA, axisB, true);
hinge.setParam(Ammo.CONSTRAINT_ERP, 0.2, 0);
hinge.setParam(Ammo.CONSTRAINT_CFM, 0.0, 0);
Для улучшения воспроизводимости и предотвращения «скачков» тел:
sleepThresholds для тел, чтобы они
стабилизировались при покое.rigidBody.setSleepingThresholds(0.1, 0.1);
В многопользовательских приложениях детерминизм Ammo.js позволяет:
При этом необходимо фиксировать шаг интеграции и инициализацию всех объектов на всех клиентах.
В Ammo.js воспроизводимость симуляций зависит от:
Следование этим принципам обеспечивает стабильное и предсказуемое поведение физической симуляции на любом устройстве и при любом повторном запуске.