Claude-skill-registry dev-phaser-physics-matter

Matter.js physics: realistic bodies, constraints, and simulation

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/dev-phaser-physics-matter" ~/.claude/skills/majiayu000-claude-skill-registry-dev-phaser-physics-matter && rm -rf "$T"
manifest: skills/data/dev-phaser-physics-matter/SKILL.md
source content

Phaser Matter Physics

"Realistic physics simulation for puzzles and complex interactions."

Before/After: Manual Physics vs Matter.js Constraints

❌ Before: Manual Physics Simulation

// Manual physics without proper engine
interface Body {
  x: number; y: number;
  vx: number; vy: number;
  mass: number;
  angle: number;
  angularVelocity: number;
}

const bodies: Body[] = [];

function updatePhysics(dt: number) {
  // Apply gravity
  for (const body of bodies) {
    body.vy += 9.8 * dt;
  }

  // Check collisions (O(n²) complexity)
  for (let i = 0; i < bodies.length; i++) {
    for (let j = i + 1; j < bodies.length; j++) {
      if (checkCollision(bodies[i], bodies[j])) {
        resolveCollision(bodies[i], bodies[j]);
      }
    }
  }

  // Update positions
  for (const body of bodies) {
    body.x += body.vx * dt;
    body.y += body.vy * dt;
    body.angle += body.angularVelocity * dt;
  }
}

// Problems:
// - O(n²) collision detection
// - No realistic stacking
// - No constraints/joints
// - Manual collision resolution
// - No rotational physics

✅ After: Phaser Matter Physics

// Matter.js handles complex physics
export class GameScene extends Phaser.Scene {
  create() {
    // Enable Matter physics
    this.physics.world.setBounds(0, 0, 800, 600);

    // Create connected bodies with constraint
    const box1 = this.matter.add.image(300, 200, 'box');
    box1.setRectangle(40, 40);
    box1.setStatic(true);

    const box2 = this.matter.add.image(300, 300, 'box');
    box2.setRectangle(40, 40);

    // Create chain constraint (rope-like) - ONE line!
    this.matter.add.constraint(box1, box2, 100, 0.9, {
      render: { visible: true }
    });

    // Add spring constraint
    const springBox = this.matter.add.image(500, 200, 'box');
    this.matter.add.spring(
      { x: 500, y: 100 },
      springBox,
      100,
      0.001,
      { damping: 0.05 }
    );
  }
}

// Benefits:
// - Efficient SAT collision detection
// - Realistic stacking behavior
// - Built-in constraints (chains, springs, hinges)
// - Rotational physics included
// - Sleep optimization for performance

When to Use This Skill

Use when:

  • Building physics puzzle games
  • Need realistic collision and physics
  • Working with complex body shapes
  • Using constraints and joints
  • Simulating realistic object interactions

Quick Start

// Enable Matter physics in game config
physics: {
  default: 'matter',
  matter: {
    gravity: { y: 1 },
    debug: false
  }
}

// Create Matter sprite
const box = this.matter.add.image(400, 300, 'box');
box.setRectangle();

Decision Framework

NeedUse
Physics puzzlesMatter physics
Realistic stackingMatter with friction
Ropes/chainsConstraints
Complex shapesCustom body vertices
Simple platformerArcade instead

Progressive Guide

Level 1: Basic Matter Bodies

export class GameScene extends Phaser.Scene {
  create() {
    // Create image as Matter body
    const box = this.matter.add.image(400, 100, "box");
    box.setRectangle(); // Default: use texture size
    box.setBounce(0.5);
    box.setFriction(0.1);

    // Circle body
    const ball = this.matter.add.image(400, 200, "ball");
    ball.setCircle();
    ball.setBounce(0.8);

    // Static ground
    const ground = this.matter.add.image(400, 550, "ground");
    ground.setStatic(true);
    ground.setRectangle(800, 50);
  }
}

Level 2: Custom Body Shapes

create() {
  // Triangle body
  const triangle = this.matter.add.image(400, 100, 'triangle');
  triangle.setPolygon(0, 0, [
    { x: 0, y: -30 },
    { x: 30, y: 30 },
    { x: -30, y: 30 }
  ]);

  // Custom shape from vertices
  const rock = this.matter.add.image(400, 200, 'rock');
  rock.setPolygon(0, 0, [
    { x: -25, y: -20 },
    { x: 20, y: -25 },
    { x: 30, y: 10 },
    { x: 10, y: 30 },
    { x: -20, y: 25 }
  ]);

  // Compound body (multiple shapes)
  const compound = this.matter.add.image(400, 300, 'crate');
  compound.setRectangle(40, 40);
  compound.setBounce(0.2);
  compound.setFriction(0.5);
}

Level 3: Constraints and Joints

create() {
  const box1 = this.matter.add.image(300, 200, 'box');
  box1.setRectangle(40, 40);
  box1.setStatic(true);

  const box2 = this.matter.add.image(300, 300, 'box');
  box2.setRectangle(40, 40);

  // Create chain constraint (rope-like)
  this.matter.add.constraint(box1, box2, 100, 0.9, {
    render: { visible: true }
  });

  // Create hinge constraint
  const door = this.matter.add.image(400, 300, 'door');
  door.setRectangle(10, 100);
  this.matter.add.constraint(door, { x: 400, y: 250 }, 50, 1, {
    pointA: { x: 0, y: -50 },
    pointB: { x: 0, y: 0 }
  });

  // Spring constraint
  const springBox = this.matter.add.image(500, 200, 'box');
  springBox.setRectangle(40, 40);

  this.matter.add.spring(
    { x: 500, y: 100 },
    springBox,
    100,
    0.001,
    { damping: 0.05 }
  );
}

