Project code

Project code

Experience the

Space begins, left and right arrows control movement

Experience the

Phaser profile

Phaser is an HTML5 gaming framework. It uses many HTML5 apis, such as Canvas, WebGL, Audio, Gamepad, etc., and adds some useful logic, such as managing the game loop and providing us with a physics engine.

With Phaser, we can build 2D games using only HTML, CSS, and JavaScript.

The project requirements

Before building a Breakout clone using Phaser, let’s first define the scope of the game:

The single-player game has 30 blocks, a bat and a ball and the goal of one level is to get the ball to destroy all of the blocks while keeping it at the bottom of the game screen. The player controls a paddle that moves left and right. The game is designed for desktop web users, so you will use a keyboard for typing

Set the Phaser

Phaser is a JavaScript library, and to develop and play our game we need some basic HTML to load JS. Create a directory named Breakout in a workspace.

Create the following files and folders in the directory:

An index. HTML file a breakout.js file named Assets In your Assets folder, create an Images folder. The game assets are the art, sound, video, and other data used in the game. For this simple Breakout clone, not many assets need to be organized using folders. However, it is good practice to separate the assets from the code and separate the assets by type.

Add the following code to your index.html file:

<! doctype html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"> <title>Breakout</title> <style> html, body { margin: 0 auto; padding: 0; width: 100%; height: 100%; } #game { margin: 10px auto; padding: 0; width: 800px; height: 640px; } </style> </head> <body> <noscript>You need to enable JavaScript to run this app.</noscript> <div id="game"></div> < script SRC = "/ / cdn.jsdelivr.net/npm/[email protected]/dist/phaser.min.js" > < / script > < script SRC = "breakout. Js" > < / script > </body> </html>Copy the code

This basic HTML code does the following:

  • Remove browser margins and padding from HTML and body tags
  • Add agameDiv element, which will contain our Breakout clone
  • Load Phaser V3.17 through its CDN
  • loadingbreakout.jsA file that currently does nothing but contains game logic

To develop the game effectively with Phaser, we need to run these files on a Web server. If the Web server, for security reasons, our browser will not allow our game script to load assets.

Fortunately, you don’t need to set up Apache or Nginx to get a running Web server. If you use VisualStudio Code, you can install the Live Server extension. Most ides and text editors have plug-ins that do similar things.

If you have Python version 3 installed, you can enter the workspace from a terminal and type python3 -m http.server. There are other CLI tools that provide a simple Web server, so choose one that will give you the fastest time to develop your game.

Finally, download the art assets we created for the game. Copy and paste the PNG file into the images folder.

Create our game world

By setting up the HTML and CSS, let’s edit the breakout.js file to set up the game world.

Start Phaser

First, we need to configure Phaser and create our Game instance. The example of this game is the Phaser game’s central controller, which does all the setup and starts the game loop.

Let’s configure and create a Game instance:

// This object contains all the Phaser configurations to load our game
const config = {
  type: Phaser.AUTO,
  parent: 'game',
  width: 800,
  heigth: 640,
  scale: {
    mode: Phaser.Scale.RESIZE,
    autoCenter: Phaser.Scale.CENTER_BOTH
  },
  scene: {
    preload,
    create,
    update,
  },
  physics: {
    default: 'arcade',
    arcade: {
      gravity: false
    },
  }
};

// Create the game instance
const game = new Phaser.Game(config);
Copy the code

The Type attribute tells Phaser what renderer to use. Phaser can render our game using HTML5’s WebGL or Canvas elements. By setting the type to phaser. AUTO, we tell Phaser to try rendering using WebGL first, and Canvas if that fails.

This parent property represents the ID of the HTML element that will play our game. We use width and define the game size (in pixels) height. The scale object does two things for us:

  • modeTell Phaser how to use the space of the parent element, in which case we make sure the game fits the size of the parent elementdiv
  • autoCenterTell the PhaserdivHow to center the game in our parent level if needed. In this case, we center the game vertically and horizontally within the parent div. This property is more useful when the game does not occupy the entire space of the parent object.

