Matter.js Raycasting Against Specific Bodies
This article explains how to perform raycasting against a specific
group of physics bodies in Matter.js using the built-in
Matter.Query.ray method. You will learn how to isolate
target bodies using collision filters, custom labels, or arrays, pass
that subset to the raycast function, and sort the results to identify
the closest collision along the ray path.
Using Matter.Query.ray
Matter.js includes a query module designed for spatial tests. The primary function for raycasting is:
Matter.Query.ray(bodies, startPoint, endPoint, [rayWidth])Unlike some physics engines that cast a ray globally into the world
and rely on mask flags inside the query function,
Matter.Query.ray requires you to supply an array of bodies
explicitly. This makes targeting a specific group straightforward: you
filter your bodies array before passing it into the query.
Methods for Grouping Bodies
There are three common ways to define a specific group of bodies in Matter.js.
1. Filtering by Custom Labels
Assign a descriptive string to the label property when
instantiating bodies.
const enemy = Matter.Bodies.rectangle(x, y, w, h, { label: 'enemy' });2. Filtering by Collision Categories
Use Matter.js bitmask categories if your groups are already separated for collision handling.
const ENEMY_CATEGORY = 0x0002;
const enemy = Matter.Bodies.rectangle(x, y, w, h, {
collisionFilter: {
category: ENEMY_CATEGORY
}
});3. Maintaining a Dedicated Array
Store references in a standalone array or a custom
Matter.Composite when objects are created, avoiding the
overhead of filtering the entire world during runtime.
const targetGroup = [];
targetGroup.push(enemy);Implementation Example
The following code demonstrates retrieving all bodies from the world, filtering them by a specific group label, executing the raycast, and identifying the closest hit.
// Define ray start and end coordinates
const rayStart = { x: 100, y: 100 };
const rayEnd = { x: 500, y: 100 };
const rayWidth = 2; // Optional thickness of the ray
// 1. Get all bodies in the world
const allBodies = Matter.Composite.allBodies(engine.world);
// 2. Filter to only include the target group (e.g., bodies labeled 'enemy')
const targetBodies = allBodies.filter(body => body.label === 'enemy');
// 3. Cast the ray against only the target group
const hits = Matter.Query.ray(targetBodies, rayStart, rayEnd, rayWidth);
// 4. Handle results
if (hits.length > 0) {
// Hits are not automatically sorted by distance along the ray.
// Calculate distance from rayStart to find the closest hit.
hits.sort((a, b) => {
const distA = Matter.Vector.magnitudeSquared(Matter.Vector.sub(a.body.position, rayStart));
const distB = Matter.Vector.magnitudeSquared(Matter.Vector.sub(b.body.position, rayStart));
return distA - distB;
});
const closestHit = hits[0];
console.log('Closest body hit:', closestHit.body);
}Key Considerations
- Ray Width: Setting
rayWidthturns the ray into a swept rectangle or capsule shape, which is useful for thick projectiles or visibility checks with volume. - Hit Sorting:
Matter.Query.rayreturns an array of collision objects containing collision details and references tobodyAandbodyB. The array is unsorted by default, so sorting by distance relative torayStartis required if you only want the first obstructed object. - Performance: Filtering through
Matter.Composite.allBodieson every frame can become expensive in complex scenes. For high-frequency casts, keep a dedicated array of target bodies updated upon body creation and destruction.