mcpField
P
physics-matter

physics-matter

github.com/phaserjs开发40.6k774 次安装10 周前更新
代码仓库

关于 physics-matter

面向 Phaser 4 开发者的 Matter.js 物理技能,说明如何创建和配置刚体、约束及复合体,并处理碰撞和瓦片地图。还介绍传感器、碰撞筛选、指针拖拽与调试配置。

主要功能

  1. 01创建精灵、图像及自定义刚体
  2. 02配置重力、摩擦、速度和力
  3. 03创建约束、复合体与复合刚体
  4. 04碰撞事件、传感器和碰撞筛选
  5. 05瓦片地图物理与指针拖拽

适用场景

  1. 01为角色和环境设置刚体碰撞
  2. 02用约束构建弹簧、关节或链条
  3. 03实现拾取物和触发区域检测
  4. 04将瓦片地图碰撞转换为 Matter 刚体

安装

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

$npx skills add https://github.com/phaserjs/phaser --skill physics-matter

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

详细介绍

Matter.js 物理

在 Phaser 4 中设置并使用 Matter.js 物理 —— 支持刚体、复合刚体、约束、组合体、传感器、碰撞过滤、指针拖拽、Tilemap 集成和调试渲染的完整刚体物理。

关键源码路径: src/physics/matter-js/MatterPhysics.js, src/physics/matter-js/World.js, src/physics/matter-js/Factory.js, src/physics/matter-js/MatterSprite.js, src/physics/matter-js/MatterImage.js, src/physics/matter-js/MatterGameObject.js, src/physics/matter-js/PointerConstraint.js, src/physics/matter-js/MatterTileBody.js, src/physics/matter-js/components/, src/physics/matter-js/events/, src/physics/matter-js/typedefs/ 相关技能: ../game-setup-and-config/SKILL.md, ../sprites-and-images/SKILL.md, ../physics-arcade/SKILL.md, ../tilemaps/SKILL.md

快速开始

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.matter)

MatterPhysics 类是场景级插件。关键属性:

  • this.matter.add —— Factory,用于创建刚体、约束、游戏对象(自动添加到世界)。
  • this.matter.world —— World 实例,管理引擎、边界、调试渲染。继承 EventEmitter。
  • this.matter.body / bodies / composite / composites / constraint —— 直接引用 Matter.js 模块以进行底层使用。
  • this.matter.bodyBounds —— 用于按可视边界对齐刚体的辅助工具。
世界(this.matter.world)
  • engine —— MatterJS.Engine 实例。
  • localWorld —— 包含所有刚体和约束的 MatterJS.World 组合体。
  • enabled —— 布尔值;false 暂停模拟。 autoUpdate —— true = 引擎在每个游戏步更新。
  • walls —— { left, right, top, bottom } 边界墙刚体(或 null)。
世界配置(MatterWorldConfig)

在游戏或场景配置中通过 physics.matter 传入:

属性默认值用途
gravity{ x: 0, y: 1 }重力向量。设为 false 禁用
setBoundsfalsetrue 或 { x, y, width, height, thickness, left, right, top, bottom }
enableSleepingfalse允许刚体静止时休眠
positionIterations6位置求解精度
velocityIterations4速度求解精度
constraintIterations2约束稳定性
timing.timeScale10 冻结,0.5 慢动作
autoUpdatetrue每个游戏帧自动步进
debugfalsetrue 或 MatterDebugConfig 对象
runner{}使用 runner.fps 设置固定时间步
Matter 游戏对象

Phaser 提供了两个物理感知的游戏对象类,以及一个可为任意游戏对象添加物理的函数:

  • Phaser.Physics.Matter.Sprite —— 继承 Sprite,包含所有 Matter 组件。通过 this.matter.add.sprite(x, y, key, frame, options) 创建。支持动画。
  • Phaser.Physics.Matter.Image —— 继承 Image,包含所有 Matter 组件。通过 this.matter.add.image(x, y, key, frame, options) 创建。不支持动画,更轻量。
  • MatterGameObject(world, gameObject, options) —— 将所有 Matter 组件注入任意现有游戏对象。通过 this.matter.add.gameObject(mySprite, options) 创建。

