00:00
00:00
View Profile PsychoGoldfish

Male

NG Code Monkey

Hard Knocks

Ur Mom

Joined on 9/26/00

Level:
13
Exp Points:
1,726 / 1,880
Exp Rank:
28,119
Vote Power:
5.51 votes
Rank:
Town Watch
Global Rank:
48,915
Blams:
46
Saves:
119
B/P Bonus:
2%
Whistle:
Normal
Trophies:
41
Medals:
605

Getting Started With Phaser

Posted by PsychoGoldfish - January 24th, 2020


Who is this for?


This intro is for anyone who wants a free tool for making web-based HTML5 games. I am assuming you have a basic familiarity with ES6 JavaScript, but any decent JavaScript knowledge should be good enough.


You can view the finished game we build in this tutorial here, and download the full source here.


What is Phaser?


Phaser is a JavaScript-based framework for building HTML5 web games. It was written by @PhotonStorm and has been in active development since 2013 (well before most of us even thought about switching from Flash to HTML5).


It's also one of my personal favorite game development frameworks. There are tutorials and examples for damn near anything you want to do, an their community is always active and helpful.


Why I prefer Phaser over other game frameworks


I think it mostly comes down to two things. I have a history with Flash and ActionScript that's shared by Phaser, and I like having as much control as possible in how I build things. I really loved the Starling framework fro ActionScript 3 games, and Phaser has a very similar workflow.


I've never been a big fan of programming game objects with drag and drop widgets or clicking baked in behaviors. I'm a bit of a control freak. Tools like Unity and Construct WILL let you code things manually, but if I'm going to be doing everything that way anyhow, I prefer to work lean.


With Phaser, you are working in pure JavaScript (or TypeScript if you prefer), so you don't have to worry about what kind of bloat some outside IDE is going to add. A simple game, like Flappy Bird, would be much smaller in size and memory use, while probably running better on lower-end hardware than the exact same game built with Unity's WebGL export.


There are drawbacks to choosing Phaser. It's a pure code library, not an IDE. You need 3rd party tools for art, animation and level design. Of course, that's not always a drawback. Because Phaser is just code, it can't crash and cause you to lose hours of work. Each tool you use may crash for whatever reason, but it will only cause you to lose maybe a single drawing or one level layout at most. It keeps you from having all your eggs in one basket.


The biggest reason I prefer Phaser is it has, hands down, one of the fastest rendering engines I've seen for web-based HTML5 games. I've had some pretty ambitious demos working at 60fps on my old low-end laptop without even using WebGL.


Getting Started


Before we get too far ahead of ourselves, there's a few things you should know before you get started. Because we are targeting web browsers with our Phaser games, we will need to run a web server.


Most modern browsers have built-in security measures that prevent local files from doing things that could be considered risky. Trying to run your game by loading it directly will almost certainly fail.


I'm a bit fan of MiniWeb since it doesn't require any real setup or server admin skills, but you could use any web server you prefer.


If you DO use MiniWeb, you need to save it to whatever directory your game will be in; let's say C:\PhaserTutorial. Next you'll need to add an 'htdocs' directory so the server knows where to find your files.


In the htdocs folder, you can add a file called "index.html". In this file, just write "Hello World" or something.


