Developing a Cross-Platform Mobile Game with React Native and Phaser #GameDev #ReactNative #Phaser


Day 7: Adding Multiple Scenes, Start Menu, and Pause Functionality

Welcome to Day 7 of our series on building a cross-platform mobile game using React Native and Phaser. Your game has mechanics, visuals, and audio — now let’s organize it with multiple scenes, a start screen, and pause/resume controls.


🧠 What You’ll Learn

  • How to use multiple Phaser scenes (Boot, Menu, Game, GameOver)
  • How to create a start menu with buttons
  • How to pause and resume the game on demand
  • Scene switching logic in Phaser

📁 Step 1: Restructure Game Code into Scenes

Replace your entire game.html with this updated structure:

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <title>Phaser Multi-Scene Game</title>
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/phaser.js"></script>
  <style>
    body { margin: 0; overflow: hidden; }
    canvas { display: block; }
  </style>
</head>
<body>
<script>
  let bgMusic;

  class MenuScene extends Phaser.Scene {
    constructor() {
      super('MenuScene');
    }

    preload() {
      this.load.image('startBtn', 'https://labs.phaser.io/assets/sprites/start-button.png');
      this.load.audio('music', 'https://labs.phaser.io/assets/audio/loops/SpaceHarrier.mp3');
    }

    create() {
      this.add.text(80, 100, '🚀 React Native Phaser Game', { fontSize: '18px', fill: '#fff' });
      const start = this.add.image(160, 240, 'startBtn').setInteractive();
      bgMusic = this.sound.add('music', { loop: true, volume: 0.5 });
      bgMusic.play();

      start.on('pointerdown', () => {
        this.scene.start('GameScene');
      });
    }
  }

  class GameScene extends Phaser.Scene {
    constructor() {
      super('GameScene');
    }

    preload() {
      this.load.image('enemy', 'https://labs.phaser.io/assets/sprites/ufo.png');
      this.load.image('player', 'https://labs.phaser.io/assets/sprites/mushroom2.png');
      this.load.image('pauseBtn', 'https://labs.phaser.io/assets/sprites/pause-button.png');
    }

    create() {
      this.score = 0;
      this.health = 3;

      this.player = this.physics.add.image(160, 400, 'player');
      this.player.setCollideWorldBounds(true);

      this.enemies = this.physics.add.group();
      this.physics.add.overlap(this.player, this.enemies, this.hitEnemy, null, this);

      this.input.on('pointermove', (pointer) => {
        this.player.x = pointer.x;
      });

      this.scoreText = this.add.text(10, 10, 'Score: 0', { fontSize: '14px', fill: '#fff' });
      this.healthText = this.add.text(220, 10, 'Health: 3', { fontSize: '14px', fill: '#fff' });

      this.time.addEvent({
        delay: 1000,
        callback: () => {
          const x = Phaser.Math.Between(20, 300);
          const enemy = this.enemies.create(x, 0, 'enemy');
          enemy.setVelocityY(100);
        },
        loop: true
      });

      // Pause Button
      const pauseBtn = this.add.image(300, 460, 'pauseBtn').setInteractive().setScale(0.5);
      pauseBtn.on('pointerdown', () => {
        this.scene.pause();
        this.scene.launch('PauseScene');
      });
    }

    hitEnemy(player, enemy) {
      enemy.destroy();
      this.health -= 1;
      this.healthText.setText('Health: ' + this.health);

      if (this.health <= 0) {
        bgMusic.stop();
        this.scene.start('GameOverScene', { score: this.score });
      }

      this.score += 1;
      this.scoreText.setText('Score: ' + this.score);
    }
  }

  class PauseScene extends Phaser.Scene {
    constructor() {
      super('PauseScene');
    }

    create() {
      this.add.text(100, 200, 'Game Paused', { fontSize: '20px', fill: '#fff' });
      const resumeBtn = this.add.text(100, 250, '[ Tap to Resume ]', { fill: '#0f0' })
        .setInteractive();

      resumeBtn.on('pointerdown', () => {
        this.scene.stop();
        this.scene.resume('GameScene');
      });
    }
  }

  class GameOverScene extends Phaser.Scene {
    constructor() {
      super('GameOverScene');
    }

    init(data) {
      this.finalScore = data.score;
    }

    create() {
      this.add.text(80, 200, '💀 Game Over', { fontSize: '24px', fill: '#f00' });
      this.add.text(100, 250, `Score: ${this.finalScore}`, { fontSize: '18px', fill: '#fff' });

      this.input.once('pointerdown', () => {
        this.scene.start('MenuScene');
      });
    }
  }

  const config = {
    type: Phaser.AUTO,
    width: 320,
    height: 480,
    scene: [MenuScene, GameScene, PauseScene, GameOverScene],
    physics: {
      default: 'arcade',
      arcade: { debug: false }
    }
  };

  new Phaser.Game(config);
</script>
</body>
</html>

🧪 Step 2: Test Your Scenes

  1. Open your app with expo start.
  2. You’ll see a start screen with a button.
  3. Tap to start the game.
  4. Press the pause button to enter the pause menu.
  5. On game over, a screen appears with your score and tap-to-restart.
See also  Developing a Cross-Platform Mobile Game with React Native and Phaser #GameDev #ReactNative #Phaser

✅ What You’ve Achieved

  • Created multiple scenes for menu, gameplay, pause, and game over
  • Built scene transitions with buttons and game state management
  • Enabled pause/resume functionality in a real mobile game setup
  • Structured your code into scalable, modular game scenes

📌 Up Next

In Day 8, we’ll introduce level design, obstacle patterns, and environment changes — making each stage feel unique and challenging.

Your game now has structure and flow — time to level it up!

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.