Phaser Framework 2.0 Tutorial

FOR THE NEW REVISED VERSION CLICK HERE

I just finished my 7th HTML5 game using Phaser JavaScript Framework, so I decided to write a detailed tutorial about it.

You can find the source files here: Download

FighterJet Challenge:

What you need to make your own game? Nothing else, only a text editor (or IDE, I use NetBeans), a browser, which supports HTML5 (and JavaScript), and if you want to create your own music then a music editor. Also an image editor, e.g. Gimp. I recommend not to use pirated software, because it can cause you troubles if you want to make your game public.

There are many sites, where you can download license-free materials, for example opengameart.com

Let's get started with programming: For the game I used only one state. Using states aren't necessary, but makes many things easier, for example if the game ends you can call the menu state, and from menu you can restart the game itself. Also you can make loading animations etc. Here is an example for a game without any state, only the game itself: photonstorm.com/phaser/tutorial-making-your-first-phaser-game

Also there are 3+1 main functions in the game you can use:

  • Preload:
  • Here you preload images, sounds, spritesheets, and tilemaps, which we will use later for the game itself.
  • Create:
  • You add the sprites, music, tiles... to your Canvas, also you can define Groups, dimensions, rules and much more here.
  • Update:
  • It is called as many times as possible using requestAnimationFrame. You can check here overlaps, sprite positions...
  • Debug:
  • We won't use this one at this game, but it is very useful for debugging the game while running it
It is not necessary to use all of them, but you have to tell to Phaser which ones will you use, when you initialize your game.

var game = new Phaser.Game(550, 310, Phaser.AUTO, 'target');  // Phaser.Game(width; height; WebGL or Canvas, Auto means WebGL by default, but if it is not supported, then Canvas; the target div, where we want to run our game

In case we don't want use states then:

var game = new Phaser.Game(550, 310, Phaser.AUTO, 'target', {preload: preload, create: create, update: update/*, render: render*/});

As I mentioned we only have one state: 'main_state'

var main_state = {/*Our function will be here*/};

In the Preload function we load our assets. You can use 'game' keyword instead of 'this', both means the same in this program. The first parameter is the name of the sprite or sound, the second is the path to the item.

   preload: function() {
        this.load.image('plane', 'assets/plane.png');
        this.load.image('shadow', 'assets/shadow.png');
        this.load.image('sky', 'assets/sky01.png');
        this.load.image('rockup', 'assets/rock.png');
        this.load.image('rockdown', 'assets/rockDown.png');
        this.load.image('line', 'assets/line.png');
        this.load.image('health', 'assets/health.png');
        this.load.image('ground', 'assets/ground.png');
        this.load.image('star', 'assets/star.png');
        this.load.image('twitter', 'assets/twitter.png');
        /* Use wav or ogg for sounds, because Firefox can't play mp3 */
        game.load.audio('hit', ['assets/hit.wav']);
        game.load.audio('pickup', ['assets/pickup.wav']);
    },