In Phaser, our game logic defines Scenes in. Think of scenes as states in the game: the title screen is a scene, each level of the game will be its own scene, the cut scene will be its own scene, and so on. Phaser provides Scene objects, but it can also work with functions defined with regular JavaScript objects preload(), create(), and update().

The last configuration tells Phaser which physics engine to use. Phaser can use three different physics engines: Arcade, Impact and Matter. Arcade is the easiest way to get started, enough to meet our game needs.

Breakout doesn’t need gravity to work, so we disabled it in the physics engine. If we were building a Platform game, we might use gravity so that when our players jump, they fall back to the ground naturally.

To make sure our gameplay works, we need to add preload(), create(), and Update () features. Once the game instance is created, add the following white space functions to it:

function preload() { }

function create() { }

function update() { }

Copy the code

With the Web server running, navigate to the page where the game is running. You should see a blank screen like this:

Load balance

The assets in the game consist of five images. In other games you might build, your assets might be huge. High-definition images, audio and video files can take up megabytes of space. The bigger the asset, the longer the burden. Thus, Phaser has a preload() feature that allows us to load all assets before we start running the game.

Change the preload() function to the following so that we can load the image before the game loop starts:

function preload() {
  this.load.image('ball', 'assets/images/ball_32_32.png');
  this.load.image('paddle', 'assets/images/paddle_128_32.png');
  this.load.image('brick1', 'assets/images/brick1_64_32.png');
  this.load.image('brick2', 'assets/images/brick2_64_32.png');
  this.load.image('brick3', 'assets/images/brick3_64_32.png');
}

Copy the code

The first parameter is the key that will be used to reference the image later, and the second parameter is the location of the image.

Note: – When we use this for our preload(), create() and Update () functions, we refer to the game instance created by the previous game.

After loading the image, we want to place sprites on the screen. At the top of breakout.js, add the following variables that will contain our Sprite data:

let player, ball, violetBricks, yellowBricks, redBricks, cursors;

Copy the code

Once they are defined globally, all of our functions can use them.

Add the elves

A Sprite is any 2D image in a game scene. In Phaser, Sprite encapsulates the image along with its location, speed, physical properties, and other properties. First, create the Player Sprite with the create() function:

player = this.physics.add.sprite(
  400, // x position
  600, // y position
  'paddle', // key of image for the sprite
);

Copy the code

You should now be able to see a paddle on the screen:

The first argument to the Sprite () method is the coordinate where X places the Sprite. The second argument is the y-coordinate, and the last argument is the key of the image asset added in the preload() function.

It’s important to understand how Phaser and most 2D game frameworks use coordinates. The graphs that we learn in school usually put the origin, the point (0,0), in the center. In Phaser, the origin is at the top left of the screen. As x increases, we’re actually moving to the right. As y increases, we’re moving down.

Our game is 800 pixels wide and 640 pixels high, so our game coordinates look like this:

Let’s add the ball to the top of the Player. Add the following code to the create() function:

ball = this.physics.add.sprite(
  400, // x position
  565, // y position
  'ball' // key of image for the sprite
);

Copy the code

Since the ball above our Player, the value in coordinate Y is lower than the Player’s y coordinate.

Adding a Sprite Group

While Phaser could easily add sprites, it would quickly become tedious if you had to define each Sprite individually. The bricks in Breakout are almost identical. The positions are different, but their properties (such as color and how they interact with the ball) are the same. Instead of creating 30 brick Sprite objects, we can create groups of sprites to manage them better.

Let’s add the first row of purple bricks via the create() function:

// Add violet bricks violetBricks = this.physics.add.group({ key: 'brick1', repeat: 9, setXY: { x: 80, y: 140, stepX: 70}});Copy the code

Instead of this.physics.add.sprite(), we use this.physics.add.group() and pass a JavaScript object. The key property references the image keys that will be used by all sprites in the Sprite group. This repeat property tells Phaser how many more sprites to create. Each Sprite group creates a Sprite. With repeat set to 9, Phaser will create a Sprite group of 10 sprites. This setXY object has three interesting properties:

  • xThat’s the x-coordinate of the first Sprite
  • yIt’s the y-coordinate of the second Sprite
  • stepXIs the length of pixels between repeated sprites on the X-axis.

There is also a stepY property, but we don’t need to use it in the game. Let’s add another two remaining Sprite groups to the brick:

