最近在技术社区看到不少开发者对"用前端框架做游戏开发"这个话题感兴趣。作为一个既做过Vue项目又折腾过小游戏的全栈开发者,我想分享一个实战案例:如何在Vue 3项目中集成Phaser 3.60引擎,开发一个完整的"太空飞船躲避陨石"游戏。这个案例特别适合已经掌握Vue基础,但想尝试游戏开发的工程师。
首先确保你的开发环境已经安装Node.js 16+版本。我们使用Vite来创建Vue 3项目,它能提供更快的开发体验:
bash复制npm create vite@latest vue-phaser-game --template vue
cd vue-phaser-game
npm install phaser@3.60 --save
项目结构设计很关键,我推荐以下组织方式:
code复制/src
/assets
/images # 存放游戏素材
/components
GameCanvas.vue # 游戏主组件
/scenes
BootScene.js # 游戏启动场景
GameScene.js # 主游戏场景
App.vue
main.js
这种结构将Phaser场景与Vue组件分离,保持代码整洁。在main.js中,我们只需要常规的Vue初始化:
javascript复制import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
在GameCanvas.vue中,我们创建游戏容器和初始化逻辑:
html复制<template>
<div ref="gameContainer" class="game-container"></div>
<div class="ui-panel">
<h2>生命值: {{ lives }}</h2>
<h2>得分: {{ score }}</h2>
</div>
</template>
<script>
import Phaser from 'phaser'
import BootScene from '../scenes/BootScene'
import GameScene from '../scenes/GameScene'
export default {
data() {
return {
lives: 3,
score: 0,
game: null
}
},
mounted() {
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
parent: this.$refs.gameContainer,
physics: {
default: 'arcade',
arcade: {
gravity: { y: 0 },
debug: false
}
},
scene: [BootScene, GameScene]
}
this.game = new Phaser.Game(config)
// 监听游戏事件
this.game.events.on('updateScore', (points) => {
this.score += points
})
this.game.events.on('loseLife', () => {
this.lives--
})
},
beforeUnmount() {
this.game.destroy(true)
}
}
</script>
<style scoped>
.game-container {
margin: 0 auto;
border: 2px solid #333;
}
.ui-panel {
margin-top: 20px;
text-align: center;
font-family: 'Arial', sans-serif;
}
</style>
这里有几个关键点:
ref获取DOM容器引用javascript复制export default class BootScene extends Phaser.Scene {
constructor() {
super('Boot')
}
preload() {
// 加载进度条等资源
this.load.image('logo', 'assets/images/logo.png')
this.load.on('progress', (value) => {
console.log(`加载进度: ${Math.round(value * 100)}%`)
})
}
create() {
this.scene.start('Game')
}
}
javascript复制export default class GameScene extends Phaser.Scene {
constructor() {
super('Game')
this.player = null
this.cursors = null
this.meteors = null
this.score = 0
this.lastMeteorTime = 0
}
preload() {
this.load.image('space', 'assets/images/space.jpg')
this.load.image('ship', 'assets/images/ship.png')
this.load.image('meteor', 'assets/images/meteor.png')
}
create() {
// 背景
this.add.image(400, 300, 'space')
// 玩家飞船
this.player = this.physics.add.sprite(400, 500, 'ship')
this.player.setCollideWorldBounds(true)
// 陨石组
this.meteors = this.physics.add.group()
// 键盘控制
this.cursors = this.input.keyboard.createCursorKeys()
// 碰撞检测
this.physics.add.collider(
this.player,
this.meteors,
this.hitMeteor,
null,
this
)
// 分数文本
this.scoreText = this.add.text(16, 16, '得分: 0', {
fontSize: '32px',
fill: '#fff'
})
}
update(time) {
// 玩家移动控制
if (this.cursors.left.isDown) {
this.player.setVelocityX(-200)
} else if (this.cursors.right.isDown) {
this.player.setVelocityX(200)
} else {
this.player.setVelocityX(0)
}
// 定时生成陨石
if (time > this.lastMeteorTime + 500) {
this.spawnMeteor()
this.lastMeteorTime = time
}
}
spawnMeteor() {
const x = Phaser.Math.Between(50, 750)
const meteor = this.meteors.create(x, -50, 'meteor')
meteor.setVelocity(0, Phaser.Math.Between(100, 300))
meteor.setAngularVelocity(Phaser.Math.Between(-50, 50))
}
hitMeteor() {
this.scene.get('Game').events.emit('loseLife')
// 重置玩家位置
this.player.setPosition(400, 500)
// 屏幕震动效果
this.cameras.main.shake(300, 0.01)
// 无敌时间
this.player.setAlpha(0.5)
this.time.delayedCall(1000, () => {
this.player.setAlpha(1)
})
}
}
游戏素材准备是重要环节。对于这个太空主题游戏,我们需要:
| 资源类型 | 规格要求 | 推荐来源 |
|---|---|---|
| 背景图 | 800x600 JPG | Kenney.nl |
| 飞船精灵 | 64x64 PNG透明背景 | OpenGameArt |
| 陨石精灵 | 32x32 - 64x64 PNG | 自制或免费资源站 |
性能优化技巧:
javascript复制// 在App.vue中预先加载关键资源
const preloadImages = [
import('./assets/images/space.jpg'),
import('./assets/images/ship.png'),
import('./assets/images/meteor.png')
]
Promise.all(preloadImages).then(() => {
// 资源加载完成后显示游戏
})
javascript复制// 在GameScene中初始化陨石对象池
create() {
this.meteorPool = this.add.group({
defaultKey: 'meteor',
maxSize: 20,
createCallback: (meteor) => {
meteor.setActive(false).setVisible(false)
}
})
}
// 重用陨石对象
spawnMeteor() {
const meteor = this.meteorPool.get()
if (meteor) {
meteor.setActive(true)
meteor.setVisible(true)
meteor.setPosition(
Phaser.Math.Between(50, 750),
-50
)
// ...其他设置
}
}
javascript复制// 在GameCanvas.vue中添加
methods: {
togglePause() {
if (this.game.scene.isPaused('Game')) {
this.game.scene.resume('Game')
} else {
this.game.scene.pause('Game')
}
}
}
Vue的响应式系统非常适合管理游戏状态。我们可以进一步扩展:
javascript复制data() {
return {
difficulty: 'normal',
difficultySettings: {
easy: { meteorSpeed: 100, spawnRate: 1000 },
normal: { meteorSpeed: 200, spawnRate: 500 },
hard: { meteorSpeed: 300, spawnRate: 300 }
}
}
},
methods: {
setDifficulty(level) {
this.difficulty = level
this.game.events.emit('changeDifficulty', this.difficultySettings[level])
}
}
javascript复制// 使用localStorage保存游戏进度
saveGame() {
const gameData = {
score: this.score,
lives: this.lives,
difficulty: this.difficulty
}
localStorage.setItem('spaceGameSave', JSON.stringify(gameData))
}
loadGame() {
const savedData = localStorage.getItem('spaceGameSave')
if (savedData) {
const { score, lives, difficulty } = JSON.parse(savedData)
this.score = score
this.lives = lives
this.difficulty = difficulty
this.game.events.emit('loadGameData', { score, difficulty })
}
}
javascript复制// 在BootScene中预加载
preload() {
this.load.audio('backgroundMusic', 'assets/audio/space.mp3')
this.load.audio('explosion', 'assets/audio/explosion.wav')
}
// 在GameScene中使用
create() {
this.music = this.sound.add('backgroundMusic', { loop: true })
this.explosionSound = this.sound.add('explosion')
// 控制音量
this.sound.volume = 0.5
}
开发过程中有几个实用的调试方法:
javascript复制// 在游戏配置中启用调试
physics: {
default: 'arcade',
arcade: {
debug: true // 显示碰撞框
}
}
javascript复制// 在create()中添加
this.debugText = this.add.text(16, 50, '', {
fontSize: '18px',
fill: '#fff'
})
// 在update()中更新
update() {
const fps = this.game.loop.actualFps.toFixed(2)
this.debugText.setText(`FPS: ${fps}\n陨石数量: ${this.meteors.getLength()}`)
}
bash复制# 使用Vite的生产构建
npm run build
# 预览生产版本
npm run preview
发布时注意:
这个项目完整源码已放在GitHub上,包含所有素材资源和详细注释。通过这个案例,你会发现Vue和Phaser的结合既保留了前端开发的便利性,又能实现丰富的游戏功能。下次可以尝试添加更多游戏机制,比如道具系统、Boss战或者多人联机功能。