MatterSprite 和 MatterImage 默认都使用与纹理尺寸匹配的矩形刚体。传入 options.shape 可覆盖。

Matter 组件(Mixin)

所有 Matter 游戏对象都混入了这些组件方法:

组件关键方法
VelocitysetVelocity(x, y)、setVelocityX(x)、setVelocityY(y)、getVelocity()、setAngularVelocity(v)、getAngularVelocity()、setAngularSpeed(s)、getAngularSpeed()
ForceapplyForce(vec2)、applyForceFrom(position, force)、thrust(speed)、thrustLeft(speed)、thrustRight(speed)、thrustBack(speed)
BouncesetBounce(value) -- 恢复系数,0 到 1
FrictionsetFriction(value, air?, fstatic?)、setFrictionAir(value)、setFrictionStatic(value)
MasssetMass(value)、setDensity(value)、centerOfMass(getter)
GravitysetIgnoreGravity(bool)
SensorsetSensor(bool)、isSensor()
StaticsetStatic(bool)、isStatic()
SleepsetToSleep()、setAwake()、setSleepThreshold(n)、setSleepEvents(start, end)
CollisionsetCollisionCategory(cat)、setCollisionGroup(group)、setCollidesWith(cats)、setOnCollide(cb)、setOnCollideEnd(cb)、setOnCollideActive(cb)、setOnCollideWith(body, cb)
SetBodysetRectangle(w, h, opts)、setCircle(r, opts)、setPolygon(r, sides, opts)、setTrapezoid(w, h, slope, opts)、setBody(config, opts)、setExistingBody(body)
TransformMatter body 与游戏对象之间的位置同步

常见模式

设置与世界配置
// 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();
创建 Matter Sprite 和 Image
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.
Body 配置选项(MatterBodyConfig)

作为 options 参数传递给任意工厂方法,或作为 Matter 游戏对象的选项:

  • label、isStatic、isSensor、angle(弧度)、timeScale、ignoreGravity、ignorePointer
  • density(默认 0.001,自动计算质量)、mass、restitution(弹力 0-1)
  • friction(0-1)、frictionAir(空气阻力)、frictionStatic(静止时的粘附力)
  • slop(重叠容差)、chamfer({ radius: 5 } 用于圆角)
  • collisionFilter: { category: 0x0001, mask: 0xFFFFFFFF, group: 0 }
  • onCollideCallback、onCollideEndCallback、onCollideActiveCallback
  • shape(用于游戏对象):{ type: 'circle', radius: 24 } 或 PhysicsEditor 数据
速度、力与推力
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);
复合 Body

将多个形状组合成单个 body。第一部分是父级。

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);

各部分共享位置、角度和速度。约束必须指向父 body,而不是各部分。

睡眠系统

处于静止状态的 body 可以进入睡眠以跳过模拟。需要在配置中设置 enableSleeping: true。

if (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/exit
碰撞类别与过滤

Matter 使用位掩码:category(该 body 属于哪个组,2 的幂)、mask(它与哪些类别碰撞)以及 group(快捷方式:相同正值 = 始终碰撞,相同负值 = 永不碰撞,0 = 使用 category/mask)。

const 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]);
摩擦类型

Matter.js 有三个独立的摩擦值:

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.world(它继承自 EventEmitter)上触发:

