mcpField
P
phaser-matter

phaser-matter

github.com/yakoub-ai开发29192 次安装9 周前更新
代码仓库

关于 phaser-matter

phaser-matter 是面向使用 Matter.js 物理引擎的 Phaser 4 开发者的参考技能,讲解复杂碰撞形状、约束、传感器及 Arcade Physics 之外的碰撞处理。

主要功能

  1. 01创建多边形和复合刚体
  2. 02设置刚体属性并施加力
  3. 03处理物理世界碰撞事件
  4. 04通过类别筛选碰撞
  5. 05使用传感器和约束

适用场景

  1. 01构建非矩形碰撞的游戏
  2. 02用关节或弹簧连接刚体
  3. 03创建触发区域和碰撞规则

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/yakoub-ai/phaser4-gamedev --skill phaser-matter

安装后在 Claude Code 中输入/phaser-matter确认已加载。

附带文件references/matter-api.md

详细介绍

Phaser 4 Matter 物理引擎

Matter.js 是 Phaser 的第二个内置物理引擎。当 Arcade Physics 无法满足需求时使用它,尤其适用于非矩形碰撞形状、真实物理约束或布娃娃式模拟。

Arcade 与 Matter:何时使用哪种

Arcade Physics(默认选择):

  • 仅支持 AABB 碰撞:矩形和圆形
  • 简单的碰撞箱,性能出色
  • 使用 body.blocked.down 检测是否接触地面
  • 可满足 95% 的 2D 游戏需求

Matter Physics(有需要时使用):

  • 凸多边形形状和复杂的凹形物体
  • 复合物体(多个形状焊接在一起)
  • 逼真的约束:铰链、弹簧、定距杆
  • 布娃娃物理和可破坏物体
  • 具备精确物理碰撞事件的传感器区域

**原则:**默认使用 Arcade。仅在需要非矩形碰撞形状或真实关节时切换到 Matter。

启用 Matter Physics

// In GameConfig:
const config: Phaser.Types.Core.GameConfig = {
  physics: {
    default: 'matter',
    matter: {
      gravity: { x: 0, y: 1 },  // normalized: 1 = earth-like downward pull
      debug: false,               // true renders wireframes — essential during development
    },
  },
};

开发期间设置 debug: true。Matter 的线框叠加层会准确显示每个物体的形状、应用的偏移量以及约束的连接位置。

创建物体

矩形(默认)
// matter.add.image creates a MatterImage (static by default if you call .setStatic)
const box = this.matter.add.image(x, y, 'box');
圆形
const ball = this.matter.add.image(x, y, 'ball').setCircle(radius);
// Optional: pass options as second arg to setCircle
const ball2 = this.matter.add.image(x, y, 'ball').setCircle(32, { restitution: 0.8 });
凸多边形
const hex = this.matter.add.image(x, y, 'hex')
  .setBody({ type: 'polygon', sides: 6, radius: 30 });
自定义顶点(fromVertices)
// Vertices defined as array of {x,y} relative to body center
const verts = [{ x: 0, y: -30 }, { x: 20, y: 10 }, { x: -20, y: 10 }];
const arrow = this.matter.add.image(x, y, 'arrow')
  .setBody({ type: 'fromVertices', verts: verts });

注意:Matter 会通过 poly-decomp 自动将凹多边形分解为凸多边形。确保顶点的顺序一致(顺时针或逆时针)。

复合物体(多个形状合为一个物体)
// Use matter.bodies to build parts, then merge into a compound
const Body    = (this.matter as any).body as typeof MatterJS.Body;
const Bodies  = (this.matter as any).bodies as typeof MatterJS.Bodies;

const torso  = Bodies.rectangle(x, y, 30, 50);
const head   = Bodies.circle(x, y - 40, 15);
const compound = Body.create({ parts: [torso, head] });

const player = this.matter.add.image(x, y, 'player').setExistingBody(compound);
静态物体(不可移动)
const ground = this.matter.add.image(x, y, 'ground').setStatic(true);
// Or create a static rectangle directly:
this.matter.add.rectangle(x, y, width, height, { isStatic: true });

物体属性

所有属性设置器都会返回 MatterGameObject,以便链式调用。

const body = this.matter.add.image(x, y, 'crate');

