Continuous Collision Detection in Matter.js
Continuous Collision Detection (CCD) is designed to prevent fast-moving bodies from passing through obstacles without triggering a collision, an issue known as tunneling. Matter.js, a popular 2D physics engine for the web, does not feature an automated, built-in swept CCD solver like some heavier physics engines. Instead, Matter.js natively relies on discrete collision detection using the Separating Axis Theorem (SAT), requiring developers to employ specific architectural strategies, such as engine sub-stepping, raycasting, and geometry adjustments, to achieve continuous collision behavior.
The Problem of Discrete Detection and Tunneling
In standard Matter.js updates, the physics engine calculates object positions frame by frame based on their velocity and the elapsed delta time. If a body moves at a high speed or the frame rate drops, the distance traveled in a single tick can exceed the thickness of another object or wall. Because Matter.js samples overlap strictly at the end of each discrete step, the body can jump completely from one side of an obstacle to the other without an intersection ever being registered.
Achieving CCD via Sub-Stepping
The primary way to simulate continuous collision detection in Matter.js is through sub-stepping. Rather than advancing the engine by a single large time step per render frame (e.g., 16.6ms for 60 FPS), you split the update into multiple smaller intervals:
- Manual Sub-Step Loop: Instead of letting
Matter.Runnerautomatically advance the engine with a singledelta, you can disable the default runner and advance the engine multiple times inside your render loop (e.g., callingMatter.Engine.update(engine, delta / subSteps)four to eight times per frame). - Reduced Displacement: By reducing the time delta per calculation, the displacement per step shrinks significantly, ensuring that fast-moving bodies overlap with barriers rather than skipping through them.
- Solver Iterations: Adjusting
engine.positionIterationsandengine.velocityIterationsimproves the precision of the contact resolution, though iterations alone do not resolve tunneling without smaller step deltas.
Trajectory Raycasting Using Matter.Query
For extremely fast, small projectiles (like bullets), sub-stepping can become computationally expensive. A more performant approach to CCD in Matter.js is predictive raycasting:
- Predictive Querying: Before allowing a body to
update its position, use
Matter.Query.ray(allBodies, startPoint, endPoint)to cast a ray along the trajectory of the body from its current position to its expected destination. - Early Collision Resolution: If the query detects an intersection along the ray vector, you can manually clamp the body's position to the contact point, fire the appropriate collision event, and either invert the velocity vector for a bounce or destroy the body entirely.
Environmental Geometry Adjustments
Another practical method to prevent tunneling without code overhead is modifying the physical properties of the environment:
- Thick Colliders: Increase the thickness of static walls and ground boundaries. Even if the visible graphic is thin, the underlying physics body can extend well beyond the visible area away from the active scene.
- Speed Clamping: Apply a terminal velocity to bodies
by clamping
body.velocityor increasing air friction (frictionAir). Limiting the maximum travel distance per step ensures it cannot exceed the width of the thinnest obstacle.