Pause and Resume Ammo.js Physics
Controlling the execution of a physics engine is essential for
implementing pause menus, managing game states, and optimizing
performance in WebGL applications. This article provides a direct guide
on how to globally pause and resume the ammo.js physics
simulation, as well as how to selectively freeze and unfreeze individual
rigid bodies within the simulation loop.
Method 1: Global Pause and Resume
The simplest way to pause the entire physics simulation is by controlling the step simulation call within your application’s animation frame loop.
ammo.js advances the physics world using the
stepSimulation method. By introducing a boolean flag, you
can conditionally skip this step.
let isPaused = false;
// Your main animation/render loop
function tick(deltaTime) {
if (!isPaused) {
// Step the physics world only if not paused
// Max sub-steps = 10, fixed time step = 1/60
dynamicsWorld.stepSimulation(deltaTime, 10, 1/60);
}
// Continue rendering your visual scene (e.g., Three.js)
renderer.render(scene, camera);
requestAnimationFrame(tick);
}
// Functions to toggle state
function pausePhysics() {
isPaused = true;
}
function resumePhysics() {
isPaused = false;
}By bypassing stepSimulation, all physical bodies freeze
in their current positions and velocities, preserving their states until
the simulation resumes.
Method 2: Selective Pausing (Freezing Individual Bodies)
If you need the physics simulation to continue running while freezing
only specific objects, you must manipulate those individual rigid
bodies. In ammo.js, this is achieved by storing the body’s
velocities and temporarily changing its activation state or kinematic
flags.
Step 1: Freezing a Specific Rigid Body
To pause an individual body, you must store its current linear and angular velocities so they can be restored later, set its velocities to zero, and disable its simulation state.
// Object to store the velocities of paused bodies
const pausedBodiesState = new Map();
function pauseRigidBody(body) {
// 1. Get and store current velocities
const linearVelocity = body.getLinearVelocity();
const angularVelocity = body.getAngularVelocity();
pausedBodiesState.set(body, {
linear: { x: linearVelocity.x(), y: linearVelocity.y(), z: linearVelocity.z() },
angular: { x: angularVelocity.x(), y: angularVelocity.y(), z: angularVelocity.z() }
});
// 2. Zero out velocities to stop movement
const zeroVector = new Ammo.btVector3(0, 0, 0);
body.setLinearVelocity(zeroVector);
body.setAngularVelocity(zeroVector);
// 3. Force the body into a disabled simulation state
// 5 represents the DISABLE_SIMULATION activation state
body.setActivationState(5);
// Clean up temporary vector
Ammo.destroy(zeroVector);
}Step 2: Resuming a Specific Rigid Body
To resume the individual body, restore its saved velocities and reactivate it within the dynamics world.
function resumeRigidBody(body) {
const savedState = pausedBodiesState.get(body);
if (savedState) {
// 1. Re-apply saved velocities
const linearVelocity = new Ammo.btVector3(savedState.linear.x, savedState.linear.y, savedState.linear.z);
const angularVelocity = new Ammo.btVector3(savedState.angular.x, savedState.angular.y, savedState.angular.z);
body.setLinearVelocity(linearVelocity);
body.setAngularVelocity(angularVelocity);
// 2. Reactivate the body
// 1 represents the ACTIVE_TAG activation state
body.activate(true);
body.setActivationState(1);
// Clean up temporary vectors and map entry
Ammo.destroy(linearVelocity);
Ammo.destroy(angularVelocity);
pausedBodiesState.delete(body);
}
}Alternative: Kinematic Toggle
Another selective approach is to temporarily convert the rigid body into a kinematic body. Kinematic bodies are not affected by gravity or forces but can still be collided with by other dynamic objects.
// Freeze by making it kinematic
function makeKinematic(body) {
body.setCollisionFlags(body.getCollisionFlags() | 2); // 2 is CF_KINEMATIC_OBJECT
body.setActivationState(4); // 4 is DISABLE_DEACTIVATION
}
// Restore to dynamic
function makeDynamic(body) {
body.setCollisionFlags(body.getCollisionFlags() & ~2); // Remove kinematic flag
body.activate(true);
}