Level 4: Physics Events and Collision

create() {
  // Collision event
  this.matter.world.on('collisionstart', (event: any) => {
    event.pairs.forEach((pair: any) => {
      const bodyA = pair.bodyA;
      const bodyB = pair.bodyB;

      if (bodyA.label === 'player' && bodyB.label === 'coin') {
        this.collectCoin(bodyB.gameObject);
      }
    });
  });

  // Create labeled bodies
  const player = this.matter.add.image(400, 300, 'player');
  player.setRectangle();
  (player.body as MatterJS.Body).label = 'player';

  const coin = this.matter.add.image(400, 400, 'coin');
  coin.setCircle();
  (coin.body as MatterJS.Body).label = 'coin';
}

Level 5: Advanced Matter Physics

export class PhysicsScene extends Phaser.Scene {
  private bridgeParts: Phaser.Physics.Matter.Image[] = [];

  create() {
    this.createBridge();
    this.createRope();
    this.createPhysicsStack();
  }

  createBridge() {
    const startX = 200;
    const startY = 200;
    const plankCount = 8;
    const plankWidth = 60;
    const gap = 5;

    let previousPlank: Phaser.Physics.Matter.Image | null = null;

    for (let i = 0; i < plankCount; i++) {
      const x = startX + i * (plankWidth + gap);
      const y = startY;

      const plank = this.matter.add.image(x, y, "plank");
      plank.setRectangle(plankWidth, 20);
      plank.setBounce(0);
      plank.setFriction(0.8);

      this.bridgeParts.push(plank);

      if (i === 0 || i === plankCount - 1) {
        plank.setStatic(true);
      }

      if (previousPlank) {
        this.matter.add.constraint(
          previousPlank,
          plank,
          plankWidth + gap,
          0.9,
          {
            pointA: { x: plankWidth / 2, y: 0 },
            pointB: { x: -plankWidth / 2, y: 0 },
          },
        );
      }

      previousPlank = plank;
    }
  }

  createRope() {
    const links = 10;
    const linkLength = 20;
    const startX = 600;
    const startY = 100;

    let prevLink: Phaser.Physics.Matter.Image | null = null;

    for (let i = 0; i < links; i++) {
      const link = this.matter.add.circle(startX, startY + i * linkLength, 5);
      link.setBounce(0);
      link.setFriction(0.5);

      if (i === 0) {
        link.setStatic(true);
      }

      if (prevLink) {
        this.matter.add.constraint(prevLink, link, linkLength, 0.9);
      }

      prevLink = link;
    }
  }

  createPhysicsStack() {
    const stackSize = 5;
    const boxSize = 40;
    const startX = 500;
    const startY = 500;

    for (let row = 0; row < stackSize; row++) {
      for (let col = 0; col < stackSize - row; col++) {
        const x = startX + col * boxSize + row * (boxSize / 2);
        const y = startY - row * boxSize;

        const box = this.matter.add.image(x, y, "box");
        box.setRectangle(boxSize, boxSize);
        box.setBounce(0.1);
        box.setFriction(0.5);
        box.setDensity(0.001);
      }
    }
  }
}

Anti-Patterns

DON'T:

  • Use Matter for simple platformers - Arcade is better
  • Set high density on all objects - causes instability
  • Ignore sleep settings - kills performance
  • Create complex constraints without tuning
  • Mix body coordinates systems arbitrarily
  • Forget to set static on anchored objects

DO:

  • Use Matter for puzzles, stacking, complex interactions
  • Adjust density based on object type
  • Enable sleep for inactive bodies
  • Test constraint values iteratively
  • Keep coordinates consistent
  • Set static bodies for anchors/ground

Code Patterns

Body Configuration

const box = this.matter.add.image(400, 300, "box");

// Shape
box.setRectangle(width, height);
box.setCircle(radius);
box.setPolygon(x, y, vertices);
box.setTrapezoid(width, height, slope);

// Physics properties
box.setBounce(0.5); // Restitution
box.setFriction(0.1); // Surface friction
box.setFrictionStatic(0.5); // Static friction
box.setDensity(0.001); // Mass = density * area
box.setAngle(Math.PI / 4); // Rotation in radians

// State
box.setStatic(true); // Immovable
box.setSensor(true); // Detect but don't collide

Constraint Types

// Chain constraint (rigid-ish connection)
this.matter.add.constraint(bodyA, bodyB, length, stiffness);

// Spring constraint
this.matter.add.spring(bodyA, bodyB, length, stiffness, config);

// World constraint (anchor to point)
this.matter.add.constraint(body, { x, y }, length, stiffness);

// Hinge (rotational joint)
this.matter.add.constraint(bodyA, bodyB, length, stiffness, {
  pointA: { x: offsetX, y: offsetY },
  pointB: { x: offsetX, y: offsetY },
});

Matter vs Arcade Comparison

FeatureArcadeMatter
PerformanceFasterSlower
ComplexitySimpleComplex
CollisionAABB/OBBSAT/Polygon
ConstraintsBasicAdvanced
Best ForPlatformers, top-downPuzzles, realistic physics
Body TypesDynamic/StaticDynamic/Static/Sensor

Checklist

  • Matter physics enabled in config
  • Bodies properly shaped
  • Static bodies marked as static
  • Constraints tuned properly
  • Collision labels set for events
  • Density/bounce/friction configured
  • Sleep enabled for performance

Reference