// Add yellow bricks yellowBricks = this.physics.add.group({ key: 'brick2', repeat: 9, setXY: { x: 80, y: 90, stepX: 70}}); // Add red bricks redBricks = this.physics.add.group({ key: 'brick3', repeat: 9, setXY: { x: 80, y: 40, stepX: 70 } });Copy the code

Our game has been put together and your screen should look like this:

Game win and end

If our ball falls to the bottom of the screen, we might lose a game. To position the ball below the screen in a phaser, the ball’s y-coordinate is greater than the height of the game world. Let’s create a function that checks this functionality and add the following at the bottom of breakout.js:

function isGameOver(world) {
  return ball.body.y > world.bounds.height;
}

Copy the code

Our function gets the world object from the physical properties of the scene, which will be available in the Update () function. It checks if the ball Sprite’s y-coordinate is greater than the height of the game world boundary.

To win the game, we need to knock down all the bricks. Sprites in phasers all have active properties. We can use this attribute to determine if we win. A Sprite group can count the number of active sprites it contains. If there are no active blocks in each block Sprite, that is, there are no 0 active blocks, then the player wins the game.

Let’s breakout.js update the file by adding a check at the bottom:

function isWon() {
  return violetBricks.countActive() + yellowBricks.countActive() + redBricks.countActive() === 0;
}

Copy the code

We take each Sprite group as an argument, add the number of active sprites to it, and check if it equals 0.

Now that we have defined the win-loss conditions, we want the Phaser to check them at the beginning of the game loop. Once the player wins or loses, the game should stop.

Let’s update the update() function:

function update() {
  // Check if the ball left the scene i.e. game over
  if (isGameOver(this.physics.world)) {
    // TODO: Show "Game over" message to the player
  } else if (isWon()) {
    // TODO: Show "You won!" message to the player
  } else {
    // TODO: Logic for regular game time
  }
}

Copy the code

Use keyboard input to move player

The player’s movements depend on keyboard input. To be able to track keyboard input. Now it is time to use the cursors variable.

And at the bottom of our create() function:

cursors = this.input.keyboard.createCursorKeys(); The cursor keys in Phaser track the use of six keyboard keys: up, right, down, left, Shift, and space.

Now we need to react to the state of the Cursors object to update the position of the player. Update () adds the following to the else clause of the function:

// Put this in so that the player stays still if no key is being pressed
player.body.setVelocityX(0);

if (cursors.left.isDown) {
  player.body.setVelocityX(-350);
} else if (cursors.right.isDown) {
  player.body.setVelocityX(350);
}

Copy the code

Now we can move the player from left to right!

You’ll notice that the player Sprite can leave the game screen, and ideally, it can’t. We will address this later when dealing with conflicts.

Waiting to start

Before we add logic to move the ball, it helps if the game waits for user input before moving. Loading the game and being forced to boot immediately is not a good experience. Players don’t have time to react!

After the player presses the space bar, let’s move the ball up. If the user moves the racquet left or right, the ball is also moved, so it is always in the center of the racquet.

First, we need our own variables to keep track of whether the game is started or not. At the top of breakout.js, after declaring the game variables, add:

let gameStarted = false;
Copy the code

Now, in the else clause of our update function:

if (!gameStarted) {
  ball.setX(player.x);

  if (cursors.space.isDown) {
    gameStarted = true;
    ball.setVelocityY(-200);
  }
}
Copy the code

If the game has not yet started, please set the X coordinate of our ball as the player’s center. The coordinates of the game object are based on its center, so the X and Y attributes of the Sprite point to the center of our Sprite.

Note that while it is possible for X to get the property value by directly referencing the property value when setting the property, we always try to use the appropriate setter functions. Setter functions can include validating our input, updating another property, or the logic that fires an event. It makes our code more predictable.

Just like we did before moving the Player, we check to see if we pressed the space bar. If this button is pressed, we switch the gameStarted flag to True so that the ball no longer follows the player’s horizontal position, and set the ball’s Y speed to -200. Negative y velocity sends the object up. For positive velocity, larger values move objects down faster. For negative velocity, smaller values move objects up faster.

