Phaser 游戏实战
概述
Phaser 是一款功能强大的开源 HTML5 游戏框架,专注于 2D 游戏开发。它基于 Canvas 和 WebGL 双渲染模式,内置了完整的游戏开发工具链,包括物理引擎、场景管理、音频系统、输入处理等。本文将通过一个平台跳跃游戏的实战开发,系统介绍 Phaser 3 的核心概念和关键 API 用法。
Phaser 核心概念
游戏实例(Game)
一切始于 Phaser.Game 实例的创建。游戏实例是整个应用的入口,负责初始化渲染器、加载资产、管理场景和驱动游戏循环:
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
physics: {
default: 'arcade',
arcade: { gravity: { y: 300 }, debug: false }
},
scene: [BootScene, GameScene, GameOverScene]
}
const game = new Phaser.Game(config)type: Phaser.AUTO 会让引擎优先选择 WebGL,在不支持时自动降级为 Canvas。physics 配置开启了 Arcade 物理引擎并设置了全局重力。
场景(Scene)
场景是 Phaser 游戏的基本组织单元,类似于电影中的"场景"或"关卡"。每个场景都有完整的生命周期方法:
- preload():预加载所有游戏资源(图片、音频、精灵图)
- create():初始化游戏对象、设置物理碰撞、绑定输入
- update():每帧执行,处理游戏逻辑更新(移动、碰撞检测、状态刷新)
class GameScene extends Phaser.Scene {
constructor() {
super({ key: 'GameScene' })
}
preload() {
this.load.image('ground', 'assets/ground.png')
this.load.spritesheet('player', 'assets/player.png', { frameWidth: 32, frameHeight: 48 })
this.load.image('coin', 'assets/coin.png')
}
create() {
// 初始化游戏世界
}
update(time, delta) {
// 每帧更新逻辑
}
}游戏对象(GameObject)
Phaser 提供了丰富的游戏对象类型,每个对象都继承自 Phaser.GameObjects.GameObject:
- Sprite:精灵,最常用的可视对象,支持动画、物理、交互
- Image:静态图片,比 Sprite 更轻量
- Graphics:图形绘制对象,用于绘制几何形状
- Text:文本对象,支持样式和字体设置
- TileSprite:平铺精灵,适合滚动背景
- Group:对象组,批量管理和操作多个游戏对象
物理引擎
Phaser 3 内置了两套物理引擎:
- Arcade Physics:轻量级的 AABB(轴对齐包围盒)物理引擎,适合大多数 2D 游戏。支持重力、速度、加速度、碰撞检测和弹跳效果。API 简单直观,性能优秀。
- Matter.js:功能完整的物理引擎,支持多边形碰撞、约束、关节和复合体。适合需要复杂物理模拟的游戏。
对于平台跳跃游戏,Arcade Physics 是最佳选择。其核心碰撞检测通过 collider 和 overlap 方法实现:
// 碰撞:玩家与地面碰撞时停止下落
this.physics.add.collider(player, platforms)
// 重叠:玩家触碰金币时触发收集
this.physics.add.overlap(player, coins, collectCoin, null, this)精灵动画系统
精灵表(Sprite Sheet)
精灵表是将多个动画帧合并到一张图片中的技术,可以显著减少 HTTP 请求和纹理切换开销。Phaser 使用 spritesheet 加载精灵表,并通过 animations 定义动画序列:
// 加载精灵表(32x48 像素的单帧尺寸)
this.load.spritesheet('player', 'assets/player.png', { frameWidth: 32, frameHeight: 48 })
// 创建行走动画
this.anims.create({
key: 'walk',
frames: this.anims.generateFrameNumbers('player', { start: 0, end: 3 }),
frameRate: 10,
repeat: -1 // 循环播放
})
// 创建跳跃动画
this.anims.create({
key: 'jump',
frames: [{ key: 'player', frame: 4 }],
frameRate: 1,
repeat: 0 // 只播放一次
})
// 播放动画
player.anims.play('walk', true)帧动画控制
Phaser 提供了精细的动画控制 API:
anims.play(key):播放指定动画anims.stop():停止当前动画anims.pause()/anims.resume():暂停/恢复anims.currentAnim:获取当前播放的动画引用anims.isPlaying:检查是否正在播放- 事件监听:
'animationcomplete'、'animationstart'、'animationrepeat'
在实际游戏开发中,通常根据角色的移动状态切换动画。例如,当检测到玩家按下方向键时播放行走动画,检测到跳跃动作时播放跳跃动画。
骨骼动画与 Spine
对于更复杂的角色动画,Phaser 支持通过插件集成 Spine 骨骼动画。Spine 动画通过骨骼和槽位控制角色动作,可以实现平滑的动画过渡和部件换装。虽然 Plask(Spine 的免费替代品)也提供了类似能力,但 Spine 仍然是业界标准方案。
Arcade 物理引擎深入
重力与弹跳
Arcade 物理引擎的重力是全局配置,但可以为每个物体单独设置:
// 为玩家设置物理属性
player.setBounce(0.2) // 弹跳系数(0-1)
player.setCollideWorldBounds(true) // 限制在世界边界内
// 为不同物体设置不同的重力
player.body.setGravityY(500) // 单独增加玩家重力
coin.body.setAllowGravity(false) // 金币不受重力影响(浮动)
// 设置速度
player.setVelocityX(160) // 水平移动速度
player.setVelocityY(-400) // 跳跃速度(负值 = 向上)弹跳系数 setBounce(0.2) 表示物体碰撞后保留 20% 的速度。值为 0 时完全不弹跳,值为 1 时弹跳到原高度。对于平台跳跃游戏,通常给玩家设置较小的弹跳值(0.1-0.2),避免跳跃手感过于"滑腻"。
碰撞检测模式
Arcade 物理提供三种碰撞检测模式:
collider(object1, object2, callback):两个物体碰撞后停止,不可穿透。这是最常见的碰撞模式,用于角色和地面、墙壁之间的交互。
overlap(object1, object2, callback):检测两个物体是否重叠,不产生物理反弹。适合收集物品、触发区域、检测敌人 proximity 等场景。
worldBounds:物体与世界边界的碰撞。通过
setCollideWorldBounds(true)启用,防止物体移出游戏画面。
// 玩家与地面碰撞
this.physics.add.collider(player, platforms)
// 玩家与金币重叠(收集)
this.physics.add.overlap(player, coins, collectCoin, null, this)
// 玩家与敌人碰撞(扣血)
this.physics.add.overlap(player, enemies, hitEnemy, null, this)
// 敌人与地面碰撞(防止掉出平台)
this.physics.add.collider(enemies, platforms)物理分组优化
当需要检测大量物体之间的碰撞时,使用 Group 或 StaticGroup 可以显著提升性能:
// 静态物理组(不移动的地面、墙壁)
platforms = this.physics.add.staticGroup()
// 动态物理组(敌人、可移动物体)
enemies = this.physics.add.group()
// 静态组与动态组的碰撞性能远优于逐一添加碰撞器
this.physics.add.collider(player, platforms)
this.physics.add.collider(enemies, platforms)StaticGroup 中的物体不参与物理计算,因此性能开销极小。对于平台游戏中的地面、墙壁等静态障碍物,始终优先使用 StaticGroup。
粒子特效系统
Phaser 3.60+ 引入了全新的粒子系统 API,基于 GPU 加速实现高性能粒子效果:
// 创建粒子纹理(通过 Graphics 动态生成)
const particles = this.add.particles(0, 0, 'particle', {
speed: { min: -100, max: 100 },
angle: { min: 0, max: 360 },
scale: { start: 0.8, end: 0 },
lifespan: 800,
gravityY: 200,
quantity: 2,
emitting: false
})
// 在特定位置爆发粒子
particles.emitParticleAt(x, y, 20)粒子系统适合实现以下游戏特效:
- 收集反馈:拾取金币时爆发金色粒子
- 受伤效果:玩家受伤时红色粒子闪烁
- 爆炸效果:敌人被消灭时碎片飞散
- 环境特效:飘落的雪花、飞行的萤火虫
- 轨迹效果:角色移动时的拖尾特效
游戏生命周期管理
场景切换
Phaser 的场景切换是通过 Scene Manager 完成的:
// 切换到另一个场景
this.scene.start('GameOverScene', { score: 100, level: 3 })
// 暂停当前场景,启动新场景(覆盖)
this.scene.launch('PauseScene')
this.scene.pause('GameScene')
// 停止场景
this.scene.stop('GameScene')场景数据传递通过 start() 的第二个参数实现。在目标场景的 init(data) 方法中可以接收这些数据:
class GameOverScene extends Phaser.Scene {
init(data) {
this.finalScore = data.score || 0
this.level = data.level || 1
}
create() {
this.add.text(400, 250, `得分: ${this.finalScore}`, { ... })
this.add.text(400, 320, `关卡: ${this.level}`, { ... })
}
}游戏状态管理
建议使用场景生命周期 + 数据单例来管理游戏状态。避免直接在场景之间共享全局变量,而是通过事件系统或场景数据传递:
// 使用注册表(Registry)存储全局游戏数据
this.registry.set('score', 0)
this.registry.set('lives', 3)
this.registry.set('level', 1)
// 在任意场景中访问
const score = this.registry.get('score')
this.registry.events.on('changedata-score', (parent, value) => {
scoreText.setText(`得分: ${value}`)
})资源卸载
场景切换时,Phaser 默认不会卸载资源。为避免内存泄漏,应当在场景销毁时手动清理不再需要的资源:
class GameScene extends Phaser.Scene {
// 场景关闭时清理
shutdown() {
this.physics.world.shutdown() // 停止物理世界
this.input.keyboard.removeAllListeners() // 清理输入监听
this.tweens.killAll() // 停止所有补间动画
}
}实战案例:平台跳跃游戏
以下 Demo 基于 Phaser 3 实现了一个完整的平台跳跃小游戏,包含角色控制、平台跳跃、金币收集和敌人碰撞等核心玩法。游戏中玩家使用键盘方向键控制角色移动和跳跃,目标是收集所有金币并避开敌人。
游戏机制:
- 操作方式:← → 方向键控制左右移动,↑ 或 Space 键跳跃
- 得分系统:每收集一个金币增加 10 分
- 生命系统:碰到敌人扣除 1 点生命值,生命归零时游戏结束
- 物理反馈:角色具有重力和弹跳效果,平台碰撞检测精确
- 视觉反馈:金币旋转动画、跳跃动画切换、游戏结束画面
交互演示
以下 Demo 展示了基于 Phaser 3 开发的平台跳跃小游戏,可直接在浏览器中运行体验: