$npx -y skills add Yakoub-ai/phaser4-gamedev --skill phaser-matterThis skill should be used when the user asks to "Matter physics", "realistic physics", "polygon collision", "joints", "constraints", "complex physics shapes", "Matter.js", "ragdoll", "hinge joint", "compound body", "physics sensor", etc.
| 1 | # Phaser 4 Matter Physics |
| 2 | |
| 3 | Matter.js is Phaser's second built-in physics engine. Use it when Arcade Physics can't meet your needs — specifically for non-rectangular collision shapes, real physics constraints, or ragdoll-style simulations. |
| 4 | |
| 5 | ## Arcade vs Matter — When to Use Which |
| 6 | |
| 7 | **Arcade Physics** (default choice): |
| 8 | - AABB collision only: rectangles and circles |
| 9 | - Simple hitboxes, excellent performance |
| 10 | - `body.blocked.down` for ground detection |
| 11 | - Covers 95% of 2D games |
| 12 | |
| 13 | **Matter Physics** (when you need it): |
| 14 | - Convex polygon shapes and complex concave bodies |
| 15 | - Compound bodies (multiple shapes welded together) |
| 16 | - Realistic constraints: hinges, springs, distance rods |
| 17 | - Ragdoll physics and destructible objects |
| 18 | - Sensor zones with physics-accurate collision events |
| 19 | |
| 20 | **Rule:** Default to Arcade. Switch to Matter only when you need non-rectangular collision shapes or real joints. |
| 21 | |
| 22 | ## Enabling Matter Physics |
| 23 | |
| 24 | ```typescript |
| 25 | // In GameConfig: |
| 26 | const config: Phaser.Types.Core.GameConfig = { |
| 27 | physics: { |
| 28 | default: 'matter', |
| 29 | matter: { |
| 30 | gravity: { x: 0, y: 1 }, // normalized: 1 = earth-like downward pull |
| 31 | debug: false, // true renders wireframes — essential during development |
| 32 | }, |
| 33 | }, |
| 34 | }; |
| 35 | ``` |
| 36 | |
| 37 | Set `debug: true` during development. Matter's wireframe overlay shows exactly what shape each body has, which offsets are applied, and where constraints attach. |
| 38 | |
| 39 | ## Creating Bodies |
| 40 | |
| 41 | ### Rectangle (default) |
| 42 | |
| 43 | ```typescript |
| 44 | // matter.add.image creates a MatterImage (static by default if you call .setStatic) |
| 45 | const box = this.matter.add.image(x, y, 'box'); |
| 46 | ``` |
| 47 | |
| 48 | ### Circle |
| 49 | |
| 50 | ```typescript |
| 51 | const ball = this.matter.add.image(x, y, 'ball').setCircle(radius); |
| 52 | // Optional: pass options as second arg to setCircle |
| 53 | const ball2 = this.matter.add.image(x, y, 'ball').setCircle(32, { restitution: 0.8 }); |
| 54 | ``` |
| 55 | |
| 56 | ### Convex Polygon |
| 57 | |
| 58 | ```typescript |
| 59 | const hex = this.matter.add.image(x, y, 'hex') |
| 60 | .setBody({ type: 'polygon', sides: 6, radius: 30 }); |
| 61 | ``` |
| 62 | |
| 63 | ### Custom Vertices (fromVertices) |
| 64 | |
| 65 | ```typescript |
| 66 | // Vertices defined as array of {x,y} relative to body center |
| 67 | const verts = [{ x: 0, y: -30 }, { x: 20, y: 10 }, { x: -20, y: 10 }]; |
| 68 | const arrow = this.matter.add.image(x, y, 'arrow') |
| 69 | .setBody({ type: 'fromVertices', verts: verts }); |
| 70 | ``` |
| 71 | |
| 72 | Note: Matter decomposes concave polygons into convex parts automatically via `poly-decomp`. Ensure your vertices are wound consistently (clockwise or counter-clockwise). |
| 73 | |
| 74 | ### Compound Body (multiple shapes as one body) |
| 75 | |
| 76 | ```typescript |
| 77 | // Use matter.bodies to build parts, then merge into a compound |
| 78 | const Body = (this.matter as any).body as typeof MatterJS.Body; |
| 79 | const Bodies = (this.matter as any).bodies as typeof MatterJS.Bodies; |
| 80 | |
| 81 | const torso = Bodies.rectangle(x, y, 30, 50); |
| 82 | const head = Bodies.circle(x, y - 40, 15); |
| 83 | const compound = Body.create({ parts: [torso, head] }); |
| 84 | |
| 85 | const player = this.matter.add.image(x, y, 'player').setExistingBody(compound); |
| 86 | ``` |
| 87 | |
| 88 | ### Static Body (immovable) |
| 89 | |
| 90 | ```typescript |
| 91 | const ground = this.matter.add.image(x, y, 'ground').setStatic(true); |
| 92 | // Or create a static rectangle directly: |
| 93 | this.matter.add.rectangle(x, y, width, height, { isStatic: true }); |
| 94 | ``` |
| 95 | |
| 96 | ## Body Properties |
| 97 | |
| 98 | All property setters return the MatterGameObject for chaining. |
| 99 | |
| 100 | ```typescript |
| 101 | const body = this.matter.add.image(x, y, 'crate'); |
| 102 | |
| 103 | body.setFriction(0.1); // surface friction: 0 = ice, 1 = very sticky |
| 104 | body.setFrictionAir(0.05); // air resistance / velocity damping per step |
| 105 | body.setFrictionStatic(0.5); // static friction (resistance to starting motion) |
| 106 | body.setRestitution(0.8); // bounciness: 0 = no bounce, 1 = perfect elastic |
| 107 | body.setMass(5); // kilograms — affects momentum transfer |
| 108 | body.setDensity(0.002); // alternative to setMass; mass derived from area |
| 109 | body.setIgnoreGravity(true); // float independent of world gravity |
| 110 | body.setFixedRotation(); // prevent rotation (essential for player characters!) |
| 111 | body.setSensor(true); // passes through objects but still fires collision events |
| 112 | body.setVelocity(vx, vy); // teleport velocity (px/frame, not px/sec like Arcade) |
| 113 | body.setAngularVelocity(0.1); // spin speed in radians per step |
| 114 | ``` |
| 115 | |
| 116 | **Critical for platform games:** Always call `setFixedRotation()` on your player body. Without it, the capsule-shaped body rolls and tips over on contact with surfaces. |
| 117 | |
| 118 | **Air friction guideline:** `0.01` = floaty/space, `0.05` = normal, `0.15` = heavy/sluggish. |
| 119 | |
| 120 | ## Applying Forces |
| 121 | |
| 122 | ```typescript |
| 123 | // Apply force at body center (world coordinates) |
| 124 | // Force is a vector in Matter units (very small numbers — tune empirically) |
| 125 | this.matter.applyForce(body.body as MatterJS.Body, { x: 0.01, y: -0.05 }); |
| 126 | |
| 127 | // Apply force at an offset point |