如何为Phaser.js游戏添加优先加载的菜单界面?
搞定Phaser 3的启动前菜单问题
嘿,你这踩了个Phaser版本的大坑!你用的是Phaser 3.1.1,但代码里写的game.state.add和game.state.start是Phaser 2的旧API,这俩版本的场景/状态系统完全不兼容,这就是你拆分状态没用的核心原因。下面给你Phaser 3的正确实现方式:
核心思路
Phaser 3用**Scene(场景)**来管理不同的游戏界面,我们只需要做三件事:
- 写一个菜单场景:显示启动按钮,点击后切换到游戏主场景
- 把你的原游戏代码改成游戏主场景格式
- 在配置里注册这两个场景,默认启动菜单场景
修改后的完整可运行代码
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>test</title> <script src="//cdn.jsdelivr.net/npm/phaser@3.1.1/dist/phaser.js"></script> <style type="text/css"> body { margin-top: 45px; margin-left: 250px; background-color: black; } .page-title { color: white; } </style> </head> <body> <script type="text/javascript"> // 菜单场景:负责显示启动界面 class MenuScene extends Phaser.Scene { constructor() { super('MenuScene'); // 给场景起个名字,方便后续切换 } preload() { // 这里可以加载菜单需要的资源,比如按钮图片、菜单背景 // 示例:this.load.image('startBtn', 'assets/start-button.png'); } create() { // 添加标题文本 this.add.text(400, 200, '我的游戏', { fontSize: '48px', fill: '#ffffff' }).setOrigin(0.5); // 创建启动按钮(你后续可以换成图片按钮) const startButton = this.add.text(400, 350, '点击开始游戏', { fontSize: '32px', fill: '#00ff00' }).setOrigin(0.5); // 给按钮添加交互能力 startButton.setInteractive(); // 点击按钮切换到游戏场景 startButton.on('pointerdown', () => { this.scene.start('GameScene'); }); // 鼠标悬停的视觉反馈(可选) startButton.on('pointerover', () => { startButton.setFill('#ffff00'); }); startButton.on('pointerout', () => { startButton.setFill('#00ff00'); }); } } // 游戏主场景:就是你原来的游戏代码,改成Phaser 3的Scene格式 class GameScene extends Phaser.Scene { constructor() { super('GameScene'); // 声明场景内需要用到的变量 this.player; this.platforms; this.cursors; this.gameOver = false; } preload () { this.load.image('sky', 'phaser_assets/sky.png'); this.load.image('ground', 'phaser_assets/platform.png'); this.load.image('star', 'phaser_assets/star.png'); this.load.image('bomb', 'phaser_assets/head1.png', { frameWidth: 48, frameHeight: 100 }); this.load.spritesheet('trump', 'phaser_assets/trump.png', { frameWidth: 48, frameHeight: 100 }); } 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, 'trump'); this.player.setBounce(0.2); this.player.setCollideWorldBounds(true); // 初始化键盘光标控制 this.cursors = this.input.keyboard.createCursorKeys(); // 定义玩家动画(你原来的代码里漏掉了这部分) this.anims.create({ key: 'left', frames: this.anims.generateFrameNumbers('trump', { start: 0, end: 3 }), frameRate: 10, repeat: -1 }); this.anims.create({ key: 'turn', frames: [ { key: 'trump', frame: 4 } ], frameRate: 20 }); this.anims.create({ key: 'right', frames: this.anims.generateFrameNumbers('trump', { start: 5, end: 8 }), frameRate: 10, repeat: -1 }); // 玩家和平台的碰撞检测(原来的代码也漏掉了) this.physics.add.collider(this.player, this.platforms); } update () { if (this.gameOver) { return; } // 玩家移动逻辑 if (this.cursors.left.isDown) { this.player.setVelocityX(-200); this.player.anims.play('left', true); } else if (this.cursors.right.isDown) { this.player.setVelocityX(200); 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); } } } // 游戏配置:注册两个场景,第一个就是默认启动的菜单场景 const config = { type: Phaser.AUTO, width: 800, height: 600, physics: { default: 'arcade', arcade: { gravity: { y: 300 }, debug: false } }, scene: [MenuScene, GameScene] }; // 初始化游戏 new Phaser.Game(config); </script> </body> </html>
关键改动说明
- 告别Phaser 2的State,拥抱Phaser 3的Scene:用ES6类定义每个场景,每个场景有自己独立的
preload/create/update方法,这是Phaser 3的标准写法 - 正确的场景切换:在菜单按钮的点击事件里用
this.scene.start('GameScene')切换场景,这是Phaser 3的官方切换方式 - 修复原游戏的遗漏问题:补上了光标初始化、动画定义、碰撞检测,否则你的原游戏也无法正常运行
- 配置里注册场景:把两个场景放进
scene数组,第一个元素就是游戏启动时默认加载的场景
后续扩展提示
- 想把按钮换成图片?只需要在
MenuScene的preload里加载按钮图片,然后用this.add.image创建按钮,再设置setInteractive()和点击事件就行 - 如果需要预加载所有游戏资源,可以再加一个
PreloadScene,先加载所有资源,再切换到菜单场景
内容的提问来源于stack exchange,提问作者thesubhuman
相关产品推荐
相关产品推荐

