本文目录导读:

经典「躲避星星」小游戏(核心:物理引擎与碰撞检测)
这是 Phaser 官方教程的升级版,适合快速上手。
游戏玩法:控制角色移动,收集星星得分,碰到炸弹则游戏结束。
// 场景配置
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
physics: {
default: 'arcade',
arcade: {
gravity: { y: 0 }, // 无重力,用于俯视游戏
debug: false
}
},
scene: {
preload: preload,
create: create,
update: update
}
};
let player;
let stars;
let bombs;
let cursors;
let score = 0;
let scoreText;
// 资源加载
function preload() {
this.load.image('sky', 'assets/sky.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 });
}
// 游戏初始化
function create() {
// 设置背景
this.add.image(400, 300, 'sky');
// 创建玩家(带动画)
player = this.physics.add.sprite(100, 450, 'dude');
player.setBounce(0.2);
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
});
// 创建星星组(12个)
stars = this.physics.add.group({
key: 'star',
repeat: 11,
setXY: { x: 12, y: 0, stepX: 70 }
});
stars.children.iterate((child) => {
child.setBounceY(Phaser.Math.FloatBetween(0.4, 0.8));
});
// 创建炸弹组(后面动态生成)
bombs = this.physics.add.group();
// 碰撞检测
this.physics.add.collider(player, stars);
this.physics.add.collider(player, bombs);
this.physics.add.collider(stars, bombs);
// 重叠检测(收集星星)
this.physics.add.overlap(player, stars, collectStar, null, this);
// 输入控制
cursors = this.input.keyboard.createCursorKeys();
// 分数显示
scoreText = this.add.text(16, 16, '分数: 0', { fontSize: '32px', fill: '#fff' });
}
// 收集星星
function collectStar(player, star) {
star.disableBody(true, true);
score += 10;
scoreText.setText('分数: ' + score);
// 生成炸弹
if (stars.countActive(true) === 0) {
stars.children.iterate((child) => {
child.enableBody(true, child.x, 0, true, true);
});
let x = (player.x < 400) ? Phaser.Math.Between(400, 800) : Phaser.Math.Between(0, 400);
let bomb = bombs.create(x, 16, 'bomb');
bomb.setBounce(1);
bomb.setCollideWorldBounds(true);
bomb.setVelocity(Phaser.Math.Between(-200, 200), 20);
}
}
// 游戏主循环
function update() {
// 玩家移动控制
if (cursors.left.isDown) {
player.setVelocityX(-160);
player.anims.play('left', true);
} else if (cursors.right.isDown) {
player.setVelocityX(160);
player.anims.play('right', true);
} else {
player.setVelocityX(0);
player.anims.play('turn');
}
if (cursors.up.isDown && player.body.touching.down) {
player.setVelocityY(-330);
}
// 游戏结束检测
this.physics.add.overlap(player, bombs, () => {
this.physics.pause();
player.setTint(0xff0000);
player.anims.play('turn');
this.add.text(300, 300, '游戏结束!', { fontSize: '64px', fill: '#ff0000' });
this.add.text(280, 380, '点击重新开始', { fontSize: '32px', fill: '#ffffff' });
this.input.once('pointerdown', () => {
this.scene.restart();
});
});
}
new Phaser.Game(config);
塔防游戏 AI(核心:路径规划与状态切换)
展示敌人如何按照路径点移动,并具有不同的攻击状态。
class Enemy extends Phaser.Physics.Arcade.Sprite {
constructor(scene, path) {
super(scene, path[0].x, path[0].y, 'enemy');
this.path = path;
this.pathIndex = 0;
this.speed = 100;
this.hp = 100;
this.state = 'moving';
scene.add.existing(this);
scene.physics.add.existing(this);
}
update() {
if (this.state === 'dead') return;
// 状态机逻辑
switch (this.state) {
case 'moving':
this.moveAlongPath();
this.checkRangeForAttack();
break;
case 'attacking':
this.attackTarget();
break;
case 'retreating':
this.moveBackToPath();
break;
}
}
// 路径移动
moveAlongPath() {
if (this.pathIndex >= this.path.length) {
this.state = 'attacking'; // 到达终点
return;
}
const targetPoint = this.path[this.pathIndex];
const distanceToPoint = Phaser.Math.Distance.Between(this.x, this.y, targetPoint.x, targetPoint.y);
if (distanceToPoint < 5) {
this.pathIndex++;
} else {
this.scene.physics.moveTo(this, targetPoint.x, targetPoint.y, this.speed);
}
}
// 检测攻击范围 (假设有炮台)
checkRangeForAttack() {
const tower = this.scene.tower;
if (tower && Phaser.Math.Distance.Between(this.x, this.y, tower.x, tower.y) < 150) {
this.state = 'retreating';
this.setTexture('enemy_angry'); // 切换表情
}
}
// 撤退逻辑
moveBackToPath() {
if (this.pathIndex === 0) {
this.state = 'moving';
this.setTexture('enemy');
return;
}
const backPoint = this.path[this.pathIndex - 1];
this.scene.physics.moveTo(this, backPoint.x, backPoint.y, this.speed * 1.2);
// 回到上一个点就继续前进
if (Phaser.Math.Distance.Between(this.x, this.y, backPoint.x, backPoint.y) < 10) {
this.state = 'moving';
}
}
}
// 在场景中使用
class GameScene extends Phaser.Scene {
constructor() {
super('GameScene');
this.path = [
{ x: 50, y: 100 },
{ x: 200, y: 100 },
{ x: 200, y: 300 },
{ x: 600, y: 300 }
];
}
create() {
// 绘制路径 (用于调试)
this.path.forEach((point, index) => {
if (index > 0) {
this.add.line(0, 0, this.path[index-1].x, this.path[index-1].y, point.x, point.y, 0xffffff).setOrigin(0);
}
});
// 创建测试塔
this.tower = this.add.sprite(400, 300, 'tower');
// 生成敌人
this.time.addEvent({
delay: 2000,
repeat: 3,
callback: () => {
new Enemy(this, this.path);
}
});
}
update() {
this.children.list.forEach(child => {
if (child instanceof Enemy) child.update();
});
}
}
RPG 对话系统(核心:数据驱动 UI)
当你需要一个可复用的对话系统并更新 UI 面板时。
class DialogueScene extends Phaser.Scene {
constructor() {
super('DialogueScene');
this.dialogues = [
{ speaker: '老人', text: '年轻人,你来了。', face: 'oldman' },
{ speaker: '玩家', text: '请问你知道宝藏在哪吗?', face: 'player' },
{ speaker: '老人', text: '穿过黑暗森林,你会找到答案。', face: 'oldman' },
{ speaker: '玩家', text: '谢谢!', face: 'player' }
];
this.currentIndex = 0;
}
create() {
// 半透明背景
this.add.rectangle(400, 300, 800, 600, 0x000000, 0.5);
// 对话框UI
this.dialogueBox = this.add.container(0, 0);
const boxBg = this.add.graphics();
boxBg.fillStyle(0xffffff, 0.9);
boxBg.fillRoundedRect(50, 400, 700, 150, 20);
this.speakerText = this.add.text(80, 420, '', { fontSize: '28px', fill: '#3d3d3d', fontStyle: 'bold' });
this.text1 = this.add.text(80, 460, '', { fontSize: '24px', fill: '#000000', wordWrap: { width: 640 } });
this.dialogueBox.add([boxBg, this.speakerText, this.text1]);
// 下一步按钮
this.nextButton = this.add.text(650, 520, '▼', { fontSize: '32px', fill: '#007acc' })
.setInteractive()
.on('pointerdown', () => this.nextDialogue());
// 首次对话
this.showDialogue(0);
}
showDialogue(index) {
if (index >= this.dialogues.length) {
this.closeDialogue();
return;
}
const data = this.dialogues[index];
this.speakerText.setText(data.speaker + ':');
this.text1.setText(data.text);
}
nextDialogue() {
this.currentIndex++;
this.showDialogue(this.currentIndex);
}
closeDialogue() {
console.log('对话结束');
this.scene.stop();
this.scene.resume('GameScene');
}
}
// 在游戏场景中触发
// this.scene.launch('DialogueScene'); // 启动对话
性能优化与调试建议
对于以上案例,如果遇到性能问题,可以这样优化:
对象池重用(用于案例一)
// 创建对象池
this.bombs = this.physics.add.group({
maxSize: 10,
// 当对象不存在时自动创建
defaultKey: 'bomb',
defaultFrame: undefined
});
// 从池中获取
let bomb = this.bombs.get(player.x, player.y, 'bomb');
if (bomb) {
bomb.setActive(true).setVisible(true);
bomb.setVelocity(100, -100);
}
调试工具(用于案例三)
// 在 create 中启用 this.physics.world.createDebugGraphic(); this.physics.world.debugGraphic.setVisible(true); // 路径可视化 this.add.graphics().lineStyle(2, 0xff0000, 1).strokePoints(this.path.map(p => new Phaser.Geom.Point(p.x, p.y)));
帧率显示
this.time.addEvent({
delay: 1000,
loop: true,
callback: () => {
const fps = this.game.loop.actualFps.toFixed(0);
this.fpsText.setText('FPS: ' + fps);
}
});
如何运行这些案例?
-
本地开发:使用
phaser3-project-template或手动引入 CDN:<script src="https://cdn.jsdelivr.net/npm/phaser@3.60.0/dist/phaser.min.js"></script>
-
在线调试:将案例代码复制到 Phaser 官方示例编辑器 中。
-
资源替换:案例中的图片资源(
sky.png、star.png)可以在 Kenney 资源库 免费获取。
需要我针对某个案例进行深度扩展(如增加多人联机、WebGL 着色器特效等),请告诉我。