Advertisement
Games

Learn CreateJS by Building an HTML5 Pong Game

by

The web moves fast - so fast that our original EaselJS tutorial is already out of date! In this tutorial, you'll learn how to use the newest CreateJS suite by creating a simple Pong clone.


Final Result Preview

Let's take a look at the final result we will be working towards:

The PONG game

Click to play

This tutorial is based on Carlos Yanez's Create a Pong Game in HTML5 With EaselJS, which in turn built on his Getting Started With EaselJS guide. The graphics and sound effects are all taken from the former tutorial.


Step 1: Create index.html

This will be our main index.html file:

<!DOCTYPE html>
<html>
	<head>
		<title>Pong</title>
		
		<style>/* Removes Mobile Highlight */ *{-webkit-tap-highlight-color: rgba(0, 0, 0, 0);}</style>

		<script src="http://code.createjs.com/easeljs-0.4.2.min.js"></script>
		<script src="http://code.createjs.com/tweenjs-0.2.0.min.js"></script>
		<script src="http://code.createjs.com/soundjs-0.2.0.min.js"></script>
		<script src="http://code.createjs.com/preloadjs-0.1.0.min.js"></script>
		<script src="http://code.createjs.com/movieclip-0.4.1.min.js"></script>
		<script src="assets/soundjs.flashplugin-0.2.0.min.js"></script>
		<script src="Main.js"></script>
		
	</head>
	<body onload="Main();">
		<canvas id="PongStage" width="480" height="320"></canvas>
	</body>
</html>

As you can see, it’s pretty short and consists mainly of loading the CreateJS libraries.

Since the release of CreateJS (which basically bundles all the separate EaselJS libraries) we no longer have to download the JS files and host them on our website; the files are now placed in a CDN (Content Delivery Network) which allows us to load these files remotely as quickly as possible.

Let’s review the code:

<style>/* Removes Mobile Highlight */ *{-webkit-tap-highlight-color: rgba(0, 0, 0, 0);}</style>

This line removes the mobile highlight which may appear when you trying to play the game on mobile. (The mobile highlight causes the canvas object to get highlighted and thus ignore your finger movements.)

Next up, we have the loading of the CreateJS libraries:

<script src="http://code.createjs.com/easeljs-0.4.2.min.js"></script>
		<script src="http://code.createjs.com/tweenjs-0.2.0.min.js"></script>
		<script src="http://code.createjs.com/soundjs-0.2.0.min.js"></script>
		<script src="http://code.createjs.com/preloadjs-0.1.0.min.js"></script>
		<script src="http://code.createjs.com/movieclip-0.4.1.min.js"></script>

This code loads the JS files from the CreateJS CDN and it basically allows us to use any of the CreateJS functions in our code

Next, we will load the SoundJS Flash plugin, which provides sound support for browsers that don’t support HTML5 Audio. This is done by using a SWF (a Flash object) to load the sounds.

<script src="assets/soundjs.flashplugin-0.2.0.min.js"></script>

In this case we will not use the CDN; instead, we’ll download the SoundJS library from http://createjs.com/#!/SoundJS/download and place the soundjs.flashplugin-0.2.0.min.js and FlashAudioPlugin.swf files in a local folder named assets.

Last among the JS files, we’ll load the Main.js file which will contain all the code to our game:

<script src="Main.js"></script>

Finally, let’s place a Canvas object on our stage.

<body onload="Main();">
		<canvas id="PongStage" width="480" height="320"></canvas>
	</body>

Now we can start working on the game code.


Step 2: The Variables

Our game code will be inside a file named Main.js, so create and save this now.

First of all, let’s define variables for all the graphic objects in the game:

var canvas; //Will be linked to the canvas in our index.html page
var stage; //Is the equivalent of stage in AS3; we'll add "children" to it

// Graphics
//[Background]

var bg; //The background graphic

//[Title View]
 

var main; //The Main Background
var startB; //The Start button in the main menu
var creditsB; //The credits button in the main menu

//[Credits]


var credits; //The Credits screen

//[Game View]


var player; //The player paddle graphic
var ball; //The ball graphic
var cpu; //The CPU paddle
var win; //The winning popup
var lose; //The losing popup

