Prevent Tunneling in Ammo.js with setCcdMotionThreshold
This article explains how to configure the
setCcdMotionThreshold method in ammo.js to prevent
“tunneling”—a common physics simulation issue where fast-moving objects
pass through solid barriers. You will learn the mechanics behind
Continuous Collision Detection (CCD), how to determine the correct
values for your rigid bodies, and how to implement the necessary code to
ensure stable and accurate collision detection in your 3D projects.
Understanding Tunneling and Continuous Collision Detection (CCD)
In discrete physics simulations, object positions are updated at fixed time steps. If an object moves extremely fast, its displacement in a single physics step can be larger than the thickness of a wall it is heading toward. As a result, the object is on one side of the wall in one frame, and on the other side in the next frame. Because the object never actually intersects the wall at the exact moment of the physics step, the engine misses the collision. This phenomenon is known as tunneling.
To solve this, ammo.js (a JavaScript port of the Bullet physics engine) uses Continuous Collision Detection (CCD). CCD sweeps a sphere along the object’s movement path to test for collisions between frames.
How to Configure setCcdMotionThreshold
To enable CCD on an Ammo.btRigidBody, you must configure
two parameters together: the motion threshold
(setCcdMotionThreshold) and the swept sphere radius
(setCcdSweptSphereRadius). Setting only one will not
activate CCD.
1. Set the Motion Threshold
The setCcdMotionThreshold method determines the velocity
limit at which the physics engine switches from discrete collision
detection to continuous collision detection for a specific body.
If the distance an object travels in a single simulation step exceeds this threshold value, CCD is triggered.
2. Set the Swept Sphere Radius
The setCcdSweptSphereRadius method defines the radius of
an invisible sphere embedded within your object. Ammo.js sweeps this
sphere along the object’s travel path to test for continuous
collisions.
Code Implementation
Here is how to apply these settings to a fast-moving rigid body, such as a projectile:
// Assuming 'body' is an instantiated Ammo.btRigidBody
// and the object's physical radius or half-extent is 1.0 unit.
const objectSize = 1.0;
// 1. Configure the motion threshold.
// Trigger CCD if the object moves more than its own radius in a single step.
body.setCcdMotionThreshold(objectSize);
// 2. Configure the swept sphere radius.
// The swept sphere should be smaller than the actual object to avoid false collisions.
body.setCcdSweptSphereRadius(objectSize * 0.2);Best Practices for Choosing Values
- Threshold Value: Set this to match the size (radius
or half-extent) of your moving object. For example, if your projectile
has a radius of
0.2units, setsetCcdMotionThreshold(0.2). Setting this value too low will trigger expensive CCD calculations unnecessarily, while setting it too high will fail to prevent tunneling. - Swept Sphere Radius: This value must be smaller
than your object’s physical geometry. A good rule of thumb is to set it
between
20%and50%of the object’s radius (e.g.,objectSize * 0.5). If the swept sphere is too large, the object may collide with walls prematurely before its actual visible mesh makes contact. - Performance Impact: CCD is computationally expensive compared to discrete collision detection. Do not enable it globally. Only apply it to critical, fast-moving objects like bullets, golf balls, or high-speed vehicles.