body.setFriction(0.1);        // surface friction: 0 = ice, 1 = very sticky
body.setFrictionAir(0.05);    // air resistance / velocity damping per step
body.setFrictionStatic(0.5);  // static friction (resistance to starting motion)
body.setRestitution(0.8);     // bounciness: 0 = no bounce, 1 = perfect elastic
body.setMass(5);              // kilograms — affects momentum transfer
body.setDensity(0.002);       // alternative to setMass; mass derived from area
body.setIgnoreGravity(true);  // float independent of world gravity
body.setFixedRotation();      // prevent rotation (essential for player characters!)
body.setSensor(true);         // passes through objects but still fires collision events
body.setVelocity(vx, vy);     // teleport velocity (px/frame, not px/sec like Arcade)
body.setAngularVelocity(0.1); // spin speed in radians per step

**平台游戏的关键事项:**始终对玩家物体调用 setFixedRotation()。否则,胶囊形物体会在接触表面时滚动并倾倒。

空气摩擦参考值:0.01 = 轻飘飘/太空感,0.05 = 正常,0.15 = 沉重/迟缓。

施加力

// Apply force at body center (world coordinates)
// Force is a vector in Matter units (very small numbers — tune empirically)
this.matter.applyForce(body.body as MatterJS.Body, { x: 0.01, y: -0.05 });

// Apply force at an offset point (creates torque)
this.matter.applyForceFromPosition(body.body as MatterJS.Body,
  { x: body.x + 10, y: body.y },
  { x: 0, y: -0.03 }
);

对于冲量式移动(跳跃、击退),直接设置速度,而不是施加力:

body.setVelocity(0, -10); // instant upward jump

碰撞检测

Matter 在世界上使用全局碰撞事件,而不是逐物体回调。

// collisionstart fires once when two bodies begin touching
this.matter.world.on(
  'collisionstart',
  (event: Phaser.Physics.Matter.Events.CollisionStartEvent) => {
    event.pairs.forEach(pair => {
      const { bodyA, bodyB } = pair;
      const objA = bodyA.gameObject as Phaser.Physics.Matter.Image | null;
      const objB = bodyB.gameObject as Phaser.Physics.Matter.Image | null;
      if (objA && objB) {
        // Both bodies have Phaser game objects attached
        console.log('Collision:', objA.texture.key, 'hit', objB.texture.key);
      }
    });
  }
);

// collisionactive fires every step while bodies remain touching
this.matter.world.on('collisionactive', (event) => { /* ... */ });

// collisionend fires once when bodies separate
this.matter.world.on('collisionend', (event) => { /* ... */ });

复合物体:每个部件都是独立的 Matter 物体。子部件的 bodyA.gameObject 会是 null;只有复合根物体才有 gameObject 引用。

碰撞过滤(类别和掩码)

使用位掩码类别控制哪些物体会相互碰撞。

// Define categories as powers of 2 (bitmask flags)
const CAT_PLAYER = 0x0001;
const CAT_ENEMY  = 0x0002;
const CAT_BULLET = 0x0004;
const CAT_WALL   = 0x0008;
const CAT_PICKUP = 0x0010;

// Player: collides with enemies and walls; own bullets pass through
player.setCollisionCategory(CAT_PLAYER);
player.setCollidesWith([CAT_ENEMY, CAT_WALL]);

// Enemy: collides with player, walls, and bullets
enemy.setCollisionCategory(CAT_ENEMY);
enemy.setCollidesWith([CAT_PLAYER, CAT_BULLET, CAT_WALL]);

// Bullet: hits enemies and walls, not the player who fired it
bullet.setCollisionCategory(CAT_BULLET);
bullet.setCollidesWith([CAT_ENEMY, CAT_WALL]);

// Pickup (coin/health): only player can collect it
pickup.setCollisionCategory(CAT_PICKUP);
pickup.setCollidesWith([CAT_PLAYER]);

每个世界最多支持 32 个类别。对于无论掩码如何设置都绝不应相互碰撞的物体(例如,同一组中的所有敌人物体使用共享的负组 ID,以跳过自碰撞),请使用碰撞过滤器的 group 属性。

传感器(触发区域)

传感器可检测重叠,但不会产生物理响应,适用于检查点、伤害区域和仇恨范围。

// Sensor rectangle (static)
const zone = this.matter.add.rectangle(x, y, width, height, {
  isSensor: true,
  isStatic: true,
  label: 'checkpoint-zone',  // label helps identify bodies in collision events
});

