Buttons

This is the 15th step out of 16 of the Gamedev Phaser tutorial. Instead of starting the game right away, we can leave that decision to the player by adding a Start button they can press. Let's investigate how to do that.

New properties

We will need a property to store a boolean value representing whether the game is currently being played or not, and another one to represent our button. Add these lines below your other properties definitions:

js
class ExampleScene extends Phaser.Scene {
  // ... previous property definitions ...
  playing = false;
  startButton;
  // ... rest of the class ...
}

Loading the button spritesheet

We can load the button spritesheet the same way we loaded the ball's wobble animation. Add the following to the bottom of the preload() method:

js
this.load.spritesheet("button", "img/button.png", {
  frameWidth: 120,
  frameHeight: 40,
});

A single button frame is 120 pixels wide and 40 pixels high.

You also need to grab the button spritesheet, and save it in your /img directory.

Adding the button to the game

Adding the new button to the game is done by using the add.sprite method. Add the following lines to the bottom of your create() method:

js
this.startButton = this.add.sprite(
  this.scale.width * 0.5,
  this.scale.height * 0.5,
  "button",
  0,
);

In addition to the parameters we passed to the other add.sprite calls (such as when we added the ball and paddle), this time we also pass the frame number, which is 0 in this case. This means that the first frame of the spritesheet will be used for the button's initial appearance.

To make the button respond to various inputs such as mouse clicks, we need to add the following lines right after the previous add.sprite call:

js
this.startButton.setInteractive();
this.startButton.on(
  "pointerover",
  () => {
    this.startButton.setFrame(1);
  },
  this,
);
this.startButton.on(
  "pointerdown",
  () => {
    this.startButton.setFrame(2);
  },
  this,
);
this.startButton.on(
  "pointerout",
  () => {
    this.startButton.setFrame(0);
  },
  this,
);
this.startButton.on(
  "pointerup",
  () => {
    this.startGame();
  },
  this,
);

First, we call setInteractive on the button to make it respond to pointer events. Then we add the four event listeners to the button:

  • pointerover—when the pointer is over the button, we change the button's frame to 1, the second frame of the spritesheet.
  • pointerdown—when the button is pressed, we change the button's frame to 2, the third frame of the spritesheet.
  • pointerout—when the pointer moves out of the button, we change the button's frame back to 0, the first frame of the spritesheet.
  • pointerup—when the button is released, we call the startGame method to start the game.

Now, we need to define the startGame() method referenced in the code above:

js
class ExampleScene extends Phaser.Scene {
  // ...
  startGame() {
    this.startButton.destroy();
    this.ball.body.setVelocity(150, -150);
    this.playing = true;
  }
}

When the button is pressed, we remove the button, set the ball's initial velocity, and set the playing property to true.

Finally for this section, go back into your create method, find the this.ball.body.setVelocity(150, -150); line, and remove it. You only want the ball to move when the button is pressed, not before!

Keeping the paddle still before the game starts

It works as expected, but we can still move the paddle when the game hasn't started yet, which looks a bit silly. To stop this, we can take advantage of the playing property and make the paddle movable only when the game has started. To do that, adjust the update() method like so:

js
class ExampleScene extends Phaser.Scene {
  // ...
  update() {
    // ...
    if (this.playing) {
      this.paddle.x = this.input.x || this.scale.width * 0.5;
    }
    // ...
  }
  // ...
}

That way the paddle is immovable after everything is loaded and prepared, but before the start of the actual game.

Compare your code

Here's what you should have so far, running live. To view its source code, click the "Play" button.

Next steps

The last thing we will do in this article series is make the gameplay even more interesting by adding some randomization to the way the ball bounces off the paddle.