If our Preload function is ready the Create function will be called:

    create: function() {
/*Some necessary variables*/
        started = 0;
        points = 0;
        i = 0;
        rockspeed = -225;
/*We start our Physics engine, Arcade is the easiest, so we will use it now*/
        this.physics.startSystem(Phaser.Physics.ARCADE);
/*We add our 'sky' sprite as our background, which starts from the 0 X and 0 Y coordinate. The sprites anchor by default is in its 0,0 coordinate*/
        this.add.sprite(0, 0, 'sky');
/*We add our sounds*/
        hit = this.add.audio('hit');
        pickup = this.add.audio('pickup');
/*Two necessary arrays*/
        hp = new Array(); // Health points, which are the hearts in the top right corner
        rock = new Array();
        /*We add our plane, which can be controlled by the player*/
        player = this.add.sprite(this.world.centerX - 120, this.world.centerY, 'plane');
        player.anchor.setTo(0.5, 0.5); //Here we set the anchor to the center of the player sprite
        this.physics.enable(player, Phaser.Physics.ARCADE); // Player will use Arcade physics engine
/*We collide with the sides of our canvas*/
        player.body.collideWorldBounds = true;
/*We create our first group, for the stars*/
        stars = game.add.group(); // Groups name will be 'stars'
        stars.enableBody = true; // We enable physical body for all 'stars' group member
        stars.physicsBodyType = Phaser.Physics.ARCADE; //The whole group will use Arcade physics engine
        /*Same as above, but for rocks*/
        rocks = game.add.group();
        rocks.enableBody = true;
        rocks.physicsBodyType = Phaser.Physics.ARCADE;
        
        rocksdisplay = game.add.group();
        rocksdisplay.enableBody = true;
        rocksdisplay.physicsBodyType = Phaser.Physics.ARCADE;
/*We "kill" the sprites which are out of the Canvas*/
        stars.setAll('outOfBoundsKill', true);
        rocks.setAll('outOfBoundsKill', true);
        rocksdisplay.setAll('outOfBoundsKill', true);
/*Adding 3 hearts*/
        for (j = 0; j < 3; j++) {
            hp[j] = this.add.sprite(10 + j * 20, 10, 'health');
        }
        j = 2;//IDs of the hearts: hp[0], hp[1], hp[2]
/*Adding our 'ground' sprite*/
        ground = this.add.sprite(0, this.world.height - 25, 'ground');
        this.physics.enable(ground, Phaser.Physics.ARCADE);
/*Sprite for a shadow, it won't have physical body, it is just for display*/
        shadow = this.add.sprite(this.world.centerX - 120, this.world.height - 20, 'shadow');
		
        shadow.anchor.setTo(0.5, 0.5);
        shadow.scale.setTo(0.2, 0.2);
/*Point counter*/
        text = game.add.text(game.world.width - 80, 15, "Points: 0", {
            font: "12px \"Press Start 2P\"",
            fill: "#333",
            align: "center"
        });
        text.anchor.setTo(0.5);
/*Starting text*/
        text2 = game.add.text(this.world.centerX, this.world.centerY - 25, "Click with your mouse to start!\nYour last score: " + score, {
            font: "12px \"Press Start 2P\"",
            fill: "#333",
            align: "center"
        });
        text2.anchor.setTo(0.5);
/*Share button*/
        share = this.add.sprite(this.world.centerX, this.world.centerY + 50, 'twitter');
        share.anchor.setTo(0.5);
        share.inputEnabled = true; // If input is enable, it can be clicked
        share.events.onInputDown.add(this.shareClicked, this); //Adding the 'shareClicked' function if the Share button is clicked
    },

Let's see our Update function:

update: function() {
/*If the game is not started already and the mouse is clicked, but we are not clicking on the Share button, then starts the game*/
        if (started === 0 && this.input.activePointer.isDown && sharing === 0) {
            started = 1; // Game started
            player.body.gravity.y = 235; // Giving gravity to our plane

            game.time.events.add(Phaser.Timer.SECOND * 1.5, function() { // Counts 1.5 seconds
                this.time.events.loop(Phaser.Timer.SECOND * 3, this.createStars, this); // Every 3 seconds a new star is called, exactly between two rocks
            }, this);
            this.time.events.loop(Phaser.Timer.SECOND * 3, this.createRocks, this); // Calling the rocks every 3 seconds
            share.visible = false; // We make the share button invisible
            text2.visible = false; // Same with the starting text
        }
        sharing = 0; // Share is not clicked
        game.physics.arcade.overlap(player, rocks, this.rockHit, null, this); // Calling the 'rockHit' function whenever our plane touches a rock
        game.physics.arcade.overlap(ground, player, this.playerDown, null, this); // When we touch the ground
        game.physics.arcade.overlap(stars, player, this.caughtStar, null, this); //Or when we caught a star

        if (started === 1)  {// If the game is already running
            if (this.input.activePointer.isDown) { // Mouse is down
                player.body.velocity.y = -165; // Players velocity on the Y axis is -165 pixel per second
                player.angle = -3; // Planes cone is up a little
            } else {
                player.angle = 0; // Everything is normal if the mouse button is not down
            }
        }
    },

