在Phaser.js游戏开发中,物理组可以批量管理具有相同物理属性的对象,而给物理组中的子对象添加拖拽功能,能实现很多丰富的交互场景,比如可拖动的关卡道具、可调整位置的障碍物等。下面我们就一步步实现这个功能。

核心实现思路
要实现物理组中的可拖拽子对象,需要完成三个核心步骤:首先创建物理组,然后向组中添加带有物理属性的子对象,最后为子对象绑定拖拽事件,同时适配物理引擎的碰撞和移动逻辑,避免拖拽时物理属性冲突。
环境准备
首先确保你已经引入了Phaser.js库,这里我们使用Phaser 3版本,基础的游戏场景结构如下:
// 基础场景结构
class MainScene extends Phaser.Scene {
constructor() {
super({ key: 'MainScene' });
}
preload() {
// 预加载资源,这里使用默认图形,无需额外加载
}
create() {
// 后续逻辑写在这里
}
update() {
// 帧更新逻辑
}
}
// 游戏配置
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
scene: [MainScene],
physics: {
default: 'arcade',
arcade: {
gravity: { y: 300 },
debug: false
}
}
};
// 启动游戏
new Phaser.Game(config);
创建物理组并添加子对象
首先在create方法中创建物理组,然后向组中添加多个矩形对象作为子对象,同时给每个子对象开启物理属性:
create() {
// 创建物理组,使用arcade物理引擎
this.physicsGroup = this.physics.add.group();
// 向物理组中添加3个子对象
for (let i = 0; i < 3; i++) {
// 创建矩形对象,设置位置和尺寸
const rect = this.add.rectangle(200 + i * 150, 200, 60, 60, 0x00ff00);
// 给矩形开启物理属性,设置为物理组的子对象
this.physicsGroup.add(rect);
// 设置物理属性:可碰撞、固定旋转
rect.body.setCollideWorldBounds(true);
rect.body.setImmovable(false);
rect.body.setBounce(0.2);
}
}
为子对象绑定拖拽事件
Phaser.js内置了拖拽管理器,我们可以为每个子对象开启拖拽,并在拖拽的不同阶段处理物理属性的适配:
create() {
this.physicsGroup = this.physics.add.group();
for (let i = 0; i < 3; i++) {
const rect = this.add.rectangle(200 + i * 150, 200, 60, 60, 0x00ff00);
this.physicsGroup.add(rect);
rect.body.setCollideWorldBounds(true);
rect.body.setBounce(0.2);
// 开启拖拽功能
rect.setInteractive();
this.input.setDraggable(rect);
// 拖拽开始事件
rect.on('dragstart', (pointer) => {
// 拖拽开始时,暂时关闭物理重力影响,避免拖拽时被重力拉走
rect.body.setGravityY(0);
// 设置半透明效果提示正在拖拽
rect.setAlpha(0.7);
});
// 拖拽进行中事件
rect.on('drag', (pointer, dragX, dragY) => {
// 更新子对象的位置为拖拽的位置
rect.x = dragX;
rect.y = dragY;
// 同步更新物理体的位置
rect.body.x = dragX - rect.width / 2;
rect.body.y = dragY - rect.height / 2;
});
// 拖拽结束事件
rect.on('dragend', () => {
// 拖拽结束后恢复重力
rect.body.setGravityY(300);
// 恢复不透明度
rect.setAlpha(1);
});
}
// 设置物理组内的对象互相碰撞
this.physics.add.collider(this.physicsGroup, this.physicsGroup);
}
常见问题说明
- 拖拽时物理体位置不同步:需要在
drag事件中同时更新对象本身的位置和物理体的位置,因为物理体的坐标和显示对象的坐标原点不同。 - 拖拽时对象被重力拉走:在拖拽开始时需要暂时关闭重力,拖拽结束后再恢复,避免拖拽过程中物理引擎干扰位置更新。
- 物理组碰撞失效:需要确保物理组中的子对象都正确添加了物理属性,并且调用了
collider方法设置碰撞规则。
完整示例代码
以下是完整的可运行代码,直接复制到HTML文件中即可看到效果:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Phaser.js物理组可拖拽子对象</title>
<script src="https://cdn.jsdelivr.net/npm/phaser@3.60.0/dist/phaser.min.js"></script>
</head>
<body>
<script>
class MainScene extends Phaser.Scene {
constructor() {
super({ key: 'MainScene' });
}
preload() {}
create() {
this.physicsGroup = this.physics.add.group();
for (let i = 0; i < 3; i++) {
const rect = this.add.rectangle(200 + i * 150, 200, 60, 60, 0x00ff00);
this.physicsGroup.add(rect);
rect.body.setCollideWorldBounds(true);
rect.body.setBounce(0.2);
rect.setInteractive();
this.input.setDraggable(rect);
rect.on('dragstart', () => {
rect.body.setGravityY(0);
rect.setAlpha(0.7);
});
rect.on('drag', (pointer, dragX, dragY) => {
rect.x = dragX;
rect.y = dragY;
rect.body.x = dragX - rect.width / 2;
rect.body.y = dragY - rect.height / 2;
});
rect.on('dragend', () => {
rect.body.setGravityY(300);
rect.setAlpha(1);
});
}
this.physics.add.collider(this.physicsGroup, this.physicsGroup);
}
update() {}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
scene: [MainScene],
physics: {
default: 'arcade',
arcade: {
gravity: { y: 300 },
debug: false
}
}
};
new Phaser.Game(config);
</script>
</body>
</html>