Now, when we move the player, the ball follows, and if we press the space bar, the ball shoots up:

So far, you’ll have observed some of the behavior in our game:

  • Balls render behind bricks
  • The player can leave the boundaries of the screen
  • The ball can leave the boundary of the screen

The ball is rendered behind the blocks because it was added to the game in the build function before the block Sprite set. In Phaser, the HTML5 Canvas element is typically used, with the most recently added image drawn on top of the previous image.

The last two problems can be solved by adding some world collisions.

### Handle collisions #### World collisions All of our Sprite interactions are defined in the create function. Using Phaser to easily collide with the world scene, add the following at the end of the create function:

player.setCollideWorldBounds(true);
ball.setCollideWorldBounds(true);
Copy the code

It should give us something like this:

When the player is moving normally, the ball appears to be stuck on top. To solve this problem, we need to set bounce ball Sprite properties. This bounce property tells the Phaser how much speed to maintain after a collision with the object.

Add this to the end of the create() function:

ball.setBounce(1, 1);
Copy the code

This tells Phaser that the ball should maintain all its X and Y speeds. If we release the ball with the space bar, the ball should bounce up and down in the game world. We need to disable collision detection from the bottom of the game world.

If we don’t, we’ll never know when the game ends. Disable collisions with the bottom of the game world by adding the following line to the end of the create function:

this.physics.world.checkCollision.down = false;
Copy the code

We should now have a game like this:

Hit a brick

Now that our sports sprites are properly colliding with our game world, let’s look at collisions between balls and bricks and balls and players.

In our create() function, add the following line to the end:

this.physics.add.collider(ball, violetBricks, hitBrick, null, this);
this.physics.add.collider(ball, yellowBricks, hitBrick, null, this);
this.physics.add.collider(ball, redBricks, hitBrick, null, this);
Copy the code

When a ball collides with various brick sprites, the Collider method tells Phaser’s physical system to do so.

Every time you press the space bar, the ball shoots up. There’s no X velocity, so the ball will go straight back to the paddle. That would be a boring game. So, the first time we touch a brick, we’re going to set a random X velocity.

HitBrick at the bottom of the following breakout.js definition:

function hitBrick(ball, brick) { brick.disableBody(true, true); if (ball.body.velocity.x === 0) { randNum = Math.random(); If (randNum >= 0.5) {ball.body.setvelocityx (150); } else { ball.body.setVelocityX(-150); }}}Copy the code

The hitBrick() function accepts the first two arguments used in the collider() method, such as ball and violetBricks. The disableBody(true, true) brick call tells Phaser to deactivate it and hide it from the screen. If the X speed of the ball is 0, the speed is assigned to the ball according to the value of random number.

If a ball rolls toward your foot at a slow speed, it will stop on impact. By default, the Arcade Physics engine simulates the impact of a collision on speed. For our game, we don’t want the ball to lose speed when it hits a brick. We need to set the immovable attribute to the Sprite group true.

Updated definitions violetBricks, yellowBricks and redBricks in the following:

// Add violet bricks
violetBricks = this.physics.add.group({
  key: 'brick1',
  repeat: 9,
  immovable: true,
  setXY: {
    x: 80,
    y: 140,
    stepX: 70
  }
});

// Add yellow bricks
yellowBricks = this.physics.add.group({
  key: 'brick2',
  repeat: 9,
  immovable: true,
  setXY: {
    x: 80,
    y: 90,
    stepX: 70
  }
});

// Add red bricks
redBricks = this.physics.add.group({
  key: 'brick3',
  repeat: 9,
  immovable: true,
  setXY: {
    x: 80,
    y: 40,
    stepX: 70
  }
});
Copy the code

Our brick collision is now complete, and our game should work like this:

Development Tip – When developing the physics of your game, you may want to enable debug mode to see the bounding boxes of sprites and how they collide with each other. In your game Config object, in the property gravity we defined in Arcade, you can enable debugging by adding it to the object:

debug: true
Copy the code

The player conflict

Handling collisions between balls and players is similar. First, make sure the player is IMmovable. Add the following at the end of the create() function:

player.setImmovable(true);
Copy the code

Then we add a collider between the ball and player:

