You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为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>

关键改动说明

  1. 告别Phaser 2的State,拥抱Phaser 3的Scene:用ES6类定义每个场景,每个场景有自己独立的preload/create/update方法,这是Phaser 3的标准写法
  2. 正确的场景切换:在菜单按钮的点击事件里用this.scene.start('GameScene')切换场景,这是Phaser 3的官方切换方式
  3. 修复原游戏的遗漏问题:补上了光标初始化、动画定义、碰撞检测,否则你的原游戏也无法正常运行
  4. 配置里注册场景:把两个场景放进scene数组,第一个元素就是游戏启动时默认加载的场景

后续扩展提示

  • 想把按钮换成图片?只需要在MenuScene的preload里加载按钮图片,然后用this.add.image创建按钮,再设置setInteractive()和点击事件就行
  • 如果需要预加载所有游戏资源,可以再加一个PreloadScene,先加载所有资源,再切换到菜单场景

内容的提问来源于stack exchange,提问作者thesubhuman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:23:54