How to Use Collision Masks in Matter.js
This guide explains how to control object interactions in Matter.js
using collision categories and masks. By leveraging bitwise flags, you
can precisely define which physics bodies collide with one another and
which pass through each other harmlessly. You will learn the mechanics
behind the collisionFilter object, how to define unique
categories, and how to write clear code to enforce these rules in your
simulation.
Understanding the Collision Filter
Matter.js uses a collisionFilter object attached to each
body to determine whether two bodies can physically interact. This
object contains two primary properties relevant to masking:
category: A bit field specifying the group(s) the body belongs to.mask: A bit field specifying all the categories this body is allowed to collide with.
For two bodies, bodyA and bodyB, to
collide, both of the following conditions must be
met:
(bodyA.collisionFilter.mask & bodyB.collisionFilter.category) !== 0
(bodyB.collisionFilter.mask & bodyA.collisionFilter.category) !== 0If either evaluation results in 0, no collision will
occur.
Defining Categories with Bitwise Flags
Categories in Matter.js must be powers of two (up to 32 unique categories, representing 32-bit integers). You can define these using hexadecimal notation or bit-shift operators:
const defaultCategory = 0x0001; // 1 << 0
const playerCategory = 0x0002; // 1 << 1
const enemyCategory = 0x0004; // 1 << 2
const groundCategory = 0x0008; // 1 << 3
const bulletCategory = 0x0010; // 1 << 4Combining Categories for the Mask
To allow a body to collide with multiple categories, combine those
categories using the bitwise OR operator (|).
Example Scenario
- Ground: Collides with everything
(
playerCategory | enemyCategory | bulletCategory). - Player: Collides with ground and enemies, but ignores bullets.
- Bullet: Collides with ground and enemies, but passes through the player.
- Enemy: Collides with ground, player, and bullets.
Implementation Example
const { Bodies } = Matter;
// 1. Define Categories
const CATEGORY_GROUND = 0x0001;
const CATEGORY_PLAYER = 0x0002;
const CATEGORY_ENEMY = 0x0004;
const CATEGORY_BULLET = 0x0008;
// 2. Create the Ground
const ground = Bodies.rectangle(400, 600, 800, 50, {
isStatic: true,
collisionFilter: {
category: CATEGORY_GROUND,
mask: CATEGORY_PLAYER | CATEGORY_ENEMY | CATEGORY_BULLET
}
});
// 3. Create the Player
const player = Bodies.circle(200, 200, 30, {
collisionFilter: {
category: CATEGORY_PLAYER,
// Collides with ground and enemy, ignores bullets
mask: CATEGORY_GROUND | CATEGORY_ENEMY
}
});
// 4. Create the Bullet
const bullet = Bodies.circle(250, 200, 5, {
collisionFilter: {
category: CATEGORY_BULLET,
// Collides with ground and enemy, ignores player
mask: CATEGORY_GROUND | CATEGORY_ENEMY
}
});
// 5. Create an Enemy
const enemy = Bodies.rectangle(500, 200, 40, 40, {
collisionFilter: {
category: CATEGORY_ENEMY,
// Collides with ground, player, and bullets
mask: CATEGORY_GROUND | CATEGORY_PLAYER | CATEGORY_BULLET
}
});Important Considerations
- Symmetry Requirement: Remember that collision is two-way. Even if the bullet's mask includes the player category, the collision will fail if the player's mask does not include the bullet category.
- The
groupProperty: IfcollisionFilter.groupis set to a non-zero value, it takes precedence over categories and masks. Bodies with the same positive group value will always collide, while bodies with the same negative group value will never collide. To rely strictly on category and mask rules, keepgroupset to0(the default).