I’ve added a comment for each variable so that you’ll know what we’ll be loading in that variable

Next up, the scores:

//[Score]

var playerScore; //The main player score
var cpuScore; //The CPU score
var cpuSpeed=6; //The speed of the CPU paddle; the faster it is the harder the game is

We’ll, need variables for the speed of the ball:

// Variables

var xSpeed = 5;
var ySpeed = 5;

You can change these values to whatever you want, if you’d like to make the game easier or harder.

If you're a Flash developer, you know that Flash’s onEnterFrame is very useful when creating games, as there are things that need to happen in every given frame. (If you're not familiar with this idea, check out this article on the Game Loop.)

We have an equivalent for onEnterFrame in CreateJS, and that is the ticker object, which can run code every fraction of a second. Let’s create the variable that will link to it:

var tkr = new Object;

Next we have the preloader, which will use the new PreloadJS methods.

//preloader
var preloader;
var manifest;
var totalLoaded = 0;
  • preloader - will contain the PreloadJS object.
  • manifest - will hold the list of files we need to load.
  • totalLoaded - this variable will hold the number of files already loaded.

Last but not least in our list of variables, we have TitleView, which will hold several graphics within in order to display them together (like a Flash DisplayObjectContainer).

var TitleView = new Container();

Let’s move on to the Main function...


Step 3: The Main() Function

This function is the first function that runs after all the JS files from the index.html are loaded. But what's calling this function?

Well, remember this line from the index.html file?

<body onload="Main();">

This code snippet states that once the HTML (and JS libraries) are loaded, the Main function should run.

Let's review it:

function Main()
{
	/* Link Canvas */
	
	canvas = document.getElementById('PongStage');
  	stage = new Stage(canvas);
  		
  	stage.mouseEventsEnabled = true;
  	
  	
	/* Set The Flash Plugin for browsers that don't support SoundJS */
  	SoundJS.FlashPlugin.BASE_PATH = "assets/";
    if (!SoundJS.checkPlugin(true)) {
      alert("Error!");
      return;
    }

  	manifest = [
				{src:"bg.png", id:"bg"},
				{src:"main.png", id:"main"},
				{src:"startB.png", id:"startB"},
				{src:"creditsB.png", id:"creditsB"},
				{src:"credits.png", id:"credits"},
				{src:"paddle.png", id:"cpu"},
				{src:"paddle.png", id:"player"},
				{src:"ball.png", id:"ball"},
				{src:"win.png", id:"win"},
				{src:"lose.png", id:"lose"},
				{src:"playerScore.mp3|playerScore.ogg", id:"playerScore"},
				{src:"enemyScore.mp3|enemyScore.ogg", id:"enemyScore"},
				{src:"hit.mp3|hit.ogg", id:"hit"},
				{src:"wall.mp3|wall.ogg", id:"wall"}
			];



  	preloader = new PreloadJS();
  	preloader.installPlugin(SoundJS);
    preloader.onProgress = handleProgress;
    preloader.onComplete = handleComplete;
    preloader.onFileLoad = handleFileLoad;
    preloader.loadManifest(manifest);

	/* Ticker */
	
	Ticker.setFPS(30);
	Ticker.addListener(stage);
}

Let’s break down each part:

  	canvas = document.getElementById('PongStage');
  	stage = new Stage(canvas);
  		
  	stage.mouseEventsEnabled = true;

Here we link the PongStage Canvas object from the index.html file to the canvas variable, and then create a Stage object from that canvas. (The stage will allow us to place objects on it.)

mouseEventsEnabled enables us to use mouse events, so we can detect mouse movements and clicks.

/* Set The Flash Plugin for browsers that don't support SoundJS */
  	SoundJS.FlashPlugin.BASE_PATH = "assets/";
    if (!SoundJS.checkPlugin(true)) {
      alert("Error!");
      return;
    }

