How to Create Rotating Obstacles in Matter.js
This guide explains how to construct dangerous, rotating obstacles such as spinning spiked clubs or maces using the Matter.js 2D physics engine. You will learn how to design a multi-part compound body for the weapon, anchor it in place using constraints, apply constant angular velocity to keep it spinning, and detect player collisions for hazard interactions.
1. Constructing the Compound Body
A spiked club consists of multiple shapes: a shaft, a main club head,
and several sharp spikes. In Matter.js, you can combine multiple
overlapping shapes into a single rigid body using
Body.create with the parts property.
const { Bodies, Body, Composite } = Matter;
// Create individual parts
const handle = Bodies.rectangle(400, 300, 20, 160, {
render: { fillStyle: '#8B5A2B' }
});
const head = Bodies.circle(400, 200, 35, {
render: { fillStyle: '#4A4A4A' }
});
// Create four spikes around the head
const spikeTop = Bodies.polygon(400, 155, 3, 15, { angle: 0 });
const spikeBottom = Bodies.polygon(400, 245, 3, 15, { angle: Math.PI });
const spikeLeft = Bodies.polygon(355, 200, 3, 15, { angle: -Math.PI / 2 });
const spikeRight = Bodies.polygon(445, 200, 3, 15, { angle: Math.PI / 2 });
// Combine parts into a single compound body
const spikedClub = Body.create({
parts: [handle, head, spikeTop, spikeBottom, spikeLeft, spikeRight],
label: 'hazard'
});
// Set the pivot point near the bottom of the handle
Body.setCentre(spikedClub, { x: 400, y: 360 }, true);
Composite.add(world, spikedClub);2. Pinning the Obstacle to the World
To make the club rotate around a fixed pivot without falling or
drifting due to gravity, attach it to an anchor point using
Constraint.create.
const { Constraint } = Matter;
const pivot = Constraint.create({
pointA: { x: 400, y: 360 }, // Fixed world coordinates
bodyB: spikedClub,
pointB: { x: 0, y: 80 }, // Relative offset to the club's pivot
stiffness: 1,
length: 0
});
Composite.add(world, pivot);3. Driving the Rotation
Matter.js physics can naturally slow an object down over time due to friction and air resistance. To maintain a constant spin, you can continuously enforce an angular velocity or update the body's rotation before every engine update tick.
Using Events.on with the beforeUpdate event
ensures predictable, continuous rotation:
const { Events } = Matter;
const rotationSpeed = 0.05; // Adjust for faster or slower rotation
Events.on(engine, 'beforeUpdate', () => {
// Method A: Maintain dynamic angular velocity
Body.setAngularVelocity(spikedClub, rotationSpeed);
// Method B: Alternatively, directly advance the angle for a kinematic hazard
// Body.setAngle(spikedClub, spikedClub.angle + rotationSpeed);
});To eliminate resistance and ensure smooth rotation if using
setAngularVelocity, set the body's friction properties to
zero:
spikedClub.frictionAir = 0;
spikedClub.friction = 0;4. Detecting Collisions with the Hazard
Use the collisionStart event to detect when a player or
other dynamic object hits the spinning obstacle. Checking the
label property allows you to apply damage, knockback, or
game-over triggers.
Events.on(engine, 'collisionStart', (event) => {
event.pairs.forEach((pair) => {
const { bodyA, bodyB } = pair;
if (bodyA.label === 'hazard' || bodyB.label === 'hazard') {
const target = bodyA.label === 'hazard' ? bodyB : bodyA;
if (target.label === 'player') {
// Trigger damage or apply knockback
Body.applyForce(target, target.position, {
x: (target.position.x - 400) * 0.005,
y: -0.05
});
}
}
});
});