// Detect entries
this.matter.world.on('collisionstart', (event) => {
  event.pairs.forEach(pair => {
    const labels = [pair.bodyA.label, pair.bodyB.label];
    if (labels.includes('checkpoint-zone')) {
      const other = pair.bodyA.label === 'checkpoint-zone' ? pair.bodyB : pair.bodyA;
      if (other.gameObject) {
        // Player entered the zone
      }
    }
  });
});

约束(关节)

约束通过杆、弹簧或铰链连接两个物体(或将一个物体连接到固定的世界坐标点)。

// Distance constraint — rigid rod between two bodies
const rod = this.matter.add.constraint(
  bodyA.body as MatterJS.Body,
  bodyB.body as MatterJS.Body,
  100,    // target length (pixels)
  0.9     // stiffness: 1 = perfectly rigid, 0.01 = very springy
);

// Spring constraint — same but low stiffness
const spring = this.matter.add.constraint(
  bodyA.body as MatterJS.Body,
  bodyB.body as MatterJS.Body,
  80,     // rest length
  0.02    // stiffness (spring-like)
  // Note: damping defaults to 0; set options.damping to prevent oscillation
);

// Pin constraint — hinge a body to a fixed world point
const hinge = this.matter.add.worldConstraint(
  pendulum.body as MatterJS.Body,
  0,     // length 0 = pivot at the anchor point
  1,     // stiffness
  {
    pointA: { x: 400, y: 100 },  // fixed world position
    pointB: { x: 0, y: -40 },    // offset on the body (pivot attachment)
  }
);
约束选项
interface MatterConstraintConfig {
  bodyA?: MatterJS.Body;
  bodyB?: MatterJS.Body;
  pointA?: { x: number; y: number };  // attachment offset on bodyA
  pointB?: { x: number; y: number };  // attachment offset on bodyB
  length?: number;       // rest length; null = current distance
  stiffness?: number;    // 0.01–1
  damping?: number;      // 0–1, reduces oscillation; 0.1 is a good starting point
  label?: string;
}

移除物体和约束

// Destroy a Matter game object (removes body from world automatically)
sprite.destroy();

// Remove a constraint
this.matter.world.removeConstraint(rod);

// Remove a raw MatterJS body (not a game object)
this.matter.world.remove(rawBody);

使用 Matter 制作平台游戏

Matter 平台游戏需要格外注意,因为 Matter 中不存在 body.blocked.down,应改用碰撞事件。

class Player extends Phaser.Physics.Matter.Sprite {
  private onGround = false;

  constructor(scene: Phaser.Scene, x: number, y: number) {
    super(scene.matter.world, x, y, 'player');
    scene.add.existing(this);
    this.setFixedRotation();        // prevent capsule rolling
    this.setFrictionAir(0.05);
    this.setCollisionCategory(0x0001);

    // Track ground contact via sensor at feet
    const sensor = (scene.matter as any).bodies.rectangle(0, 20, 20, 4, { isSensor: true });
    const compound = (scene.matter as any).body.create({
      parts: [this.body, sensor],
    });
    this.setExistingBody(compound, true);

    scene.matter.world.on('collisionstart', (ev: any) => {
      ev.pairs.forEach((p: any) => {
        if (p.bodyA === sensor || p.bodyB === sensor) this.onGround = true;
      });
    });
    scene.matter.world.on('collisionend', (ev: any) => {
      ev.pairs.forEach((p: any) => {
        if (p.bodyA === sensor || p.bodyB === sensor) this.onGround = false;
      });
    });
  }

  jump(): void {
    if (this.onGround) this.setVelocityY(-10);
  }
}

世界配置

// Pause/resume simulation
this.matter.world.pause();
this.matter.world.resume();

// Change gravity at runtime
this.matter.world.setGravity(0, 2);  // doubled gravity
this.matter.world.setGravity(0, 0);  // zero-g

// Run one manual step (useful for deterministic replays)
this.matter.world.step(16.666);  // ms per step

// Set world bounds (creates static boundary bodies)
this.matter.world.setBounds(0, 0, worldWidth, worldHeight);

调试渲染

// Enable debug wireframes in config (recommended during development):
matter: { debug: true }

// Or toggle at runtime:
this.matter.world.drawDebug = !this.matter.world.drawDebug;
// Re-create the debug renderer if toggling on after init:
this.matter.world.createDebugGraphic();

其他资源

参考文件
  • references/matter-api.md — 完整的 MatterPhysics 插件方法、MatterGameObject 混入、MatterJS.Body 静态方法、约束选项、碰撞事件结构、CollisionFilter 接口

评论

支持 Markdown