šŸŽ„ Get 20% off from our JavaScript course for the holidays! šŸŽ„
šŸ’” This page contain affiliate links. By making a purchase through them, we may earn a commission at no extra cost to you.
Recreate Breakout with Emojis in JavaScript

Recreate Breakout with Emojis in JavaScript

Creating your very first game in Phaser
Ferenc Almasi • Last updated 2022 July 05 • Read time 17 min read
Learn how you can use the Phaser game framework to your advantage to build the famous Breakout game entirely in JavaScript.
  • twitter
  • facebook
JavaScript

Ever since I started coding, I always dreamt about coding my own games. This hasn’t stopped since then and now I’m at a level where I can write simple arcade-style games such as the famousĀ Breakout. It is interesting to see how we can implement the same using only JavaScript.

We are not going to start from zero though. Making even the simplest games can take up some time if you’re going with vanilla JavaScript. You don't just have to take care of assets like images, sounds, or animations but you also have to think about rendering, physics, or user input. To make things a little bit more simple we are going to make use of a game framework written in Javascript calledĀ Phaser. That way, we can focus on creating game features rather than dealing with core functionalities like handling user input.


Setting Up The Project

Before diving into coding, we are going to need a couple of things at our disposal. To make things even work, a web server will be needed. To serve files, I’m usingĀ http-serverĀ installed globally. That way, you can typeĀ http-serverĀ into the command line to start a web server in your directory. To install it globally, you can runĀ npm i http-server -g.

Next, we are going to need some images for the game elements. I went toĀ emojipediaĀ to create screenshots from different emojis, namely:

  • šŸ“Ā triangular rulerĀ for the paddle
  • šŸŽ±Ā pool 8 ballĀ for the ball
  • šŸ™„Ā rolling eyesĀ for the bricks
  • 😯 hushed faceĀ for the destroyed state of the bricks

I’ve rotated the paddle by 45° and made it to 100 x 55 px. I resized the ball to be 40 by 40 and I made the bricks 50 x 50 px. In the end, we are left with these four assets:

The collection of assets used

Now that we have every asset in place, we can create the project structure. Create anĀ indexĀ file that holds references to the Phaser library and our JavaScript file where we will write our game:

Copied to clipboard! Playground
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <meta http-equiv="X-UA-Compatible" content="ie=edge" />
        <title>Breakout</title>
        <style>* { margin: 0; overflow: hidden; }</style>
    </head>
    <body>
        <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/phaser.min.js"></script>
        <script src="breakout.js"></script>
    </body>
</html>
index.html

We don’t need to worry about adding any additional elements as Phaser will create aĀ canvasĀ for us. Everything we have for the game will be drawn on that. You can pull in the latest version of Phaser from CDN and apply some styles to remove margins and scrollbars. We can also create a folder for the assets and of courseĀ breakout.js. This leaves us with the following project structure:

The project structure layout
The project structure of the breakout game

Configuring Phaser

Every Phaser game starts with a configuration object. It will hold some very basic information about ourĀ canvas. It’s also a good time to define variables for each game object that we will use throughout the game.

Copied to clipboard! Playground
let ball;           // Game object for the ball
let paddle;         // Game object for the paddle
let bricks;         // Game object for the bricks
let scoreText;      // Game object for showing score
let livesText;      // Game object for showing lives
let startButton;    // Game object for the start button
let gameOverText;   // Game object for showing "Game Over!"
let wonTheGameText; // Game object for showing "You won the game!"
let rotation;       // Flag will be used to define which direction the ball should rotate

let score = 0;      // Variable holding the number of scores
let lives = 3;      // Variable holding the remaining lives

// We are going to use these styles for texts
const textStyle = { 
    font: 'bold 18px Arial', 
    fill: '#FFF' 
};

const config = {
    type: Phaser.AUTO,
    width: window.innerWidth,
    height: window.innerHeight,
    backgroundColor: '#222',
    physics: {
        default: 'arcade',
        arcade: {
            // debug: true - Set debug: true if you want collision boxes to be drawn
            checkCollision: {
                up: true,
                down: false,
                left: true,
                right: true
            }
        }
    },
    scene: {
        preload,
        create,
        update
    }
};

const game = new Phaser.Game(config);
breakout.js