this.physics.add.collider(ball, player, hitPlayer, null, this);
Copy the code

When the ball hits a player, we want two things to happen:

  • The ball should move faster to gradually increase the difficulty of the game
  • The horizontal direction of the ball depends on which side of the player it hits – if the ball hits the left side of the player then it should go left, if the ball hits the right side of the player then it should go right.

To accommodate these conditions, let’s update breakout.js with the following hitPlayer() functionality:

function hitPlayer(ball, player) { // Increase the velocity of the ball after it bounces ball.setVelocityY(ball.body.velocity.y - 5); let newXVelocity = Math.abs(ball.body.velocity.x) + 5; // If the ball is to the left of the player, ensure the X-velocity is negative if (ball.x < player.x) { ball.setVelocityX(-newXVelocity); } else { ball.setVelocityX(newXVelocity); }}Copy the code

Note: one Sprite can collide with another, one Sprite can collide with groups of sprites, and groups of sprites can collide with each other. Phaser is smart enough to use the collision function we defined in the context.

Now we have both player conflict and brick conflict:

To add text

Even though our game worked perfectly, people playing it didn’t know how to start or when the game ended.

Let’s add three new global variables at the top of the gameStarted declaration that will store our text data breakout.js:

let openingText, gameOverText, playerWonText;
Copy the code

Start interface

Let’s load the game with some text that tells the player to press space. Add the following code to the create() function:

openingText = this.add.text( this.physics.world.bounds.width / 2, this.physics.world.bounds.height / 2, 'Press SPACE to Start', { fontFamily: 'Monaco, Courier, monospace', fontSize: '50px', fill: '#fff' } ); OpeningText. SetOrigin (0.5);Copy the code

The first two arguments to the text method are the X and Y coordinates of the text box. We use the width and height of the game scene to determine its placement – centered. The third parameter is the text to display. The fourth argument is the JS object that contains the typeface data.

Unlike sprites, the X and Y coordinates of a text object refer to the top-left point of the object, not its center. This is why we use the setOrigin() method to make the coordinate system work like sprites, in this case it makes it easier to locate in the center.

When playing a game, we no longer want to see opening text. In the update() function, change the statement that checks if the space bar is pressed to the following:

if (cursors.space.isDown) {
  gameStarted = true;
  ball.setVelocityY(-200);
  openingText.setVisible(false);
}
Copy the code

Text objects are not sprites, and we cannot disable their bodies. We can make them invisible when we don’t need to see them. Our game now starts as follows:

Game over and win the match

As before, we need to add text objects to the create() function and make them invisible so that we don’t see them when the game starts:

// Create game over text gameOverText = this.add.text( this.physics.world.bounds.width / 2, this.physics.world.bounds.height / 2, 'Game Over', { fontFamily: 'Monaco, Courier, monospace', fontSize: '50px', fill: '#fff' } ); GameOverText. SetOrigin (0.5); // Make it invisible until the player loses gameOverText.setVisible(false); // Create the game won text playerWonText = this.add.text( this.physics.world.bounds.width / 2, this.physics.world.bounds.height / 2, 'You won! ', { fontFamily: 'Monaco, Courier, monospace', fontSize: '50px', fill: '#fff' } ); PlayerWonText. SetOrigin (0.5); // Make it invisible until the player wins playerWonText.setVisible(false);Copy the code

Now that they are defined, we must change their visibility in the update() function:

// Check if the ball left the scene i.e. game over
if (isGameOver(this.physics.world)) {
  gameOverText.setVisible(true);
  ball.disableBody(true, true);
} else if (isWon()) {
  playerWonText.setVisible(true);
  ball.disableBody(true, true);
} else {
  ...
}

Copy the code

We disabled the sphere, so we no longer need to update and display the sphere.

If we lose the game, we will see:

If we win the game, here’s what we’ll see:

Our game of brick breaking is complete!

conclusion

Phaser is an HTML5 game development framework that allows us to quickly build video games on the web. In addition to abstraction through HTML5 apis, it provides us with useful utilities, such as a physics engine, and manages the game loop — the execution life cycle of all games.

The complete code

// Game objects are global variables so that many functions can access them
let player, ball, violetBricks, yellowBricks, redBricks, cursors;
// Variable to determine if we started playing
let gameStarted = false;
// Add global text objects
let openingText, gameOverText, playerWonText;

// This object contains all the Phaser configurations to load our game
const config = {
  /**
   * The type can be Phaser.CANVAS, Phaser.WEBGL or Phaser.AUTO. AUTO means that
   * Phaser will try to render with WebGL, and fall back to Canvas if it fails
   */
  type: Phaser.AUTO,
  // Parent element to inject the Canvas/WebGL element with the game
  parent: 'game',
  width: 800,
  heigth: 640,
  scale: {
    // Ensure the canvas is resized to fit the parent div's dimensions
    mode: Phaser.Scale.RESIZE,
    // Center the game canvas both horizontally and vertically within the parent
    autoCenter: Phaser.Scale.CENTER_BOTH
  },
  /**
   * A scene is "self-contained" game world - all the logic and state of a game
   * component. For e.g. it's common to a game menu to be one scene, whereas the
   * first level is another scene. Phaser has a Scene object, but we can provide
   * a regular JS object with these function names:
   */
  scene: {
    preload,
    create,
    update,
  },
  /**
   * The physics engine determines how objects interact with the world. Phaser
   * supports three physics engines out of the box: arcade, impact and matter.
   * Arcade is understood to be the simplest one to implement
   */
  physics: {
    default: 'arcade',
    arcade: {
      gravity: false
    },
  }
};

// Create the game instance
const game = new Phaser.Game(config);

/**
 * The function loads assets as Phaser begins to run the scene. The images are
 * loaded as key value pairs, we reference the assets by their keys of course
 */
function preload() {
  this.load.image('ball', 'assets/images/ball_32_32.png');
  this.load.image('paddle', 'assets/images/paddle_128_32.png');
  this.load.image('brick1', 'assets/images/brick1_64_32.png');
  this.load.image('brick2', 'assets/images/brick2_64_32.png');
  this.load.image('brick3', 'assets/images/brick3_64_32.png');
}

/**
 * We create our game world in this function. The initial state of our game is
 * defined here. We also set up our physics rules here
 */
function create() {
  /**
   * Coordinates start at 0,0 from the top left
   * As we move rightward, the x value increases
   * As we move downward, the y value increases.
   */
  player = this.physics.add.sprite(
    400, // x position
    600, // y position
    'paddle', // key of image for the sprite
  );

  // Let's add the ball
  ball = this.physics.add.sprite(
    400, // x position
    565, // y position
    'ball' // key of image for the sprite
  );

  // Add violet bricks
  violetBricks = this.physics.add.group({
    key: 'brick1',
    repeat: 9,
    immovable: true,
    setXY: {
      x: 80,
      y: 140,
      stepX: 70
    }
  });

  // Add yellow bricks
  yellowBricks = this.physics.add.group({
    key: 'brick2',
    repeat: 9,
    immovable: true,
    setXY: {
      x: 80,
      y: 90,
      stepX: 70
    }
  });

  // Add red bricks
  redBricks = this.physics.add.group({
    key: 'brick3',
    repeat: 9,
    immovable: true,
    setXY: {
      x: 80,
      y: 40,
      stepX: 70
    }
  });

  // Manage key presses
  cursors = this.input.keyboard.createCursorKeys();

  // Ensure that the player and ball can't leave the screen
  player.setCollideWorldBounds(true);
  ball.setCollideWorldBounds(true);
  /**
   * The bounce ensures that the ball retains its velocity after colliding with
   * an object.
   */
  ball.setBounce(1, 1);

  /**
   * Disable collision with the bottom of the game world. This needs to be added
   * so the ball falls to the bottom, which means that the game is over
   */
  this.physics.world.checkCollision.down = false;

  // Add collision for the bricks
  this.physics.add.collider(ball, violetBricks, hitBrick, null, this);
  this.physics.add.collider(ball, yellowBricks, hitBrick, null, this);
  this.physics.add.collider(ball, redBricks, hitBrick, null, this);

  // Make the player immovable
  player.setImmovable(true);
  // Add collision for the player
  this.physics.add.collider(ball, player, hitPlayer, null, this);

  // Create opening text
  openingText = this.add.text(
    this.physics.world.bounds.width / 2,
    this.physics.world.bounds.height / 2,
    'Press SPACE to Start',
    {
      fontFamily: 'Monaco, Courier, monospace',
      fontSize: '50px',
      fill: '#fff'
    },
  );

  /**
   * The origin of the text object is at the top left, change the origin to the
   * center so it can be properly aligned
   */
  openingText.setOrigin(0.5);

  // Create game over text
  gameOverText = this.add.text(
    this.physics.world.bounds.width / 2,
    this.physics.world.bounds.height / 2,
    'Game Over',
    {
      fontFamily: 'Monaco, Courier, monospace',
      fontSize: '50px',
      fill: '#fff'
    },
  );

  gameOverText.setOrigin(0.5);

  // Make it invisible until the player loses
  gameOverText.setVisible(false);

  // Create the game won text
  playerWonText = this.add.text(
    this.physics.world.bounds.width / 2,
    this.physics.world.bounds.height / 2,
    'You won!',
    {
      fontFamily: 'Monaco, Courier, monospace',
      fontSize: '50px',
      fill: '#fff'
    },
  );

  playerWonText.setOrigin(0.5);

  // Make it invisible until the player wins
  playerWonText.setVisible(false);
}

/**
 * Our game state is updated in this function. This corresponds exactly to the
 * update process of the game loop
 */
function update() {
  // Check if the ball left the scene i.e. game over
  if (isGameOver(this.physics.world)) {
    gameOverText.setVisible(true);
    ball.disableBody(true, true);
  } else if (isWon()) {
    playerWonText.setVisible(true);
    ball.disableBody(true, true);
  } else {
    // Put this in so that the player doesn't move if no key is being pressed
    player.body.setVelocityX(0);

    /**
     * Check the cursor and move the velocity accordingly. With Arcade Physics we
     * adjust velocity for movement as opposed to manipulating xy values directly
     */
    if (cursors.left.isDown) {
      player.body.setVelocityX(-350);
    } else if (cursors.right.isDown) {
      player.body.setVelocityX(350);
    }

    // The game only begins when the user presses Spacebar to release the paddle
    if (!gameStarted) {
      // The ball should follow the paddle while the user selects where to start
      ball.setX(player.x);

      if (cursors.space.isDown) {
        gameStarted = true;
        ball.setVelocityY(-200);
        openingText.setVisible(false);
      }
    }
  }
}

/**
 * Checks if the user lost the game
 * @param world - the physics world
 * @return {boolean}
 */
function isGameOver(world) {
  return ball.body.y > world.bounds.height;
}

/**
 * Checks if the user won the game
 * @return {boolean}
 */
function isWon() {
  return violetBricks.countActive() + yellowBricks.countActive() + redBricks.countActive() == 0;
}

/**
 * This function handles the collision between a ball and a brick sprite
 * In the create function, ball is a sprite and violetBricks, yellowBricks and
 * redBricks are sprite groups. Phaser is smart enough to handle the collisions
 * for each individual sprite.
 * @param ball - the ball sprite
 * @param brick - the brick sprite
 */
function hitBrick(ball, brick) {
  brick.disableBody(true, true);

  if (ball.body.velocity.x == 0) {
    randNum = Math.random();
    if (randNum >= 0.5) {
      ball.body.setVelocityX(150);
    } else {
      ball.body.setVelocityX(-150);
    }
  }
}

/**
 * The function handles the collision between the ball and the player. We want
 * to ensure that the ball's direction after bouncing off the player is based
 * on which side of the player was hit. Also, to make things more difficult, we
 * want to increase the ball's velocity when it's hit.
 * @param ball - the ball sprite
 * @param player - the player/paddle sprite
 */
function hitPlayer(ball, player) {
  // Increase the velocity of the ball after it bounces
  ball.setVelocityY(ball.body.velocity.y - 5);

  let newXVelocity = Math.abs(ball.body.velocity.x) + 5;
  // If the ball is to the left of the player, ensure the x velocity is negative
  if (ball.x < player.x) {
    ball.setVelocityX(-newXVelocity);
  } else {
    ball.setVelocityX(newXVelocity);
  }
}

Copy the code