If everything is set up right, you should be able to run miniweb.exe and it will have a line saying "host" (it'll look something like 123.45.678.910:8000). Type that exact host address in your browser, and you should see your "Hello World" text.


We are also going to need to add directories for our assets (sound, art, etc) and JavaScripts. Let's go ahead and add 'lib' (asset library) and 'js' (JavaScripts) directories inside our 'htdocs' folder.


To review, you should currently have a file setup something like this:


C:\PhaserTutorial
C:\PhaserTutorial\miniweb.exe
C:\PhaserTutorial\htdocs
C:\PhaserTutorial\htdocs\index.html
C:\PhaserTutorial\htdocs\lib
C:\PhaserTutorial\htdocs\js

Now that we have a working server, and our directories are all set up, we can install Phaser. At the time this was written, you could grab the latest version of Phaser 3 here. For this tutorial, we'll be using the 'js' version. When you build a game and are ready to release it, you'll probably want to use the min.js version to get a smaller download size.


Make sure your file is saved to C:\PhaserTutorial\js\phaser.js (or wherever your 'js' directory happens to be).


Now we need to update that 'index.html' file we made earlier so it's a proper HTML5 file, and loads the Phaser library. Replace your "Hello World" text with the following:


<!doctype html>
<html lang="en">
	<head>

		<meta charset="utf-8">

		<title>My Phaser Game</title>
		<meta name="description" content="This is a Phaser Game!">
		<meta name="author" content="Your Name Here">

		<style>
			body {
				background-color: black;
				margin: 0px;
				padding: 0px;
			}
		</style>

	</head>

	<body>

		<!-- This is where we load our scripts -->
		<script src="js/phaser.js"></script>

	</body>
</html>

This is a simple HTML5 template. The <head> tag contains our title and some other meta info, along with a style tag that makes the background black, and gets rid of margins. Newgrounds runs HTML5 games in an Iframe, so we don't want any gutters making our game look goofy.


We are now ready to start building a game! (If you test your app in the browser now, you should just see a black screen)


Organizing Your Project


Note: There's no real right or wrong way to set up your Phaser project. If you've done other tutorials, you'll have seen examples where you create a Phaser instance and use functions to handle everything. I prefer to put most of my logic in Phaser scenes, and use some utility scripts to pull them all together.


This will add another layer of learning that most introductory tutorials don't add on, but I think learning how to organize your project is just as important as actually coding it.


Phaser uses scenes and Sprites for most of it's magic. These are classes that have baked in events like 'preload' and 'update' that come in very handy (we'll get into all that soon).


Before Phaser can use any scenes, you have to tell it what scene objects are available. This is done in the config object Phaser uses when it is instantiated.


And before any of your scene files can even be used, we have to load their js files. Wouldn't it be nice if we could just handle keeping track of these files in one place?


Let's create a new file in our 'js' folder named 'main.js' (C:\PhaserTutorial\js\main.js), and add the following code:


var MainConfig = (()=>{

	// loose reference
	let config = this;

	// path to our scene scripts
	let scene_path = "js/scenes/";

	// class/file names of all our scenes
	config.scene_names = [
		'InitScene',
		'PreloaderScene',
		'GameScene'
	];

	// This will be called when all our scene files have loaded and we are ready to start the Phaser game.
	function startGame() {

		config.game = new Phaser.Game({

			width: 860,
			height: 640,
			type: Phaser.AUTO,	// Render mode, could also be Phaser.CANVAS or Phaser.WEBGL
			scene: config.scene_classes // the code below will set this for us

		});
	}


	//---------- You shouldn't need to edit anything below here ----------\\

	// this will store references to our scene classes as they are loaded.
	config.scene_classes = [];

	// get the body tag in the HTML document
	let body = document.getElementsByTagName('body')[0];

	// keep track of number of loaded scenes
	let scenes_loaded = 0;

	// Loads a scene file by adding a script tag for it in our page HTML
	function loadScene(scene_name) {

		let script_tag = document.createElement('script');
		script_tag.src = scene_path + scene_name + ".js";

		// wait for the scene file to load, then check if we have loaded them all
		script_tag.addEventListener('load', ()=>{
			scenes_loaded++;
			eval("config.scene_classes.push("+scene_name+")");

			// looks like we can start the game!
			if (scenes_loaded === config.scene_names.length) startGame();
		});

		body.appendChild(script_tag);
	}

	// start loading all of our scene files
	config.scene_names.forEach((scene_name)=> {
		loadScene(scene_name);
	});

	return this;
})();

Next, edit your 'index.html' file so it knows to load your main.js file.


		<!-- This is where we load our scripts -->
		<script src="js/phaser.js"></script>
		<script src="js/main.js"></script>

This script gives you a handy array of any scenes your game will use. In this example, we'll be adding scenes called 'InitScene', 'PreloaderScene' and 'GameScene'. All of these scenes will be extensions of the Phaser.scene class.


InitScene is a very basic scene that preloads the absolute minimum assets we will need to render our proper preloader.


PreloadScene is where we will load everything else and render a proper preloader. It's also where we'll make sure sound is enabled in our game.


GameScene will be the class that controls our actual game. In a real game, you might have a TitleScene and OptionsScene, and multiple variations of GameScene as well, but we won't be doing anything that polished in this guide.


