How to Change Ammo.js Collision Shapes at Runtime
In ammo.js, a standard rigid body cannot have its collision shape swapped instantly while it is actively participating in the physics simulation. However, you can dynamically change a rigid body’s collision shape during runtime by temporarily removing the body from the physics world, applying the new shape, recalculating its inertia, and then re-adding it to the simulation. This article provides a direct, step-by-step guide and code implementation on how to achieve this safely without breaking your physics simulation.
Why Direct Swapping Fails
Ammo.js is a direct port of Bullet Physics. The physics engine caches
collision detection data, contact points, and broadphase proxies for
active rigid bodies. If you change the underlying collision shape of an
active rigid body directly via setCollisionShape() without
updating the physics world, it will cause collision glitches, ghost
collisions, or immediate crashes because the cached data no longer
matches the new geometry.
The Standard Re-addition Method
To safely change a collision shape at runtime, you must follow this specific sequence:
- Remove the rigid body from the dynamics world to clear cached collision pairs.
- Apply the new collision shape to the rigid body.
- Recalculate the local inertia based on the new shape’s geometry and the body’s mass.
- Update the mass properties of the rigid body.
- Re-add the rigid body back to the dynamics world.
Code Implementation
Here is how to implement this process in ammo.js:
function changeRigidBodyShape(dynamicsWorld, rigidBody, newShape, mass) {
// 1. Remove the body from the physics world
dynamicsWorld.removeRigidBody(rigidBody);
// 2. Set the new collision shape
rigidBody.setCollisionShape(newShape);
// 3. Recalculate local inertia for the new shape
const localInertia = new Ammo.btVector3(0, 0, 0);
if (mass > 0) {
newShape.calculateLocalInertia(mass, localInertia);
}
// 4. Update the rigid body's mass and inertia
rigidBody.setMassProps(mass, localInertia);
rigidBody.updateInertiaTensor();
// 5. Re-add the body to the physics world
dynamicsWorld.addRigidBody(rigidBody);
// Clean up temporary vector
Ammo.destroy(localInertia);
}Alternative: Using Compound Shapes
If you need to change the collision boundaries frequently—such as
toggling parts of a destructible object or extending a character’s
reach—using btCompoundShape is often a highly efficient
alternative.
With a compound shape, you can add, remove, or rescale child shapes
dynamically using addChildShape() and
removeChildShape() methods. After modifying child shapes
within a compound shape, you only need to call
dynamicsWorld.updateSingleAabb(rigidBody) to update the
collision bounds, bypassing the need to remove and re-add the rigid body
entirely.