9 min read

Let’s take a look into writing a simple Breakout, Arkanoid for some of us, clone with Phaser. To keep it as simple as possible I’ve created a separate github repository for the code used in this post. I’m going to assume you have some experience in JavaScript. You should also give Phasers official web site a visit and see what the commotion is about.

Setup

To have everything in working order, download https://nodejs.org/ and have it working on your command prompt, meaning commands like node and npm are recognized.

If you are having difficulties or just like to explore the different options of creating a no hassle http server for Phaser projects, you can always look at the Phasers official getting started guide on the HTTP servers.

Project structure

Create a barkanoid directory on your local machine and extract or clone the files from the github repository into the directory. You should see the following project structure:

barkanoid
|
|----js
|----|----barkanoid.js
|----assets
|----|----background.jpg
|----|----ball.png
|----|----paddle.png
|----|----tile0.png
|----|----tile1.png
|----|----tile2.png
|----|----tile3.png
|----|----tile4.png
|----|----tile5.png
|----index.html
|----package.json
  • Assets is for all game related assets such as graphics, sounds and the likes.
  • Js directory is for all the JavaScript files and since we are keeping it as simple as possible for the sake of this post, it’s only one .js file.
  • index.html is the actual game canvas.
  • package.json is the node file that tells the Node package manager (npm) what to install when we use it.

Installing dependencies

There are a few dependencies that we first need to take care of, such as the actual Phaser itself and HTTP server we are going to serve our files from. Luckily for us, Node.js makes this super simple and with the project in the github you can just simply write the following command in the barkanoid directory.

npm install

It might take a while, depending on your Internet connection. All dependencies should now be installed.

Programming time

Phaser requires at least one HTML file to act as the starting canvas for our game, so let’s go ahead and create it. Save the index.html into the root of the Barkanoid directory for easier access.

index.html

<!doctype html>
<html>
   <head>
       <meta charset="UTF-8"/>
       <title>Barkanoid Example</title>
       <script src="/node_modules/phaser/dist/phaser.min.js"></script>
       <script src="/js/barkanoid.js"></script>
   </head>
   <body>
       <div id="barkanoid"></div>
   </body>
</html>

Notice the html elements attribute id=”barkanoid”, that HTML div element is the container where Phaser will inject the game canvas. This can be called anything really but it’s important to know what the id of the element is so we can actually tell Phaser about this.

Let’s continue with the js/barkanoid.js file. Create the Phaser game object and set it up with the HTML div element with the id “barkanoid”.

// phaserCreate the game object itself
var game = newPhaser.Game(
   800, 600,                       // 800 x 600 rebackgroundolution.
   Phaser.AUTO,                   // Allow Phaser to determine Canvas or WebGL
   "barkanoid",                   // The HTML element ID we will connect Phaser to.
   {                               // Functions (callbacks) for Phaser to call in
       preload: phaserPreload,     // in different states of its execution
       create: phaserCreate,
       update: phaserUpdate
   }
);

You can attach callbacks for Phasers preload, create, update and render. For this project we only need the preload, create and update.

Preload function:

/**
* Preload callback. Used to load all assets into Phaser.
*/
functionphaserPreload() {
   // Loading the background abackground an image
   game.load.image("background", "/assets/background.jpg");
   // Loading the tiles
 game.load.image("tile0", "/assets/tile0.png");
   game.load.image("tile1", "/assets/tile1.png");
   game.load.image("tile2", "/assets/tile2.png");
   game.load.image("tile3", "/assets/tile3.png");
   game.load.image("tile4", "/assets/tile4.png");
   game.load.image("tile5", "/assets/tile5.png");
   // Loading the paddle and the ball
   game.load.image("paddle", "/assets/paddle.png");
   game.load.image("ball", "/assets/ball.png");
}

This is nothing too fancy. I am keeping it as simple as possible and just loading set of images into Phaser with game.load.image and giving them simple aliases and a location of the file.

The following is the phaserCreate function, but don’t get scared, it’s actually quite simple even though a bit lengthy compared to the preload one. We’ll walk through it in three steps.

/**
* Create callback. Used to create all game related objects, set states and other pre-game running
* details.
*/
functionphaserCreate() {
   game.physics.startSystem(Phaser.Physics.ARCADE);
   // All walls collide except the bottom
   game.physics.arcade.checkCollision.down = false;
   // Using the in-game name to fetch the loaded asset for the Background object
   background = game.add.tileSprite(0, 0, 800, 600, "background");

Simply telling Phaser that Arcade style physics are enabled and that we do not want to check for collisions on the bottom of the screen and create a simple background from the background image.

// Continuing from the first part ...

   // Creating a tile group
   tiles = game.add.group();
   tiles.enableBody = true;
   tiles.physicsdBodyType = Phaser.Physics.ARCADE;
   // Creating N tiles into the tile group
   for (var y = 0; y < 4; y++) {
       for (var x = 0; x < 15; x++) {
           // Randomizing the tile sprite we load for the tile
           var randomTileNumber = Math.floor(Math.random() * 6);
           var tile = tiles.create(120 + (x * 36), 100 + (y * 52), "tile" + randomTileNumber);
           tile.body.bounce.set(1);
           tile.body.immovable = true;
       }
   }

Next create a group for the tiles object with game.add.group. The group can be of many different things but we are going to have a group of game objects for easier collision manipulation. The tile colors get randomized every time the game starts. Create four rows with 15 columns on them of tiles.

// Continuing from the second part ...

   // Setup the player -- paddle
   paddle = game.add.sprite(game.world.centerX, 500, "paddle");
   paddle.anchor.setTo(0.5, 0.5);
   game.physics.enable(paddle, Phaser.Physics.ARCADE);
   paddle.body.collideWorldBounds = true;
   paddle.body.bounce.set(1);
   paddle.body.immovable = true;

   // phaserCreate the ball
   ball = game.add.sprite(game.world.centerX, paddle.y - 16, "ball");
   ball.anchor.set(0.5);
   ball.checkWorldBounds = true;
   game.physics.enable(ball, Phaser.Physics.ARCADE);
   ball.body.collideWorldBounds = true;
   ball.body.bounce.set(1);
   // When it goes out of bounds we'll call the function 'death'
   ball.events.onOutOfBounds.add(helpers.death, this);

   // Setup score text
   scoreText = game.add.text(32, 550, "score: 0", defaultTextOptions);
   livesText = game.add.text(680, 550, "lives: 3", defaultTextOptions);
   introText = game.add.text(game.world.centerX, 400, "- click to start -", boldTextOptions);
   introText.anchor.setTo(0.5, 0.5);
   game.input.onDown.add(helpers.release, this);
}