Here we configure where the Flash sound plugin resides for those browsers in which HTML5 Audio is not supported

	manifest = [
				{src:"bg.png", id:"bg"},
				{src:"main.png", id:"main"},
				{src:"startB.png", id:"startB"},
				{src:"creditsB.png", id:"creditsB"},
				{src:"credits.png", id:"credits"},
				{src:"paddle.png", id:"cpu"},
				{src:"paddle.png", id:"player"},
				{src:"ball.png", id:"ball"},
				{src:"win.png", id:"win"},
				{src:"lose.png", id:"lose"},
				{src:"playerScore.mp3|playerScore.ogg", id:"playerScore"},
				{src:"enemyScore.mp3|enemyScore.ogg", id:"enemyScore"},
				{src:"hit.mp3|hit.ogg", id:"hit"},
				{src:"wall.mp3|wall.ogg", id:"wall"}
			];

In the manifest variable we place an array of files we want to load (and provide a unique ID for each one). Each sound has two formats - MP3 and OGG - because different browsers are (in)compatible with different formats.

  	preloader = new PreloadJS();
  	preloader.installPlugin(SoundJS);
    preloader.onProgress = handleProgress;
    preloader.onComplete = handleComplete;
    preloader.onFileLoad = handleFileLoad;
    preloader.loadManifest(manifest);

Here we configure the preloader object using PreloadJS. PreloadJS is a new addition to the CreateJS libraries and quite a useful one.

We create a new PreloadJS object and place it in the preloader variable, then assign a method to each event (onProgress, onComplete, onFileLoad). Finally we use the preloader to load the manifest we created earlier.

Ticker.setFPS(30);
	Ticker.addListener(stage);

Here we add the Ticker object to the stage and set the frame rate to 30 FPS; we’ll use it later in the game for the enterFrame functionality.


Step 4: Creating the Preloader Functions

function handleProgress(event)
{
	//use event.loaded to get the percentage of the loading
}

function handleComplete(event) {
         //triggered when all loading is complete
}

function handleFileLoad(event) {
         //triggered when an individual file completes loading
            
         switch(event.type)
         {
         	case PreloadJS.IMAGE:
         	//image loaded
         	 var img = new Image();
              img.src = event.src;
              img.onload = handleLoadComplete;
              window[event.id] = new Bitmap(img);
         	break;

         	case PreloadJS.SOUND:
         	//sound loaded
         	handleLoadComplete();
         	break;
         }
}

