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