As you may have noticed in the 'main.js' file, we will be storing our scenes in "js/scenes/". You will need to add the 'scenes' directory in your js folder, then create a file for each scene, using the exact scene name as it's filename, like so:


C:\PhaserTutorial\htdocs\js\scenes\InitScene.js
C:\PhaserTutorial\htdocs\js\scenes\PreloaderScene.js
C:\PhaserTutorial\htdocs\js\scenes\GameScene.js

Add the following code to your files:


InitScene.js

class InitScene extends Phaser.Scene {

    init()
    {
    	console.log('InitScene Started');
    }

	constructor ()
    {
    	// Sets the string name we can use to access this scene from other scenes
		super({key:'InitScene'});
    }

	preload()
	{
		// This is where you would preload the elements you need to render the preloader scene
	}

	create()
	{
		console.log('InitScene Created');

		// Once the preload phase is done, we can switch to our preloader scene
		this.scene.start('PreloaderScene');
	}

}


PreloaderScene.js

class PreloaderScene extends Phaser.Scene {

	constructor ()
    {
    	// Sets the string name we can use to access this scene from other scenes
		super({key:'PreloaderScene'});
    }

    init()
    {
    	console.log('PreloaderScene Started');
    }

	preload()
	{
		// each of these events can update your preloader!
		this.load.on('progress', function (progress) {
			console.log("total progress: " + (progress * 100) + "%");
		});

		this.load.on('fileprogress', function (file) {
			console.log("loading asset: " + file.src);
			console.log("file progress: " + (file.percentComplete * 100) + "%");
		});

		this.load.on('complete', function () {
			console.log("everything is loaded!");
		});

		// Load your assets here
	}

	create()
	{
		console.log('PreloaderScene Created');

		// When our bulk assets have been preloaded, we can start our main game scene
		this.scene.start('GameScene');
	}

}


GameScene.js

class GameScene extends Phaser.Scene {

	constructor ()
    {
    	// Sets the string name we can use to access this scene from other scenes
		super({key:'GameScene'});
    }

    init()
    {
    	console.log('GameScene Started');
    }

	create()
	{
		console.log('GameScene Created');

  		// You could set a background color here...
  		this.cameras.main.setBackgroundColor(0x00AAFF);
  
  		// add whatever else you need to the scene here
  	}
  
  	update()
  	{
  		// This is your main game loop. Code in here runs on every frame tick.
  	}
  
  }


If you test your project now, you should see an 860x640 blue box. It's not fancy, but you officially have a working Phaser app.


Okay, but how does this all actually work?


Let's look at our new scripts.


main.js


This file is kind of our staging area for our Phaser game. It loads all of our scene files so we only have to update the config.scene_name array whenever we add new scenes.


This file is also responsible for instantiating our Phaser game. In the new Phaser.Game(...) call, we pass any configuration options our game needs.


We are making our game 860px x 640px, and letting Phaser automatically determine the best rendering mode to use on the user's end machine.


When the Phaser game starts, the first scene in the config.scene_name array is started by default. In this case, InitScene.


The way this script is set up, it creates a global MainConfig object. You could access the main Phaser.Game object from anywhere by using MainConfig.game.


This file is a handy place to put any other data you would like to be globally available.


InitScene.js


This is our first Phaser scene, and it is a proper ES6 subclass of Phaser.Scene (as are all of our scene classes).


The first thing to note is that we call the parent class constructor to pass along a key. This is a string that Phaser uses to label each scene internally, and you should have this in all of your scene classes. Without it, you won't be able to switch between scenes, and your game will likely crash.


The 'init' method is the first thing that's called when this scene is started. It's a good place to initialize any data you may need later in the class.


The 'preload' method gets called next. In this method, you can tell the scene what assets to load before the scene is actually created. In this scene we would load any artwork we want our PreloaderScene to use.


The 'create' method gets called when everything has been loaded and the scene is ready for action. Because this scene won't be doing anything after our preloader art is loaded, it simply tells Phaser to go to the next scene...


So, let's go ahead and tell it to load our preloader images. You can grab them here:


ng-simple-preloader-bg.png

ng-simple-play-btn.png


Save these files to your 'lib' folder (C:\PhaserTutorial\htdocs\lib\).


