Phaser 物理引擎 #
物理引擎概述 #
Phaser 内置了两个物理引擎:Arcade Physics(轻量级)和 Matter.js(真实物理)。
text
┌─────────────────────────────────────────────────────────────┐
│ 物理引擎对比 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Arcade Physics │
│ ├── 轻量级,高性能 │
│ ├── AABB 碰撞检测 │
│ ├── 适合简单平台游戏 │
│ ├── 无旋转碰撞 │
│ └── 学习成本低 │
│ │
│ Matter.js │
│ ├── 真实物理模拟 │
│ ├── 复杂形状碰撞 │
│ ├── 支持旋转、约束 │
│ ├── 适合物理益智游戏 │
│ └── 功能强大 │
│ │
└─────────────────────────────────────────────────────────────┘
Arcade Physics #
配置 #
javascript
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
physics: {
default: 'arcade',
arcade: {
gravity: { y: 300 },
debug: false
}
},
scene: GameScene
};
创建物理精灵 #
javascript
create() {
this.player = this.physics.add.sprite(100, 450, 'dude');
this.player.setBounce(0.2);
this.player.setCollideWorldBounds(true);
this.player.setGravityY(300);
this.player.setVelocity(100, 200);
this.player.setVelocityX(160);
this.player.setVelocityY(-330);
}
静态物理体 #
javascript
create() {
this.platforms = this.physics.add.staticGroup();
this.platforms.create(400, 568, 'ground').setScale(2).refreshBody();
this.platforms.create(600, 400, 'ground');
this.platforms.create(50, 250, 'ground');
this.platforms.create(750, 220, 'ground');
}
动态物理体 #
javascript
create() {
this.stars = this.physics.add.group({
key: 'star',
repeat: 11,
setXY: { x: 12, y: 0, stepX: 70 }
});
this.stars.children.iterate((child) => {
child.setBounceY(Phaser.Math.FloatBetween(0.4, 0.8));
});
}
物理体属性 #
javascript
const sprite = this.physics.add.sprite(100, 100, 'player');
sprite.setVelocity(x, y);
sprite.setVelocityX(value);
sprite.setVelocityY(value);
sprite.setAcceleration(x, y);
sprite.setAccelerationX(value);
sprite.setAccelerationY(value);
sprite.setBounce(x, y);
sprite.setBounceX(value);
sprite.setBounceY(value);
sprite.setDrag(x, y);
sprite.setDragX(value);
sprite.setDragY(value);
sprite.setGravity(x, y);
sprite.setGravityX(value);
sprite.setGravityY(value);
sprite.setMass(value);
sprite.setAngularVelocity(value);
sprite.setAngularDrag(value);
sprite.setImmovable(true);
sprite.setCollideWorldBounds(true);
sprite.body.allowGravity = false;
sprite.setFriction(x, y);
sprite.setMaxVelocity(x, y);
sprite.setOffset(x, y);
sprite.setSize(width, height);
sprite.setCircle(radius, offsetX, offsetY);
碰撞检测 #
javascript
create() {
this.physics.add.collider(this.player, this.platforms);
this.physics.add.overlap(this.player, this.stars, this.collectStar, null, this);
}
collectStar(player, star) {
star.disableBody(true, true);
this.score += 10;
this.scoreText.setText('Score: ' + this.score);
}
碰撞回调 #
javascript
create() {
this.physics.add.collider(this.player, this.platforms, null, this.checkCollision, this);
}
checkCollision(player, platform) {
return player.y < platform.y;
}
分组碰撞 #
javascript
create() {
this.enemies = this.physics.add.group();
this.bullets = this.physics.add.group();
this.physics.add.collider(this.enemies, this.platforms);
this.physics.add.collider(this.bullets, this.platforms, this.bulletHitPlatform, null, this);
this.physics.add.overlap(this.player, this.enemies, this.hitEnemy, null, this);
this.physics.add.overlap(this.bullets, this.enemies, this.bulletHitEnemy, null, this);
}
物理体调试 #
javascript
const config = {
physics: {
default: 'arcade',
arcade: {
debug: true,
debug: {
showBody: true,
showStaticBody: true,
showVelocity: true,
bodyColor: 0xff0000,
staticBodyColor: 0x0000ff,
velocityColor: 0x00ff00
}
}
}
};
手动碰撞检测 #
javascript
update() {
if (this.physics.world.overlap(this.player, this.enemy)) {
this.handleCollision();
}
if (this.physics.world.collide(this.player, this.platforms)) {
console.log('Colliding with platform');
}
}
射线检测 #
javascript
const ray = new Phaser.Geom.Line(
this.player.x, this.player.y,
this.enemy.x, this.enemy.y
);
const hit = this.physics.raycast(
this.player.x, this.player.y,
this.enemy.x, this.enemy.y,
this.platforms
);
移动向目标 #
javascript
this.physics.moveTo(this.player, targetX, targetY, speed);
this.physics.moveToObject(this.player, this.enemy, 200);
this.physics.moveToPointer(this.player, 200);
const velocity = this.physics.velocityFromAngle(45, 200);
this.player.setVelocity(velocity.x, velocity.y);
const angle = this.physics.angleToXY(this.player, targetX, targetY);
const velocity = this.physics.velocityFromAngle(angle * 180 / Math.PI, 200);
Matter.js #
配置 #
javascript
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
physics: {
default: 'matter',
matter: {
gravity: { y: 1 },
debug: false
}
},
scene: GameScene
};
创建物理体 #
javascript
create() {
this.player = this.matter.add.sprite(100, 100, 'player');
this.player.setFriction(0.1);
this.player.setBounce(0.5);
this.player.setDensity(0.01);
this.matter.add.rectangle(400, 550, 800, 50, { isStatic: true });
this.matter.add.circle(400, 300, 50);
this.matter.add.polygon(400, 200, 6, 50);
}
物理体形状 #
javascript
this.matter.add.rectangle(x, y, width, height, options);
this.matter.add.circle(x, y, radius, options);
this.matter.add.polygon(x, y, sides, radius, options);
this.matter.add.trapezoid(x, y, width, height, slope, options);
this.matter.add.fromVertices(x, y, vertexSets, options);
this.matter.add.gameObject(sprite, {
shape: {
type: 'polygon',
sides: 6,
radius: 50
}
});
静态物体 #
javascript
this.matter.add.rectangle(400, 550, 800, 50, {
isStatic: true
});
this.matter.add.rectangle(400, 300, 200, 20, {
isStatic: true,
angle: Math.PI / 4
});
物理属性 #
javascript
const body = this.matter.add.circle(400, 300, 50);
body.setDensity(0.01);
body.setMass(10);
body.setFriction(0.1);
body.setFrictionAir(0.01);
body.setFrictionStatic(0.5);
body.setBounce(0.8);
body.setVelocity({ x: 10, y: 0 });
body.setAngularVelocity(0.1);
body.setPosition({ x: 100, y: 100 });
body.setAngle(Math.PI / 4);
body.setStatic(true);
body.setSensor(true);
body.setFixedRotation(true);
约束 #
javascript
const bodyA = this.matter.add.circle(200, 200, 20);
const bodyB = this.matter.add.circle(300, 200, 20);
this.matter.add.constraint(bodyA, bodyB, 100, 0.1);
this.matter.add.spring(bodyA, bodyB, 100, 0.1);
this.matter.add.worldConstraint(body, 200, 0.1, {
pointA: { x: 400, y: 100 }
});
碰撞事件 #
javascript
create() {
this.matter.world.on('collisionstart', (event) => {
event.pairs.forEach((pair) => {
const bodyA = pair.bodyA;
const bodyB = pair.bodyB;
console.log('Collision between:', bodyA.label, bodyB.label);
});
});
this.matter.world.on('collisionactive', (event) => {
});
this.matter.world.on('collisionend', (event) => {
});
}
碰撞分类 #
javascript
const CATEGORY_PLAYER = 0x0001;
const CATEGORY_ENEMY = 0x0002;
const CATEGORY_PLATFORM = 0x0004;
this.player = this.matter.add.sprite(100, 100, 'player', null, {
collisionFilter: {
category: CATEGORY_PLAYER,
mask: CATEGORY_ENEMY | CATEGORY_PLATFORM
}
});
this.enemy = this.matter.add.sprite(300, 100, 'enemy', null, {
collisionFilter: {
category: CATEGORY_ENEMY,
mask: CATEGORY_PLAYER | CATEGORY_PLATFORM
}
});
this.platform = this.matter.add.rectangle(400, 550, 800, 50, {
isStatic: true,
collisionFilter: {
category: CATEGORY_PLATFORM,
mask: CATEGORY_PLAYER | CATEGORY_ENEMY
}
});
传感器 #
javascript
const sensor = this.matter.add.rectangle(400, 300, 100, 100, {
isSensor: true,
isStatic: true,
label: 'trigger'
});
this.matter.world.on('collisionstart', (event) => {
event.pairs.forEach((pair) => {
if (pair.bodyA.label === 'trigger' || pair.bodyB.label === 'trigger') {
console.log('Entered trigger zone');
}
});
});
复合体 #
javascript
const car = this.matter.add.rectangle(400, 300, 100, 40);
const wheelA = this.matter.add.circle(370, 330, 15);
const wheelB = this.matter.add.circle(430, 330, 15);
const compound = this.matter.body.create({
parts: [car, wheelA, wheelB]
});
this.matter.add.constraint(wheelA, car, 30, 0.1, {
pointA: { x: 0, y: 0 },
pointB: { x: -30, y: 30 }
});
Matter.js 调试 #
javascript
const config = {
physics: {
default: 'matter',
matter: {
debug: {
showBody: true,
showStaticBody: true,
showInternalEdges: true,
showVelocity: true,
showCollisions: true,
showAxes: true,
showAngleIndicator: true,
bodyColor: 0xff0000,
staticBodyColor: 0x0000ff
}
}
}
};
物理体类型 #
Arcade Physics 类型 #
text
┌─────────────────────────────────────────────────────────────┐
│ Arcade 物理体类型 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Dynamic Body │
│ ├── 受重力影响 │
│ ├── 受速度影响 │
│ ├── 可以移动 │
│ └── 适合玩家、敌人 │
│ │
│ Static Body │
│ ├── 不受重力影响 │
│ ├── 固定位置 │
│ ├── 高性能 │
│ └── 适合地面、平台 │
│ │
│ Kinematic Body │
│ ├── 不受重力影响 │
│ ├── 可以移动 │
│ ├── 不受碰撞影响 │
│ └── 适合移动平台 │
│ │
└─────────────────────────────────────────────────────────────┘
Matter.js 类型 #
javascript
const dynamic = this.matter.add.circle(400, 100, 50);
const static = this.matter.add.rectangle(400, 550, 800, 50, {
isStatic: true
});
const kinematic = this.matter.add.rectangle(400, 300, 100, 20, {
isStatic: true,
ignoreGravity: true
});
完整示例 #
平台游戏 #
javascript
class PlatformGame extends Phaser.Scene {
constructor() {
super({ key: 'PlatformGame' });
}
preload() {
this.load.image('sky', 'assets/sky.png');
this.load.image('ground', 'assets/platform.png');
this.load.image('star', 'assets/star.png');
this.load.image('bomb', 'assets/bomb.png');
this.load.spritesheet('dude', 'assets/dude.png', {
frameWidth: 32,
frameHeight: 48
});
}
create() {
this.add.image(400, 300, 'sky');
this.platforms = this.physics.add.staticGroup();
this.platforms.create(400, 568, 'ground').setScale(2).refreshBody();
this.platforms.create(600, 400, 'ground');
this.platforms.create(50, 250, 'ground');
this.platforms.create(750, 220, 'ground');
this.player = this.physics.add.sprite(100, 450, 'dude');
this.player.setBounce(0.2);
this.player.setCollideWorldBounds(true);
this.anims.create({
key: 'left',
frames: this.anims.generateFrameNumbers('dude', { start: 0, end: 3 }),
frameRate: 10,
repeat: -1
});
this.anims.create({
key: 'turn',
frames: [ { key: 'dude', frame: 4 } ],
frameRate: 20
});
this.anims.create({
key: 'right',
frames: this.anims.generateFrameNumbers('dude', { start: 5, end: 8 }),
frameRate: 10,
repeat: -1
});
this.cursors = this.input.keyboard.createCursorKeys();
this.stars = this.physics.add.group({
key: 'star',
repeat: 11,
setXY: { x: 12, y: 0, stepX: 70 }
});
this.stars.children.iterate((child) => {
child.setBounceY(Phaser.Math.FloatBetween(0.4, 0.8));
});
this.score = 0;
this.scoreText = this.add.text(16, 16, 'Score: 0', {
fontSize: '32px',
fill: '#fff'
});
this.physics.add.collider(this.player, this.platforms);
this.physics.add.collider(this.stars, this.platforms);
this.physics.add.overlap(this.player, this.stars, this.collectStar, null, this);
}
update() {
if (this.cursors.left.isDown) {
this.player.setVelocityX(-160);
this.player.anims.play('left', true);
} else if (this.cursors.right.isDown) {
this.player.setVelocityX(160);
this.player.anims.play('right', true);
} else {
this.player.setVelocityX(0);
this.player.anims.play('turn');
}
if (this.cursors.up.isDown && this.player.body.touching.down) {
this.player.setVelocityY(-330);
}
}
collectStar(player, star) {
star.disableBody(true, true);
this.score += 10;
this.scoreText.setText('Score: ' + this.score);
}
}
下一步 #
现在你已经掌握了物理引擎,接下来学习 动画系统,了解如何创建流畅的游戏动画!
最后更新:2026-03-29