This creates the player, the ball and some informative text elements.

And last but not least, the common update function Phaser calls every update cycle. This is where you can handle updating different objects, their states and other rocket-sciency parts one might have in a game.

/**
* Phaser Engines update loop that gets called every phaserUpdate.
*/
functionphaserUpdate () {
   paddle.x = game.input.x;

   // Making sure the player does not move out of bounds
   if (paddle.x < 24) {
       paddle.x = 24;
   } elseif (paddle.x > game.width - 24) {
       paddle.x = game.width - 24;
   }

   if (ballOnPaddle) {
       // Setting the ball on the paddle when player has it
       ball.body.x = paddle.x;
   } else {
       // Check collisions, the function gets called when the N collides with X
       game.physics.arcade.collide(ball, paddle, helpers.ballCollideWithPaddle, null, this);
       game.physics.arcade.collide(ball, tiles, helpers.ballCollideWithTile, null, this);
   }
}

You probably noticed the functions we are calling and objects we are using that were never declared anywhere, like defaultTextOptions and helpers.release. All the helper functions are defined after the callbacks for Phaser.

// Few game related variables that we'll leave undefined
var ball, paddle, tiles, livesText, introText, background;

var ballOnPaddle = true;
var lives = 3;
var score = 0;

var defaultTextOptions = { font: "20px Arial", align: "left", fill: "#ffffff" };
var boldTextOptions = { font: "40px Arial", fill: "#ffffff", align: "center" };

/**
* Set of helper functions.
*/
var helpers = {
   /**
     * Releases ball from the paddle.
     */
   release: function() {
       if (ballOnPaddle) {
           ballOnPaddle = false;
           ball.body.velocity.y = -300;
           ball.body.velocity.x = -75;
           introText.visible = false;
       }
   },

   /**
     * Ball went out of bounds.
     */
   death: function() {
       lives--;
       livesText.text = "lives: " + lives;

       if (lives === 0) {
           helpers.gameOver();
       } else {
           ballOnPaddle = true;
           ball.reset(paddle.body.x + 16, paddle.y - 16);
       }
   },

   /**
     * Game over, all lives lost.
     */
   gameOver: function() {
       ball.body.velocity.setTo(0, 0);
       introText.text = "Game Over!";
       introText.visible = true;
   },

   /**
     * Callback for when ball collides with Tiles.
     */
   ballCollideWithTile: function(ball, tile) {
       tile.kill();

       score += 10;
       scoreText.text = "score: " + score;

       // Are they any tiles left?
       if (tiles.countLiving() <= 0) {
           // New level start
           score += 1000;
           scoreText.text = "score: " + score;
           introText.text = "- Next Level -";

           // Attach ball to the players paddle
           ballOnPaddle = true;
           ball.body.velocity.set(0);
           ball.x = paddle.x + 16;
           ball.y = paddle.y - 16;

           // Tell tiles to revive
           tiles.callAll("revive");
       }

   },

   /**
     * Callback for when ball collides with the players paddle.
     */
   ballCollideWithPaddle: function(ball, paddle) {
       var diff = 0;

       // Super simplistic bounce physics for the ball movement
       if (ball.x < paddle.x) {
           // Ball is on the left-hand side
           diff = paddle.x - ball.x;
           ball.body.velocity.x = (-10 * diff);
       } elseif (ball.x > paddle.x) {
           // Ball is on the right-hand side
           diff = ball.x -paddle.x;
           ball.body.velocity.x = (10 * diff);
       } else {
           // Ball is perfectly in the middle
           // Add a little random X to stop it bouncing straight up!
           ball.body.velocity.x = 2 + Math.random() * 8;
       }
   }
};

Most of the helper functions are pretty self-explanatory and there’s a decent amount of comments around them so they should be easy to understand.

Time to play the game

After about 200 lines or so of code and setting everything up, you should be ready to say nmp start in the barkanoid directory to start the game.

Enjoy the Barkanoid game you just created. Play a round or two and start customizing it as much as you want. Have fun!

About the author

Mika Turunen is a software professional hailing from the frozen cold Finland. He spends a good part of his day playing with emerging web and cloud related technologies, but he also has a big knack for games and game development. His hobbies include game collecting, game development and games in general. When he’s not playing with technology he is spending time with his two cats and growing his beard.

LEAVE A REPLY

Please enter your comment!
Please enter your name here