Update InitScene's 'preload' function:


	preload()
	{
		// Load the images for our preloader
		this.load.image('preloader_background', 'lib/ng-simple-preloader-bg.png');
		this.load.image('preloader_button', 'lib/ng-simple-play-btn.png');
	}

Once these are loaded, we can grab them using the 'preloader_background' and 'preloader_button' labels we have set.


And now we're done with this file!


PreloaderScene.js


This is the scene where we'll load the rest of our game assets and show the user a nicer preloader. As you can see, this scene has all the same methods InitScene.js had, but does a few more things within the 'preload' method.


During the preload phase of a scene, the load object (we'll get into that more later) can fire events letting us know what it is currently loading, and how far along we are.


The 'progress' event will give is a number between 0 and 1, indicating how far along the overall preload phase has gotten.


The 'fileprogress' event will update us on how far each individual file has gotten along, returning one of Phaser's file objects. This object has a bunch of properties, but you'll be most interested in file.src (the filename) and file.percentComplete (a number between 0 and 1 indicating how much has downloaded).


The 'complete' event will fire when all your files have been loaded, but before the 'create' method is called.


We'll use these events later to make a nice preloader.


First, we need a large enough file to actually see our preloader. Go ahead and download this familiar song:



Save the song to your 'lib' folder (C:\PhaserTutorial\htdocs\lib\306544_Four_Brave_Champions__FULL.mp3).


Update the preload function in PreloaderScene.js to:


	preload()
	{
		// set our background color
		this.cameras.main.setBackgroundColor(0x42484E);


		// add our preloader image and center it on the camera
		this.bg_image = this.add.image(this.cameras.main.centerX, this.cameras.main.centerY, 'preloader_background');


		// note where our dynamic/interactive stuff should go
		this.interact_point = {x: this.bg_image.x, y: this.bg_image.y + (this.bg_image.height/2) - 42};


		let loadbar_width = 224;
		let loadbar_height = 8;
		let loadbar_pad = 4;
		let loadbar_x = this.interact_point.x - loadbar_width/2;
		let loadbar_y = this.interact_point.y - loadbar_height/2;


		// create a  bg for the loading bar (doing this dynamically saves us having to add more image files!)
		let loadbar_bg = this.add.graphics();
		// color and alpha
		loadbar_bg.fillStyle(0x525961, 1);
		// position and dimensions (not position here does NOT set origin point, thats why we're using 0,0)
		loadbar_bg.fillRect(0, 0, loadbar_width + loadbar_pad*2, loadbar_height + loadbar_pad*2);
		// move the bar where we want it
		loadbar_bg.x = loadbar_x - loadbar_pad;
		loadbar_bg.y = loadbar_y - loadbar_pad;


		// do the same for the loading bar itself
		let loadbar_bar = this.add.graphics();
		loadbar_bar.fillStyle(0xD2D8E3, 1);
		loadbar_bar.fillRect(0, 0, loadbar_width, loadbar_height);
		loadbar_bar.x = loadbar_x;
		loadbar_bar.y = loadbar_y;


		// start the bar at 0% width
		loadbar_bar.scaleX = 0;


		// update the progress bar width with each progress event
		this.load.on('progress', function (progress) {
			loadbar_bar.scaleX = progress;
		});


		// remove the progress bar when everything has loaded
		this.load.on('complete', function () {
			loadbar_bar.destroy();
			loadbar_bg.destroy();
		});


		// Load your assets here
    	this.load.audio('castle_crashers_theme', 'lib/306544_Four_Brave_Champions__FULL.mp3');
	}

Now when you test the game, you'll see our preloader just before GameScene is started.


Now we can make our game play the Castle Crashers theme!


GameScene.js


This part is simple. Just update the 'create' method to:


	create()
	{
		// add the Castle Crasher theme to the scene
		this.cc_theme = this.sound.add('castle_crashers_theme');
		// and play it on a loop
		this.cc_theme.play({loop: true});


		// Lets change the background again too so you can see something happened
		this.cameras.main.setBackgroundColor(0x00AAFF);
	}

Now when you test your game, you'll see the preloader, then our blue screen and hear the Castle Crashers theme! Or wait, will you?


Most modern browsers actually won't let your game play sound without some sort of user interaction. So it would appear we need to make the user click on our game before we need to play any sounds. The easiest place to do that is back in our preloader.


So, let's edit PreloaderScene.js again. This time, replace the 'create' method so adds a play button instead of auto-starting the GameScene.


	create()
	{
		let play_btn = this.add.image(this.interact_point.x, this.interact_point.y, 'preloader_button');
		// make the button interactive
		play_btn.setInteractive();
		// add some alpha for the default state
		play_btn.alpha = 0.9;


		// remove alpha on hover
		play_btn.on('pointerover', ()=>{
			play_btn.alpha = 1;
		});


		// add alpha on roll out
		play_btn.on('pointerout', ()=>{
			play_btn.alpha = 0.9;
		});


		// start the GameScene when the button is clicked. Bonus - this will enable audo playback as well!
		play_btn.on('pointerup', ()=>{
			this.scene.start('GameScene');
		});
	}

Now when you test your game, you should see the preloader, have to click 'PLAY', and THEN you'll hear the music!


This is all great, but when do we actually make a game?


I realize we've done a lot of stuff here and haven't really even made anything resembling a game now, but there's a pretty good reason for that.


First of all, you now know how to create and switch scenes. You should be able to import images, sound files and add them to your screen. You should be able to make a basic button, and loop music. That's a lot of stuff you'll be needing to make any sort of game!


Second, you know how to make a pretty preloader so your users won't think your game is broken because it's a just blank screen for several minutes.


Third, you could save what we have done so far and use it as a boiler plate for any future projects and not have to build a preloader or scene manager from scratch ever again!


You're welcome.


Thanks, I Guess... But how about the game part?


Okay, you've made it this far, so I guess we can bang out super basic game.


Let's grab some more assets and put them in our 'lib' folder (C:\PhaserTutorial\htdocs\lib\).


cat-head.png

cheezeburger.png


Open up PreloaderScene.js again, and add these lines to the end of the preload function:


    	this.load.image('cat_head', 'lib/cat-head.png');
    	this.load.image('cheezeburger', 'lib/cheezeburger.png');

And finally, replace all the code in GameScene.js with this:


class GameScene extends Phaser.Scene {

	constructor ()
    {
		super({key:'GameScene'});
    }

	create()
	{
		// add the Castle Crasher theme to the scene
		this.cc_theme = this.sound.add('castle_crashers_theme');
		// and play it on a loop
		this.cc_theme.play({loop: true});

		// Lets change the background again too so you can see something happened
		this.cameras.main.setBackgroundColor(0x00AAFF);

		// add the player sprite
		this.player = this.add.image(this.cameras.main.centerX, this.cameras.main.centerY, 'cat_head');

		// add the cheeseburger sprite somewhere off-screen
		this.cheezeburger = this.add.image(-9999, 0, 'cheezeburger');

		// set a cool time for cheezeburger respawn
		this.cheezeburger_cooldown = 80;

		// set the initial cool time for the cheezeburger
		this.cheezeburger_cooltime = this.cheezeburger_cooldown;

		// set how fast the player can move
		this.player_speed = 20;

		// handy container for our keyboard keys
		this.controls = {
			up: 	this.input.keyboard.addKey('UP'), // up arrow
			down: 	this.input.keyboard.addKey('DOWN'), // down arrow
			left: 	this.input.keyboard.addKey('LEFT'), // left arrow
			right: 	this.input.keyboard.addKey('RIGHT') // right arrow
		};

		// this is our score counter
		this.score = 0;

		// and this is our score text
		this.scoretext = this.add.text(5, 5, "Score: "+this.score);
		this.scoretext.setStyle({
			fontSize: "26px",
			fontFamily: "Arial Black"
		});
	}

	update()
	{
		// up and down movement
		if (this.controls.up.isDown) {
			this.player.y -= this.player_speed;
			// screen wrap
			if (this.player.y < 0) this.player.y = this.cameras.main.height;
		} else if (this.controls.down.isDown) {
			this.player.y += this.player_speed;
			// screen wrap
			if (this.player.y > this.cameras.main.height) this.player.y = 0;
		}

		// up and down movement
		if (this.controls.left.isDown) {
			this.player.x -= this.player_speed;
			// screen wrap
			if (this.player.x < 0) this.player.x = this.cameras.main.width;
		} else if (this.controls.right.isDown) {
			this.player.x += this.player_speed;
			// screen wrap
			if (this.player.x > this.cameras.main.width) this.player.x = 0;
		}

		// check our cheezeburger cool time
		if (this.cheezeburger_cooltime > 0) {
			this.cheezeburger_cooltime--;
			if (this.cheezeburger_cooltime < 1) {
				this.spawnBurger();
			}
		}

		// check for collision with cheezeburger
		// (note, we aren't using physics here, just checking distance with pythagorean theorem)
		let cheezeburger_distance = Math.sqrt(
			Math.pow(this.player.x - this.cheezeburger.x, 2) + Math.pow(this.player.y - this.cheezeburger.y, 2)
		);

		if (cheezeburger_distance < 60) this.eatBurger();
	}

	//------------------- custom methods -------------------\\

	// handles "spawning" the burger
	spawnBurger()
	{
		// put the burger somweher random within camera bounds
		this.cheezeburger.x = Math.round(Math.random() * this.cameras.main.width);
		this.cheezeburger.y = Math.round(Math.random() * this.cameras.main.height);
	}

	// handles "eating" the burger
	eatBurger()
	{
		// update the score
		this.score++;

		// move the burger off-screen
		this.cheezeburger.x = -9999;

		// reset the cool time
		this.cheezeburger_cooltime = this.cheezeburger_cooldown;

		this.scoretext.setText("Score: "+this.score);
	}

}

In this version of the file, we're adding all our assets to the scene in the 'create' function, and setting a bunch of instance values that other functions will have access to. You are also adding text for the first time here!


In our 'update' function, we're handling all the player movement, collision detection and burger spawn time.


At the end of the file, we've added some custom methods that get called by our game loop. We could have put this code right in the 'update' function, but I wanted to illustrate how you can expand the core abilities of the scene object and organize your code a bit more.


If you test the game now, you should be able to control the cat and have him haz cheezeburgers.


This is an extremely basic game, but you should have a pretty good grasp on how to start your own Phaser project by now.


As you dive a bit deeper, you'll earn how to use the physics models to handle proper collisions and things like that. You'll learn how to make sprite sheets (or better yet, texture atlases) so you aren't loading as many image files. You'll learn how to use Sprites and animate them, and so much more.


Be sure to check out all the great resources at http://phaser.io/learn and follow me on Newgrounds for any further Phaser stuff I may post about!


Tags:

11

Comments (9)

gonna be digging through this later, god bless ya PG

Thank you so much for this!

Thanks for writing this up, PG, Super helpful as always. I noticed two things as I was following along. First off, you wrote that the scene files should look like this in their directories:

C:\PhaserTutorial\htdocs\js\InitScene.js
C:\PhaserTutorial\htdocs\js\PreloaderScene.js
C:\PhaserTutorial\htdocs\js\GameScene.js

But if I'm following along correctly (which I believe I am) , they should actually be:

C:\PhaserTutorial\htdocs\js\scenes\InitScene.js
C:\PhaserTutorial\htdocs\js\scenes\PreloaderScene.js
C:\PhaserTutorial\htdocs\js\scenes\GameScene.js

So it looks like you just left out the scenes folder there.

The other thing is that for a while I couldn't get past the black screen even after setting up the InitScene, PreloaderScene, and GameScene scripts. Finally on a whim I added script src="js/main.js" after the phaser script src in the body of the index file and that got everything rolling. I'm not sure if you actually did say to do this in your post or if I just totally overlooked it, but yeah, that was a little hang up I had.

Good catch, I tottaly messed those bits up. Should be all up to snuff now.

this sounds awesome.. what about completely code-illiterate goons like me?

Get literate, duh.

Really cool! Thanks for posting this!

This comment pretty much confirms HTML5 Shark game right?

@PsychoGoldfish haha well maybe a baby shark game :D

Oh, I somehow deleted my old comment. Hm, I should drink less beers. :/

I disagree

I'm an idiot, didn't realise you needed to highlight the host code to make it work

I "kinda" fixed it, but for some reason not only does it show black boxes where the Newgrounds logo and play button are supposed to be, the one I built doesn't want to play at all. Is there something I'm missing this time?