Loading...
Loading...
Use this skill when using Matter.js physics in Phaser 4. Covers rigid bodies, constraints, composite bodies, sensors, collision filtering, world configuration, and advanced physics shapes. Triggers on: Matter, matter physics, constraint, joint, rigid body, sensor.
npx skill4agent add phaserjs/phaser physics-matterSetting up and using Matter.js physics in Phaser 4 -- full-body physics with rigid bodies, compound bodies, constraints, composites, sensors, collision filtering, pointer dragging, tilemap integration, and debug rendering.
src/physics/matter-js/MatterPhysics.jssrc/physics/matter-js/World.jssrc/physics/matter-js/Factory.jssrc/physics/matter-js/MatterSprite.jssrc/physics/matter-js/MatterImage.jssrc/physics/matter-js/MatterGameObject.jssrc/physics/matter-js/PointerConstraint.jssrc/physics/matter-js/MatterTileBody.jssrc/physics/matter-js/components/src/physics/matter-js/events/src/physics/matter-js/typedefs/class GameScene extends Phaser.Scene {
create() {
// Matter sprite (dynamic, has animation support)
this.player = this.matter.add.sprite(400, 200, 'player');
this.player.setBounce(0.5);
this.player.setFriction(0.05);
// Matter image (dynamic, no animation)
const box = this.matter.add.image(300, 100, 'crate');
// Static body from raw shape
this.matter.add.rectangle(400, 580, 800, 40, { isStatic: true });
// Enable pointer dragging on all bodies
this.matter.add.mouseSpring();
this.cursors = this.input.keyboard.createCursorKeys();
}
update() {
if (this.cursors.left.isDown) {
this.player.setVelocityX(-5);
} else if (this.cursors.right.isDown) {
this.player.setVelocityX(5);
}
if (this.cursors.up.isDown && this.player.body.velocity.y > -0.1) {
this.player.setVelocityY(-10);
}
}
}
// Enable Matter physics in game config
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
physics: {
default: 'matter',
matter: {
gravity: { y: 1 },
enableSleeping: true,
debug: true,
setBounds: true // walls around canvas edges
}
},
scene: GameScene
};
const game = new Phaser.Game(config);this.matterMatterPhysicsthis.matter.addFactorythis.matter.worldWorldEventEmitterthis.matter.bodybodiescompositecompositesconstraintthis.matter.bodyBoundsthis.matter.worldengineMatterJS.EnginelocalWorldMatterJS.WorldenabledfalseautoUpdatetruewalls{ left, right, top, bottom }MatterWorldConfigphysics.matter| Property | Default | Purpose |
|---|---|---|
| | Gravity vector. Set |
| | |
| | Allow bodies to sleep when at rest |
| | Position solving accuracy |
| | Velocity solving accuracy |
| | Constraint stability |
| | |
| | Auto-step each game frame |
| | |
| | Use |
Phaser.Physics.Matter.SpriteSpritethis.matter.add.sprite(x, y, key, frame, options)Phaser.Physics.Matter.ImageImagethis.matter.add.image(x, y, key, frame, options)MatterGameObject(world, gameObject, options)this.matter.add.gameObject(mySprite, options)MatterSpriteMatterImageoptions.shape| Component | Key Methods |
|---|---|
| Velocity | |
| Force | |
| Bounce | |
| Friction | |
| Mass | |
| Gravity | |
| Sensor | |
| Static | |
| Sleep | |
| Collision | |
| SetBody | |
| Transform | Position sync between Matter body and Game Object |
// Runtime gravity and bounds
this.matter.world.setGravity(0, 2); // x, y, scale (default 0.001)
this.matter.world.disableGravity();
this.matter.world.setBounds(0, 0, 1600, 1200, 64, true, true, true, true);
this.matter.set60Hz(); // fixed timestep
this.matter.world.autoUpdate = false; // then manual: this.matter.step(16.666);
this.matter.pause(); // pause/resume
this.matter.resume();const player = this.matter.add.sprite(200, 300, 'hero'); // default rect body matching texture
// Custom body shapes via options.shape
const ball = this.matter.add.image(400, 100, 'ball', null, { shape: { type: 'circle', radius: 24 } });
const hex = this.matter.add.sprite(300, 100, 'hex', null, { shape: { type: 'polygon', sides: 6, radius: 32 } });
const ship = this.matter.add.sprite(400, 200, 'ship', null, {
shape: { type: 'fromVerts', verts: '0 0 40 0 40 40 20 60 0 40' }
});
// PhysicsEditor shape data
const shapes = this.cache.json.get('shapes');
const enemy = this.matter.add.sprite(500, 200, 'enemy', null, { shape: shapes.enemy });
// Add Matter physics to an existing Game Object
const existingSprite = this.add.sprite(100, 100, 'box');
this.matter.add.gameObject(existingSprite, { restitution: 0.8 });
// existingSprite now has setVelocity, setBounce, etc.MatterBodyConfigoptionslabelisStaticisSensorangletimeScaleignoreGravityignorePointerdensitymassrestitutionfrictionfrictionAirfrictionStaticslopchamfer{ radius: 5 }collisionFilter: { category: 0x0001, mask: 0xFFFFFFFF, group: 0 }onCollideCallbackonCollideEndCallbackonCollideActiveCallbackshape{ type: 'circle', radius: 24 }sprite.setVelocity(3, -5); // units per step, not pixels/sec
sprite.setVelocityX(-3);
sprite.setAngularVelocity(0.05);
const vel = sprite.getVelocity(); // { x, y }
// Forces use very small values (0.01 - 0.1)
sprite.applyForce({ x: 0.05, y: 0 });
sprite.applyForceFrom(position, { x: 0.02, y: -0.02 });
// Directional thrust relative to body angle
sprite.thrust(0.05); // forward
sprite.thrustBack(0.05); // backward
sprite.thrustLeft(0.03); // strafe left
sprite.thrustRight(0.03); // strafe right
// Batch operations via this.matter
this.matter.setVelocity(arrayOfBodies, 2, -3);
this.matter.applyForce(arrayOfBodies, { x: 0.01, y: 0 });// constraint(bodyA, bodyB, length?, stiffness?, options?) -- aliases: joint, spring
const rigid = this.matter.add.constraint(bodyA, bodyB, 100, 1); // rigid joint
const spring = this.matter.add.constraint(bodyA, bodyB, 200, 0.02, { damping: 0.05 });
const pin = this.matter.add.constraint(bodyA, bodyB, 0, 0.9); // pin joint
// World constraint (one body pinned to world point)
this.matter.add.worldConstraint(body, 50, 0.5, { pointA: { x: 400, y: 100 } });
// Offset attachment points
this.matter.add.constraint(bodyA, bodyB, 80, 1, {
pointA: { x: 20, y: 0 }, // offset from bodyA center
pointB: { x: -20, y: 0 } // offset from bodyB center
});
this.matter.getConstraintLength(constraint); // distance between anchor points
this.matter.world.removeConstraint(constraint); // remove from world// Stack of bodies in a grid
const stack = this.matter.add.stack(100, 100, 5, 4, 10, 10, (x, y) => {
return this.matter.bodies.rectangle(x, y, 40, 40);
});
// Image stack (grid of Matter Images)
const imageStack = this.matter.add.imageStack('crate', null, 100, 100, 5, 4, 5, 5);
// Chain bodies in a composite together
this.matter.add.chain(stack, 0.5, 0, -0.5, 0, { stiffness: 0.7 });
// Mesh (grid with constraints, optional cross braces)
this.matter.add.mesh(stack, 5, 4, true, { stiffness: 0.5 });
// Soft body (cols, rows, gaps, crossBrace, particleRadius, bodyOpts, constraintOpts)
this.matter.add.softBody(200, 100, 5, 5, 0, 0, true, 10, { friction: 0.1 }, { stiffness: 0.5 });
// Pre-built composites: newtonsCradle, car, pyramid
this.matter.add.newtonsCradle(300, 50, 5, 20, 200);
this.matter.add.car(400, 300, 120, 30, 25);const partA = this.matter.bodies.rectangle(0, 0, 60, 20);
const partB = this.matter.bodies.circle(0, -30, 15);
const compoundBody = this.matter.body.create({ parts: [partA, partB] });
// Attach to a Game Object
const player = this.matter.add.sprite(400, 200, 'hero');
player.setExistingBody(compoundBody);enableSleeping: trueif (body.isSleeping) { /* body is at rest */ }
sprite.setSleepThreshold(30); // lower = falls asleep faster (default 60)
sprite.setToSleep(); // force sleep
sprite.setAwake(); // force wake
sprite.setSleepEvents(true, true); // enable sleepstart/sleepend events
this.matter.world.on('sleepstart', (event, body) => { /* body slept */ });
this.matter.world.on('sleepend', (event, body) => { /* body woke */ });const trigger = this.matter.add.rectangle(400, 300, 100, 100, { isSensor: true, isStatic: true });
sprite.setSensor(true); // toggle on Game Object
sprite.isSensor(); // check state
// Sensors fire normal collision events -- use collisionstart/end to detect entry/exitcategorymaskgroupconst PLAYER = this.matter.world.nextCategory(); // 0x0002 (32 max)
const ENEMY = this.matter.world.nextCategory(); // 0x0004
const GROUND = this.matter.world.nextCategory(); // 0x0008
player.setCollisionCategory(PLAYER);
player.setCollidesWith([ENEMY, GROUND]);
bullet.setCollisionCategory(0x0010);
bullet.setCollidesWith([ENEMY, GROUND]); // bullets skip player
// Collision groups: same negative = never collide with each other
const noCollide = this.matter.world.nextGroup(true);
spriteA.setCollisionGroup(noCollide);
spriteB.setCollisionGroup(noCollide);
// Via body config: collisionFilter: { category, mask, group }
// Batch: this.matter.setCollisionCategory([bodyA, bodyB], ENEMY);// Per-body callbacks (on Matter Game Objects)
player.setOnCollide((pair) => { /* pair.bodyA, pair.bodyB */ });
player.setOnCollideEnd((pair) => { /* collision ended */ });
player.setOnCollideActive((pair) => { /* still colliding */ });
player.setOnCollideWith(enemy, (body, pair) => { /* hit specific body */ });
// Game Object-level events (emitted on the Game Object itself)
player.on('collide', (bodyA, bodyB, pair) => {});
player.on('collideEnd', (bodyA, bodyB, pair) => {});const map = this.make.tilemap({ key: 'level' });
const tileset = map.addTilesetImage('tiles', 'tiles-img');
const layer = map.createLayer('Ground', tileset, 0, 0);
layer.setCollisionByProperty({ collides: true }); // MUST set collision first
this.matter.world.convertTilemapLayer(layer); // creates MatterTileBody per colliding tile
// Uses Tiled collision shapes (rect, circle, polygon) if defined, otherwise tile bounds.
// Access: tile.physics.matterBody
// Individual tile: this.matter.add.tileBody(tile, { isStatic: true, friction: 0.5 });
// After map changes: this.matter.world.convertTiles([tile1, tile2]);sprite.setFriction(0.1); // dynamic: resistance during motion (0-1)
sprite.setFrictionStatic(0.5); // static: resistance before motion starts
sprite.setFrictionAir(0.05); // air: environmental drag (default 0.01)
sprite.setFriction(0.1, 0.02, 0.3); // set all three: dynamic, air, static// Create body from vertex string (concave shapes auto-decomposed)
const body = this.matter.add.fromVertices(400, 300, '0 0 40 0 40 40 20 60 0 40');
// Multiple vertex sets for complex shapes
const vertexSets = [
[{ x: 0, y: 0 }, { x: 40, y: 0 }, { x: 40, y: 40 }],
[{ x: 40, y: 40 }, { x: 20, y: 60 }, { x: 0, y: 40 }]
];
this.matter.add.fromVertices(300, 200, vertexSets);// Enable click-and-drag on all Matter bodies
const pc = this.matter.add.mouseSpring({ stiffness: 0.2, damping: 0.1 });
pc.active = false; // disable temporarily
body.ignorePointer = true; // prevent specific body from being dragged
pc.stopDrag(); // release current drag programmatically
pc.destroy(); // remove entirely
// Drag events on the world
this.matter.world.on('dragstart', (body, part, constraint) => {});
this.matter.world.on('drag', (body, constraint) => {});
this.matter.world.on('dragend', (body, constraint) => {});const hits = this.matter.intersectPoint(pointer.x, pointer.y); // bodies at point
const contains = this.matter.containsPoint(body, x, y); // point-in-body test
const inRegion = this.matter.intersectRect(100, 100, 200, 200); // bodies in rect
const rayHits = this.matter.intersectRay(0, 300, 800, 300, 1); // raycast
const colliding = this.matter.intersectBody(playerBody); // body overlap
// Overlap with callbacks
this.matter.overlap(playerBody, enemyBodies, (bodyA, bodyB, info) => {
console.log('Overlapping', bodyA, bodyB);
});// Enable debug with specific options (pass as debug property in matter config)
// Boolean flags: showBody, showStaticBody, showVelocity, showCollisions, showSensors,
// showJoint, showPositions, showBounds, showAxes, showAngleIndicator, showSleeping,
// showConvexHulls, showInternalEdges, renderFill, renderLine
// Color/style: lineColor, lineThickness, fillColor, staticLineColor, staticFillColor,
// sensorLineColor, jointColor, pinColor, springColor, anchorColor, positionColor
// Toggle debug at runtime
this.matter.world.drawDebug = false;
this.matter.world.debugGraphic.visible = false;
// Set render style on individual body or constraint
this.matter.world.setBodyRenderStyle(body, 0xff0000, 1, 2, 0x00ff00, 0.5);
this.matter.world.setConstraintRenderStyle(constraint, 0xffff00, 1, 2);this.matter.worldEventEmitter| Event | Callback Signature | When |
|---|---|---|
| | Two bodies first start colliding |
| | Two bodies are still colliding |
| | Two bodies stop colliding |
| | Before engine update step |
| | After engine update step |
| | Before a body/constraint is added |
| | After a body/constraint is added |
| | Before a body/constraint is removed |
| | After a body/constraint is removed |
| | Pointer starts dragging body |
| | Pointer is dragging body |
| | Pointer stops dragging body |
| | Body goes to sleep (requires |
| | Body wakes up (requires |
| none | World paused |
| none | World resumed |
event.pairsbodyAbodyBthis.matter.addsprite(x, y, key, frame?, opts?)image(x, y, key, frame?, opts?)gameObject(go, opts?)tileBody(tile, opts?)rectangle(x, y, w, h, opts?)circle(x, y, r, opts?)polygon(x, y, sides, r, opts?)trapezoid(x, y, w, h, slope, opts?)fromVertices(x, y, verts, opts?)fromPhysicsEditor(x, y, config, opts?)fromSVG(x, y, xml, scale?, opts?)fromJSON(x, y, config, opts?)constraint(a, b, len?, stiff?, opts?)jointspringworldConstraint(body, len?, stiff?, opts?)mouseSpring(opts?)pointerConstraintstack(x, y, cols, rows, colGap, rowGap, cb)imageStack(key, frame, x, y, cols, rows)pyramid(...)chain(composite, xA, yA, xB, yB, opts?)mesh(composite, cols, rows, cross, opts?)softBody(...)car(x, y, w, h, wheelSize)newtonsCradle(x, y, num, size, len)this.matterpause()resume()set60Hz()set30Hz()step(delta?)setVelocity(bodies, x, y)setAngularVelocity(bodies, v)applyForce(bodies, force)applyForceFromAngle(bodies, speed, angle?)containsPoint(body, x, y)intersectPoint(x, y)intersectRect(x, y, w, h, outside?)intersectRay(x1, y1, x2, y2, width?)intersectBody(body)overlap(target, bodies?, cb?)setCollisionCategory(bodies, value)setCollisionGroup(bodies, value)setCollidesWith(bodies, cats)alignBody(body, x, y, align)this.matter.worldsetBounds(x?, y?, w?, h?, thickness?, l?, r?, t?, b?)setGravity(x?, y?, scale?)disableGravity()add(object)remove(object, deep?)removeConstraint(constraint)convertTilemapLayer(layer, opts?)convertTiles(tiles, opts?)nextCategory()nextGroup(isNonColliding?)getAllBodies()has(body)pause()resume()this.matterbodybodiescompositecompositesconstraintdetectorquerypairpairsresolveraxesboundssvgvectorvertices0.010.1115setBodysetRectanglepartsnextCategory()collisionFilter.groupsprite.setSleepEvents(true, true)setCollisionByPropertyMath.max(bodyA.restitution, bodyB.restitution)body.ignorePointer = true| Path | Purpose |
|---|---|
| Scene plugin ( |
| World management, engine, bounds, debug, events proxy |
| |
| Physics sprite (Sprite + Matter components) |
| Physics image (Image + Matter components) |
| Injects Matter components into any Game Object |
| Wraps a Tile with a Matter body |
| Click-and-drag body constraint |
| Body alignment by visual bounds |
| Parses PhysicsEditor JSON into bodies |
| Mixins: Velocity, Force, Collision, SetBody, Sensor, Bounce, Friction, Mass, Gravity, Static, Sleep, Transform |
| Event constants (COLLISION_START, DRAG_START, SLEEP_START, etc.) |
| TypeDefs: MatterWorldConfig, MatterBodyConfig, MatterCollisionFilter, MatterConstraintConfig, etc. |
| Bundled Matter.js library modules |