Let’s review the functions:

  • handleProgress - In this function you’ll be able to follow the percentage of the loading progress using this parameter: event.loaded. You could use this to create for example a progress bar.
  • handleComplete - This function is called once all the files have been loaded (in case you want to place something there).
  • handleFileLoad - Since we load two types of files - images and sounds - we have this function that will handle each one separately. If it’s an image, we create a bitmap image and place it in a variable (whose name is the same as the ID of the loaded image) and then call the handleLoadComplete function (which we'll write next); if it’s a sound then we just call the handleLoadComplete immediately.

Now let’s discuss the handleLoadComplete function I just mentioned:

 function handleLoadComplete(event) 
 {

	totalLoaded++;
	
	if(manifest.length==totalLoaded)
	{
		addTitleView();
	}
 }

It’s a pretty straightforward function; we increase the totalLoaded variable (that holds the number of assets loaded so far) and then we check if the number of items in our manifest is the same as the number of loaded assets, and if so, go to the Main Menu screen.


Step 5: Creating the Main Menu

The Main Menu
function addTitleView()
{
	//console.log("Add Title View");
	startB.x = 240 - 31.5;
	startB.y = 160;
	startB.name = 'startB';
	
	creditsB.x = 241 - 42;
	creditsB.y = 200;
	
	TitleView.addChild(main, startB, creditsB);
	stage.addChild(bg, TitleView);
	stage.update();
	
	// Button Listeners
	
	startB.onPress = tweenTitleView;
	creditsB.onPress = showCredits;

Nothing special here. We place the images of the Background, Start Button and Credits Button on the stage and link onPress event handlers to the Start and Credits buttons.

Here are the functions that display and remove the credits screen and the tweenTitleView which starts the game:

function showCredits()
{
	// Show Credits
		
	credits.x = 480;
		
	stage.addChild(credits);
	stage.update();
	Tween.get(credits).to({x:0}, 300);
	credits.onPress = hideCredits;
}

// Hide Credits

function hideCredits(e)
{
	Tween.get(credits).to({x:480}, 300).call(rmvCredits);
}

// Remove Credits

function rmvCredits()
{
	stage.removeChild(credits);
}

// Tween Title View

function tweenTitleView()
{		
	// Start Game
		
	Tween.get(TitleView).to({y:-320}, 300).call(addGameView);
}

Step 6: The Game Code

The PONG game

We’ve reached the main part of this tutorial which is the code of the game itself.

First of all, we need to add all the required assets to the stage, so we do that in the addGameView function:

function addGameView()
{
	// Destroy Menu & Credits screen
	
	stage.removeChild(TitleView);
	TitleView = null;
	credits = null;
	
	// Add Game View
	
	player.x = 2;
	player.y = 160 - 37.5;
	cpu.x = 480 - 25;
	cpu.y = 160 - 37.5;
	ball.x = 240 - 15;
	ball.y = 160 - 15;
	
	// Score
	
	playerScore = new Text('0', 'bold 20px Arial', '#A3FF24');
	playerScore.x = 211;
	playerScore.y = 20;
	
	cpuScore = new Text('0', 'bold 20px Arial', '#A3FF24');
	cpuScore.x = 262;
	cpuScore.y = 20;
	
	stage.addChild(playerScore, cpuScore, player, cpu, ball);
	stage.update();
	
	// Start Listener 
	
	bg.onPress = startGame;
}

Again, a pretty straightforward function that places the objects on the screen and adds a mouseEvent to the background image, so that when the user clicks it the game will start (we will call the startGame function).

Let’s review the startGame function:

function startGame(e)
{
	bg.onPress = null;
	stage.onMouseMove = movePaddle;
	
	Ticker.addListener(tkr, false);
	tkr.tick = update;
}

Here, as you can see, in addition to adding an onMouseMove event that will move our paddle. We add the tick event, which will call the update function in each frame.

Let’s review the movePaddle and reset functions:

function movePaddle(e)
{
	// Mouse Movement
	player.y = e.stageY;
}

/* Reset */

function reset()
{
	ball.x = 240 - 15;
	ball.y = 160 - 15;
	player.y = 160 - 37.5;
	cpu.y = 160 - 37.5;
	
	stage.onMouseMove = null;
	Ticker.removeListener(tkr);
	bg.onPress = startGame;
}

In movePaddle, we basically place the user’s paddle at the mouse's y-coordinate.

In reset, we do something similar to addGameView, except here we don’t add any graphic elements since they are already on the screen.

Using the alert function we’ll display the winning and losing popup:

function alert(e)
{
	Ticker.removeListener(tkr);
	stage.onMouseMove = null;
	bg.onPress = null
	
	if(e == 'win')
	{
		win.x = 140;
		win.y = -90;
	
		stage.addChild(win);
		Tween.get(win).to({y: 115}, 300);
	}
	else
	{
		lose.x = 140;
		lose.y = -90;
	
		stage.addChild(lose);
		Tween.get(lose).to({y: 115}, 300);
	}
}

Step 7: The Game Loop

Now, for the last part of our tutorial we’ll work on the update function (which occurs in every frame of the game - similar to Flash’s onEnterFrame):

function update()
{
	// Ball Movement 

	ball.x = ball.x + xSpeed;
	ball.y = ball.y + ySpeed;
	
	// Cpu Movement
	
	if(cpu.y < ball.y) {
		cpu.y = cpu.y + 4;
	}
	else if(cpu.y > ball.y) {
		cpu.y = cpu.y - 4;
	}
	
	// Wall Collision 

	if((ball.y) < 0) { ySpeed = -ySpeed; SoundJS.play('wall'); };//Up
	if((ball.y + (30)) > 320) { ySpeed = -ySpeed; SoundJS.play('wall');};//down
	
	/* CPU Score */
	
	if((ball.x) < 0)
	{
		xSpeed = -xSpeed;
		cpuScore.text = parseInt(cpuScore.text + 1);
		reset();
		SoundJS.play('enemyScore');
	}
	
	/* Player Score */
	
	if((ball.x + (30)) > 480)
	{
		xSpeed = -xSpeed;
		playerScore.text = parseInt(playerScore.text + 1);
		reset();
		SoundJS.play('playerScore');
	}
	
	/* Cpu collision */
	
	if(ball.x + 30 > cpu.x && ball.x + 30 < cpu.x + 22 && ball.y >= cpu.y && ball.y < cpu.y + 75)
	{
		xSpeed *= -1;
		SoundJS.play('hit');
	}
	
	/* Player collision */
	
	if(ball.x <= player.x + 22 && ball.x > player.x && ball.y >= player.y && ball.y < player.y + 75)
	{
		xSpeed *= -1;
		SoundJS.play('hit');
	}
	
	/* Stop Paddle from going out of canvas */
	
	if(player.y >= 249)
	{
		player.y = 249;
	}
	
	/* Check for Win */
	
	if(playerScore.text == '10')
	{
		alert('win');
	}
	
	/* Check for Game Over */
	
	if(cpuScore.text == '10')
	{
		alert('lose');
	}
}

Looks scary, doesn’t it? Don’t worry, we’ll review each part and discuss it.

	// Ball Movement 

	ball.x = ball.x + xSpeed;
	ball.y = ball.y + ySpeed;

In each frame, the ball will move according to its x and y speed values

// Cpu Movement
	
	if((cpu.y+32) < (ball.y-14)) {
		cpu.y = cpu.y + cpuSpeed;
	}
	else if((cpu.y+32) > (ball.y+14)) {
		cpu.y = cpu.y - cpuSpeed;
	}

Here we have the basic AI of the computer, in which the computer’s paddle simply follows the ball without any special logic. We just compare the location of the center of the paddle (which is why we add 32 pixels to the cpu Y value) to the location of the ball, with a small offset, and move the paddle up or down as necessary.

if((ball.y) < 0) { //top
	ySpeed = -ySpeed; 
	SoundJS.play('wall'); 
};
if((ball.y + (30)) > 320) { //bottom
	ySpeed = -ySpeed; 
	SoundJS.play('wall');
};

If the ball hits the top border or the bottom border of the screen, the ball changes direction and we play the Wall Hit sound.

 	/* CPU Score */
 	if((ball.x) < 0)
 	{
 		xSpeed = -xSpeed;
 		cpuScore.text = parseInt(cpuScore.text + 1);
 		reset();
 		SoundJS.play('enemyScore');
 	}
 	/* Player Score */
  	if((ball.x + (30)) > 480)
 	{
 		xSpeed = -xSpeed;
 		playerScore.text = parseInt(playerScore.text + 1);
 		reset();
 		SoundJS.play('playerScore');
 	}

The score login is simple: if the ball passes the left or right borders it increases the score of the player or CPU respectively, plays a sound, and resets the location of the objects using the reset function we’ve discussed earlier.

 
/* CPU collision */
if(ball.x + 30 > cpu.x && ball.x + 30 < cpu.x + 22 && ball.y >= cpu.y && ball.y < cpu.y + 75)
{
	xSpeed *= -1;
	SoundJS.play('hit');
}
/* Player collision */
if(ball.x <= player.x + 22 && ball.x > player.x && ball.y >= player.y && ball.y < player.y + 75) 	
{ 		
 	xSpeed *= -1;
 	SoundJS.play('hit');
 }

Here we deal with collisions of the ball with the paddles; every time the ball hits one of the paddles, the ball changes direction and a sound is played

 	if(player.y >= 249)
 	{
 		player.y = 249;
 	}

If the player's paddle goes out of bounds, we place it back within the bounds.

 /* Check for Win */
 if(playerScore.text == '10')
 {
 	alert('win'); 	
} 	 	
/* Check for Game Over */
if(cpuScore.text == '10')
{
 	alert('lose'); 	
}

In this snippet, we check whether either of the players’ score has reached 10 points, and if so we display the winning or losing popup to the player (according to his winning status).


Conclusion

That’s it, you’ve created an entire pong game using CreateJS. Thank you for taking the time to read this tutorial.

Related Posts