事件回调签名触发时机
'collisionstart'(event, bodyA, bodyB)两个刚体首次开始碰撞
'collisionactive'(event, bodyA, bodyB)两个刚体仍在碰撞
'collisionend'(event, bodyA, bodyB)两个刚体停止碰撞
'beforeupdate'(event)引擎更新步骤之前
'afterupdate'(event)引擎更新步骤之后
'beforeadd'(event)添加刚体/约束之前
'afteradd'(event)添加刚体/约束之后
'beforeremove'(event)移除刚体/约束之前
'afterremove'(event)移除刚体/约束之后
'dragstart'(body, part, constraint)指针开始拖动刚体
'drag'(body, constraint)指针正在拖动刚体
'dragend'(body, constraint)指针停止拖动刚体
'sleepstart'(event, body)刚体进入休眠(需要 setSleepEvents)
'sleepend'(event, body)刚体被唤醒(需要 setSleepEvents)
'pause'无世界已暂停
'resume'无世界已恢复

碰撞事件包含 event.pairs —— 一个碰撞对对象数组,带有 bodyA、bodyB、碰撞深度和法线。

API 快速参考

this.matter.add(工厂)

游戏对象: sprite(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?)(别名:joint、spring)、worldConstraint(body, len?, stiff?, opts?)、mouseSpring(opts?)(别名:pointerConstraint) 复合体: stack(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.matter(MatterPhysics)—— 批处理和实用方法

pause()、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.world(World)

setBounds(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.matter 上直接引用的 Matter.js 模块

body (Matter.Body)、bodies (Matter.Bodies)、composite (Matter.Composite)、composites (Matter.Composites)、constraint (Matter.Constraint)、detector、query、pair、pairs、resolver、axes、bounds、svg、vector、vertices

注意事项

  • 力的值非常小。 力使用 0.01-0.1,速度使用 1-15。不基于像素。
  • setBody/setRectangle/等会重置所有属性 —— 质量、摩擦力、碰撞过滤器、回调都会被清除。更改形状后需重新应用。
  • 约束必须针对父刚体,而不是复合刚体的 parts。
  • 最多 32 个碰撞类别。 每次 nextCategory() 使用一个位。
  • collisionFilter.group 会覆盖类别/掩码。 相同的正值 = 始终碰撞;相同的负值 = 永不碰撞;零/不同 = 使用类别/掩码。
  • 传感器仍需匹配的碰撞过滤器才能触发事件。
  • Matter 位置是质心,不是左上角(与 Arcade Physics 不同)。
  • 睡眠事件需要主动启用: sprite.setSleepEvents(true, true)。
  • Tilemap 转换需要先设置碰撞,通过 setCollisionByProperty 等。
  • 弹性使用 Math.max(bodyA.restitution, bodyB.restitution) —— 弹性更大的值胜出。
  • body.ignorePointer = true 会阻止指针约束拖拽该刚体。

源文件映射

路径用途
src/physics/matter-js/MatterPhysics.js场景插件(this.matter),暴露所有 Matter 模块
src/physics/matter-js/World.js世界管理、引擎、边界、调试、事件代理
src/physics/matter-js/Factory.jsthis.matter.add —— 所有创建方法
src/physics/matter-js/MatterSprite.js物理精灵(Sprite + Matter 组件)
src/physics/matter-js/MatterImage.js物理图像(Image + Matter 组件)
src/physics/matter-js/MatterGameObject.js将 Matter 组件注入任何游戏对象
src/physics/matter-js/MatterTileBody.js用 Matter 刚体包装 Tile
src/physics/matter-js/PointerConstraint.js点击并拖拽刚体约束
src/physics/matter-js/BodyBounds.js按视觉边界对齐刚体
src/physics/matter-js/PhysicsEditorParser.js将 PhysicsEditor JSON 解析为刚体
src/physics/matter-js/components/混入:Velocity、Force、Collision、SetBody、Sensor、Bounce、Friction、Mass、Gravity、Static、Sleep、Transform
src/physics/matter-js/events/事件常量(COLLISION_START、DRAG_START、SLEEP_START 等)
src/physics/matter-js/typedefs/类型定义:MatterWorldConfig、MatterBodyConfig、MatterCollisionFilter、MatterConstraintConfig 等
src/physics/matter-js/lib/捆绑的 Matter.js 库模块

评论

支持 Markdown