Now our other function:

shareClicked: function() {
        	sharing = 1; // We are clicking on the Share button
	window.open("https://twitter.com/intent/tweet?url=http://svejkgames.com/game/fighterjet/&text=I+just+made+" + score + "+points+on+FighterJet+Challenge!", "_blank"); // Opening the link on a blank page
    },
    caughtStar: function(a, b) { // a is the plane, b is the star
        b.kill(); // "Killing" the star
        points++;// +1 point
        text.setText("Points: " + points); // Updating our point counter text
        pickup.play(); // Playing the pickup sound
    },
createRocks: function() {
        i++;// incrementing i, which will be the index of our current rock
        rnd = this.rnd.integerInRange(-119, 119); //random generator from -119 to 119
        rock[i] = rocksdisplay.create(this.world.width + 50, rnd, 'rockdown'); // Creating a new 'rockdisplay' group member
        rock[i].anchor.setTo(0.5);
        rock[i].body.velocity.x = rockspeed; // -255 velocity on the X angle
/*Since in Arcade physics our sprite is always a rectangle, if we don't set it otherwise, so I will use a 1px wide, rock sprite tall transparent rectangle, which will be exactly on our rocks Y axis*/
        line = rocks.create(this.world.width + 58, rnd, 'line');
		
        line.anchor.setTo(0.5);
        line.body.velocity.x = rockspeed; // We move along with its rock
        line.name = i;// This lines name will be the index of the rock

        i++;// Other rock, exactly under our previous rock
        rock[i] = rocksdisplay.create(this.world.width + 50, rnd + 239 + 60, 'rockup');
        rock[i].anchor.setTo(0.5);
        rock[i].body.velocity.x = rockspeed;

        line = rocks.create(this.world.width + 58, rnd + 239 + 60, 'line');
        line.anchor.setTo(0.5);
        line.body.velocity.x = rockspeed;
        line.name = i;
    },
/*Generating sprites on random Y position*/
    createStars: function() {
        star = stars.create(this.world.width + 58, rnd = this.rnd.integerInRange(0, this.world.height - 50), 'star');
        star.anchor.setTo(0.5);
        star.body.velocity.x = rockspeed;
    },
    rockHit: function(a, b) { // In reality we hit our line, not the rock
        rock[b.name].kill(); // We "kill" the rock with the index of the line's name
        b.kill(); //"Killing" the line 
        hp[j].kill(); // "Killing" the hearth with the largest index
        j--; // Decrementing our j variable
        hit.play(); // Playing the hit sound

        if (j === -1) { // If our last heart (index: 0) is "killed" then we restart the game
            score = points; // Our score will be the current points
            this.state.start('main'); // Starting the main state again
        }
    },
	
    playerDown: function(a, b) { // Runs when player collides (in this case overlaps, even by one pixel) the ground sprite
        hit.play(); // Playing the hit sound
        score = points;
        game.state.start('main');
    } // no ',' because this is our last function

And of course we have to add these 2 lines to our code to start the game at the firs load:

		game.state.add('main', main_state); // We add a new state
		game.state.start('main'); // We run the 'main' state for the first time
	

Now let's see our index.html file:

<!DOCTYPE html>
<html>
    <head>
        
        
        
        
    </head>
    <body>
        
</body> </html>

If you have any question, suggestion, or you found a grammar error or syntax error in the program, please feel free to contact me or leave a comment.

Other Phaser tutorials: lessmilk.com/phaser-tutorial/

Thank you for reading!


comments powered by Disqus

Follow us on Twitter