Apart from the individual game objects, we can also create a constant for the text styles. Lastly, theĀ configĀ object will be passed toĀ Phaser.GameĀ to initialize the game. This configuration object takes in the following properties:

  • typeĀ will be used to tell Phaser which renderer to use. UsingĀ Phaser.AUTOĀ will choose WebGL if available. Otherwise, it will fall back to canvas.
  • TheĀ widthĀ andĀ heightĀ properties alongside withĀ backgroundColorĀ are used to set the corresponding styles for the canvas element.
  • TheĀ physicsĀ object is used to tell which physics system to use. It can be eitherĀ arcade,Ā impactĀ orĀ matter. Based on the type of game we are creating, we’re going to useĀ arcade. Here you can also pass another object with the name of the physics system to further configure it. By settingĀ debugĀ toĀ true, you can enable collision boxes to be drawn for the sprites.
  • checkCollisionĀ will be responsible for checking collisions on the world boundaries. We want to check for every side except for the bottom. This means the ball can fall through the screen to the bottom but not through the top or left and right side.

A Phaser game is made out of scenes. You can think of scenes as different phases of a game. You can have a scene for the loading screen, you can have one for the main menu or for the game itself. This is what theĀ sceneĀ object is used for. We can define here multiple scenes inside an array, but since we only have the game itself, we can also pass in a single object. Each scene is controlled by a different set of functions:

  • preloadĀ is used for loading in assets such as images or sounds
  • createĀ is used for adding game objects to the scene
  • updateĀ is called indefinitely and is used for animations or checks for conditions that can happen in any frame, such as the ball falling down

We used ES6 method shorthand which means we will need to define the corresponding functions for each property; one forĀ preloadĀ one forĀ create, and one forĀ update.Ā If your fire up your server and open your localhost, you will have an empty full-screen canvas.

Looking to improve your skills? Check out our interactive course to master JavaScript from start to finish.
Master JavaScript Remove ads

Preloading Assets

To load in our assets we can use the Phaser Loader plugin’sĀ imageĀ method:

Copied to clipboard! Playground
function preload() {
    this.load.image('paddle', 'img/paddle.png');
    this.load.image('brick', 'img/brick.png');
    this.load.image('destroyed', 'img/destroyed.png');
    this.load.image('ball', 'img/ball.png');
}
breakout.js

This is all it takes to load in every asset.Ā thisĀ references the current scene. InsideĀ image, we pass a key that we can later use to reference an asset, then we define the path for it.


Creating The World

To create the world we need to add aĀ createĀ function that will add all game objects to our scene. Implementing it will probably be the longest step but once in place, adding the game functionality will be a breeze. Let’s start with the paddle:

Copied to clipboard!
function create() {
    paddle = this.physics.add.image(this.cameras.main.centerX, this.game.config.height - 50, 'paddle')
        .setImmovable();
}
breakout.js

We can add image objects by callingĀ this.physics.add.image, passing in theĀ xĀ andĀ yĀ position and theĀ keyĀ we created inside theĀ preloadĀ function. We assign this to theĀ paddleĀ variable we defined at the beginning, since we are going to make use of it later.

To center it horizontally, we can get the middle of the screen withĀ cameras.main.centerX. To display it at the bottom, we get theĀ heightĀ of the canvas – 50px. We also callĀ setImmovableĀ to tell Phaser this body can’t be moved by collisions. Now we can move onto the ball:

Copied to clipboard!
ball = this.physics.add.image(this.cameras.main.centerX, this.game.config.height - 100, 'ball')
    .setCollideWorldBounds(true)
    .setBounce(1);
breakout.js

Same as with the paddle, we center it horizontally and we position it just above the paddle on the vertical axis. We also want the ball to collide with the world boundaries and bounce back from them, this is what we achieve with the function chaining. We’ve left with the bricks:

Copied to clipboard! Playground
bricks = this.physics.add.staticGroup({
    key: 'brick',
    frameQuantity: 20,
    gridAlign: { width: 10, cellWidth: 60, cellHeight: 60, x: this.cameras.main.centerX - 277.5, y: 100 }
});
breakout.js

For them, we are using aĀ staticGroup. TheĀ keyĀ references our asset’s name.Ā frameQuantityĀ is used for the number of times the image will be displayed andĀ gridAlignĀ is used for alignments:

  • widthĀ is used for the number of items displayed on one line. Since we want to display 20 items on two lines, we can use a 10Ɨ2 grid.
  • cellWidthĀ andĀ cellHeightĀ is for each individual item. The image itself is 50x50px and we want 5px paddings on each side so we can go with a value ofĀ 60. To center it horizontally, we getĀ centerX – (half of the width of the group). Lastly I also positioned it 100px from the top.
Way to calculate half of the grid size
The way we can calculate half of the grid’s size

Displaying texts

Displaying the user interface can be done in 1–1 line:

Copied to clipboard!
scoreText = this.add.text(20, 20, 'Score: 0', textStyle);
livesText = this.add.text(this.game.config.width - 20, 20,  `Lives: ${lives}`, textStyle).setOrigin(1, 0);
breakout.js

We can create texts using theĀ this.add.textĀ method. It takes four parameters: theĀ xĀ andĀ yĀ position, the text itself, and an optional styles object. Score will be displayed on the top left corner while ā€œlivesā€ will be on the top right.

The anchor point for the texts is on the top left corner by default, so to correctly position ā€œlivesā€, we need to move the anchor to the top right corner. This is whatĀ setOriginĀ is supposed to do. While here, let’s also add the game over and winning texts:

Copied to clipboard! Playground
gameOverText = this.add.text(this.cameras.main.centerX, this.cameras.main.centerY, 'Game over!', textStyle)
    .setOrigin(0.5)
    .setPadding(10)
    .setStyle({ backgroundColor: '#111', fill: '#e74c3c' })
    .setVisible(false);

wonTheGameText = this.add.text(this.cameras.main.centerX, this.cameras.main.centerY, 'You won the game!', textStyle)
    .setOrigin(0.5)
    .setPadding(10)
    .setStyle({ backgroundColor: '#111', fill: '#27ae60' })
    .setVisible(false);
breakout.js

We want to center them on the world. Since we have the anchor positioned on the top left corner again, we need to move it to the middle withĀ setOrigin(0.5). I also added some padding and overrides for the default styles withĀ setStyle. And as we don’t want them to be displayed at the start of the game, we can hide them with theĀ setVisibleĀ method.

Position of the anchor illustrated

We are left with the start button. Just like for the previous two, we want to set some styles to it:

Copied to clipboard! Playground
startButton = this.add.text(this.cameras.main.centerX, this.cameras.main.centerY, 'Start game', textStyle)
    .setOrigin(0.5)
    .setPadding(10)
    .setStyle({ backgroundColor: '#111' })
    .setInteractive({ useHandCursor: true })
    .on('pointerdown', () => startGame.call(this))
    .on('pointerover', () => startButton.setStyle({ fill: '#f39c12' }))
    .on('pointerout', () => startButton.setStyle({ fill: '#FFF' }));
breakout.js

To make it act like a button we can register inputs by callingĀ setInteractive. AddingĀ useHandCursorĀ will show a pointer when hovered instead of the default cursor. We can also define different event listeners on it using theĀ onĀ method. For hover, we can set a different fill color.Ā pointeroutĀ will be the blur event where we set back the style. On click — which is handled byĀ pointerdown — we call theĀ startGameĀ function.

Hovering on the start game button

Starting The Game

Now that we have everything on screen we can start implementing the gameplay logic. When we hit start, we want the following things to happen:

  • Remove the start button
  • Shoot out the ball
  • Move the paddle to the position where the cursor is
Copied to clipboard! Playground
function startGame() {
    startButton.destroy();
    ball.setVelocity(-300, -150);
    rotation = 'left';
    
    this.input.on('pointermove', pointer => {
        paddle.x = Phaser.Math.Clamp(pointer.x, paddle.width / 2, this.game.config.width - paddle.width / 2);
    });
}
breakout.js

We can remove the start button by callingĀ destroyĀ on it. To shoot out the ball we can apply a force usingĀ setVelocity. It takes in two forces, one on theĀ xĀ and one on theĀ yĀ axis. We also set the rotation toĀ leftĀ which we will later use to rotate the ball as it flies.

Finally, to move the paddle we can add an event listener on the whole scene withĀ input.on. Inside the callback, we set the paddle’s x position to the mouse x position. To avoid moving it outside of the screen, we forceĀ pointer.xĀ to be between a min and a max value. This is done using theĀ Math.ClampĀ method.

Starting the game without collision
Without collision, the ball just flies around without hitting anything

Adding Collision Detection

Since there’s no collision, the ball just flies around without any purpose. We want two collisions to happen: one between the ball and the bricks and one between the ball and the paddle. To create these collisions, add the following two lines as the last thing in yourĀ createĀ function:

Copied to clipboard!
this.physics.add.collider(ball, bricks, brickHit, null, this);
this.physics.add.collider(ball, paddle, paddleHit, null, this);
breakout.js

add.colliderĀ expects 5 params:

  • The two objects which between the collision happens
  • A callback function that will run whenever the two objects collide
  • A process callback, which will fire when the two objects intersect. It is similar to the callback function, but it must return a boolean. We can leave itĀ null.
  • The context of the callback function

When the ball collides with a brick, we run theĀ brickHitĀ function, when it collides with the paddle, we run theĀ paddleHitĀ function. Let’s see first what happens when we hit a brick:

Hitting the bricks

When a brick is hit, we want to create the following animation:

Animation when a brick is being hit

We change the brick’s texture and after a short time, we shrink it till it disappears. Of course, we also want to give some scores to the player so they don’t leave right away. Once no more bricks are left, we can display the ā€œYou won!ā€ message.

Copied to clipboard! Playground
function brickHit(ball, brick) {
    brick.setTexture('destroyed');
   
    score += 5;
    scoreText.setText(`Score: ${score}`);

    this.tweens.add({
        targets: brick,
        ease: 'Power1',
        scaleX: 0,
        scaleY: 0,
        angle: 180,
        duration: 500,
        delay: 250,
        onComplete: () => { 
            brick.destroy();

            if (bricks.countActive() === 0) {
                ball.destroy();

                wonTheGameText.setVisible(true);
            }
        }
    });
}
breakout.js

To switch between textures we can use theĀ setTextureĀ method where we pass in the key of the preloaded asset. After increasing the score, we simply re-set the text to be updated. To create the animation, we can useĀ tween.add.

As you can see, we have a bunch of configuration options to set. targets will determine which game object will be animated. We can add easings for the animation and a duration. By setting scaleX and scaleY to 0, we can shrink it down and by using angle: 180 it will rotate it by 180° in the meantime. To stop the animation from starting as soon as the collision happens, we can also specify a delay.

Once the animation completes, we can get rid of the brick and also do a check. If there are no more bricks on the screen, we can remove the ball and display the ā€œYou won!ā€ message.

Hitting the paddle

We could actually get away without adding any functionality for hitting the paddle as the ball will bounce off of it, since we alreadyĀ add.colliderĀ defined andĀ bounceĀ set to 1. With just a couple of lines however we can create some randomness in the game:

Copied to clipboard! Playground
function paddleHit(ball, paddle) {
    let diff = 0;

    if (ball.x < paddle.x) {
        diff = paddle.x - ball.x;
        ball.setVelocityX(-20 * diff);
        rotation = 'left';
    } else if (ball.x > paddle.x) {
        diff = ball.x - paddle.x;
        ball.setVelocityX(20 * diff);
        rotation = 'right';
    } else {
        ball.setVelocityX(2 + Math.random() * 10);
    }
}
breakout.js

If the ball’s x position is less than the paddle’s x position, it means that the ball hit the left side of the paddle. In this case, we want to apply a negative x force to shoot it to the left side. Otherwise, it hit the right-hand side in which case, we shoot it to the right. If it falls completely perpendicular to the paddle, we still want to add some x velocity to avoid shooting it straight up. We will also switch directions between the rotation based on which side of the paddle the ball falls.

Looking to improve your skills? Check out our interactive course to master JavaScript from start to finish.
Master JavaScript Remove ads

Game Over

The only missing part is theĀ updateĀ function that will handle the ball’s rotation as well as our lives to see if we’re about to lose.

Copied to clipboard! Playground
function update() {
    if (rotation) {
        ball.rotation = rotation === 'left' ?  ball.rotation - .05 : ball.rotation + .05;
    }

    if (ball.y > paddle.y) {
        lives--;

        if (lives > 0) {
            livesText.setText(`Lives: ${lives}`);

            ball.setPosition(this.cameras.main.centerX, this.game.config.height - 100)
                .setVelocity(300, -150);
        } else {
            ball.destroy();

            gameOverText.setVisible(true);
        }
    }
}
breakout.js

We can add a rotation to our ball by settingĀ ball.rotation, based on the value of ourĀ rotationĀ flag. The higher the value, the faster the rotation will be.

Rotating ball

To check whether we are about to lose a life or not, we can simply check if the ball is below the paddle. If it is, we decrease the number of lives and reset the ball’s position. If we are unlucky and there’s no more life left, we are presented with the game over message.

And that’s it, we are done. If you’ve made it this far, congratulations, you’ve just made your first game in JavaScript! šŸŽ‰ If you would like to check out the whole source code, you can clone it fromĀ this GitHub repository.

Do you have experience with Phaser? What are your thoughts on the framework? Let us know in the comments! Thank you for reading through, happy gaming!

Playing the game created in Phaser
How I Made A Snake Game Out Of Checkboxes
Did you find this page helpful?
šŸ“š More Webtips
Frontend Course Dashboard
Master the Art of Frontend
  • check Access exclusive interactive lessons
  • check Unlimited access to hundreds of tutorials
  • check Remove ads to learn without distractions
Become a Pro

Courses

Recommended