Advertisement

Create a Pong Game in HTML5 With EaselJS

by

In this tutorial, we'll create a clone of the classic game, Pong, in HTML5, using the EaselJS library. The game will have multiple screens, sound effects, and a (very simple) AI opponent.

Step 1: Brief Overview

Using pre-made graphics we will code an entertaining Pong game in HTML5 using the EaselJS library, which provides a Flash-like interface for the HTML5 canvas. For an introduction to EaselJS, see this Activetuts+ article.

The player will be able to control a paddle using the mouse and play against the computer-controller opponent to get points.


Step 2: Interface

A simple interface with a neo-futuristic style will be used; this involves multiple shapes, buttons, bitmaps and more.

The graphics required for this tutorial can be found in the attached download.


Step 3: Get EaselJS

The EaselJS library will be used to build our game, make sure you read the Getting Started tutorial if you're new to this library.

You can download the latest version of EaselJS from its official website. However, this may be incompatible with the code here, so I suggest using the version of the library that is included with the source download.


Step 4: HTML Structure

Let's prepare our HTML document. We'll start with the very basics, just a barebones outline:

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

Step 5: Hide Mobile Highlight

We must add a little bit of CSS too, to remove the default highlight that's applied when you tap on an element in a mobile browser. Without this, the mobile experience would decrease drastically.

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

Step 6: Javascript Libraries

The following code adds the necessary javascript libraries for our app to work.

 
<!DOCTYPE html> 
<html> 
	<head> 
		<title>Pong</title> 
		 
		<style>*{-webkit-tap-highlight-color: rgba(0, 0, 0, 0);}</style> 
 
		<script src="easel.js"></script> 
		<script src="Tween.js"></script> 
		<script src="sound.js"></script> 
		<script src="Main.js"></script> 
	</head> 
	<body> 
	</body> 
</html>/

As well as EaselJS, we'll also be using TweenJS (for handling screen transitions and the game loop) and SoundJS (for the sound effects).

Main.js is the file we'll use to hold our own JS code.


Step 7: Call Main Function

In the next lines we call our Main() function. This is the function that will start our application; it'll be created later in the tutorial, inside Main.js.

 
<!DOCTYPE html> 
<html> 
	<head> 
		<title>Pong</title> 
		 
		<style>*{-webkit-tap-highlight-color: rgba(0, 0, 0, 0);}</style> 
 
		<script src="easel.js"></script> 
		<script src="Tween.js"></script> 
        <script src="sound.js"></script> 
		<script src="Main.js"></script> 
 
	</head> 
	<body onload="Main();"> 
	</body> 
</html>

Step 8: Canvas Tag

An HTML5 canvas is created in this line; we assign it an ID so we can reference it later and also set its width and height.

 
<!DOCTYPE html> 
<html> 
	<head> 
		<title>Pong</title> 
		 
		<style>*{-webkit-tap-highlight-color: rgba(0, 0, 0, 0);}</style> 
 
		<script src="easel.js"></script> 
		<script src="Tween.js"></script> 
		<script src="sound.js"></script> 
		<script src="Main.js"></script> 
 
	</head> 
	<body onload="Main();"> 
		<canvas id="Pong" width="480" height="320"></canvas> 
	</body> 
</html>

Step 9: Create Main.js

Let's begin our game creation!

Open your preferred JavaScript editor (any text editor will work, but you won't have syntax highlighting) and prepare to write the code. Remember to save the file as Main.js in your project folder.


Step 10: Define Canvas

We'll start by defining all the graphic and logic variables.

The next variables represent the HTML canvas element and the stage that will be linked to it. The stage variable will behave in a similar way as the AS3 stage.

 
/* Define Canvas */ 
 
var canvas; 
var stage;

Step 11: Background

This variable stores the title background image.

 
/* Background */ 
 
var bgImg = new Image(); 
var bg;

Step 12: Title View

This is the Title View, the first interactive screen to appear in our game. These variables store its components.

 
/* Title View */ 
 
var mainImg = new Image(); 
var main; 
var startBImg = new Image(); 
var startB; 
var creditsBImg = new Image(); 
var creditsB; 
 
var TitleView = new Container();

Step 13: Credits

This view will show the credits, year and copyright of the game, these variables will be used to store it.

 
/* Credits */ 
 
var creditsViewImg = new Image(); 
var credits;

Step 14: Game View

The next variables store the individual graphics that appear in the Game View:

 
/* Game View */ 
 
var playerImg = new Image(); 
var player; 
var ballImg = new Image(); 
var ball; 
var cpuImg = new Image(); 
var cpu; 
var winImg = new Image(); 
var win; 
var loseImg = new Image(); 
var lose;

Step 15: Score

The score values will be handled by the next variables:

 
/* Score */ 
 
var playerScore; 
var cpuScore;

Step 16: Variables

These are the variables we'll use, read the comments in the code to understand what they're for:

 
var xSpeed = 5; //Horizontal speed of the ball 
var ySpeed = 5; //Vertical speed of the ball 
var gfxLoaded = 0; //used as a preloader, counts the already loaded items 
var tkr = new Object; //used as an event listener to the Ticker

Step 17: Create Sound Effects

We'll use sound effects to enhance the feeling of the game. The sounds in this example were created using the excellent free tool as3sfxr and converted to MP3 using Audacity.

The sounds required can all be found in the source download. If you want to create your own, you'll need four:

  • hit.mp3: played when the ball hits a paddle
  • playerScore.mp3: played when they player scores
  • enemyScore.mp3: played when the enemy scores
  • wall.mp3: played when the ball hits the top or bottom boundary

Step 18: Main Function

The Main() function will be the first to execute when the web page is loaded, because it is referred to in the onload attribute of the HTML document (see Step 7).

It will call the necessary functions to start the game.

 
function Main() 
{ 
	//code... 
}

Step 19: Link Canvas

This code gets the HTML canvas ID and links it to the EaselJS Stage class. This will make the stage variable behave like the stage class in AS3. Add this to Main().

 
/* Link Canvas */ 
	 
canvas = document.getElementById('Pong'); 
stage = new Stage(canvas);

Step 20: Enable Mouse Events

Mouse events are disabled by default in EaselJS to improve performance. Since we need those in the game, we add the following line. Add this to Main().

 
stage.mouseEventsEnabled = true;

Step 21: Load Sounds

We'll use SoundJS to add sounds to our game; write the following code to import the sounds we'll be using. Add this to Main().

 
/* Sound */ 
 
SoundJS.addBatch([ 
	{name:'hit', src:'hit.mp3', instances:1}, 
	{name:'playerScore', src:'playerScore.mp3', instances:1}, 
	{name:'enemyScore', src:'enemyScore.mp3', instances:1}, 
	{name:'wall', src:'wall.mp3', instances:1}]);

Step 22: Load Graphics

This code is used to preload the graphics, with the help of a function that we'll write later. It sets the Image objects we created before to point to the relevant source PNG files in our document folder.

A name is given to each, so we can detect which image is loaded later, and lastly the function that handles the loaded images is called.

Add this to Main().

 
/* Load GFX */ 
 
bgImg.src = 'bg.png'; 
bgImg.name = 'bg'; 
bgImg.onload = loadGfx; 
 
mainImg.src = 'main.png'; 
mainImg.name = 'main'; 
mainImg.onload = loadGfx; 
 
startBImg.src = 'startB.png'; 
startBImg.name = 'startB'; 
startBImg.onload = loadGfx; 
 
creditsBImg.src = 'creditsB.png'; 
creditsBImg.name = 'creditsB'; 
creditsBImg.onload = loadGfx; 
 
creditsViewImg.src = 'credits.png'; 
creditsViewImg.name = 'credits'; 
creditsViewImg.onload = loadGfx; 
 
playerImg.src = 'paddle.png'; 
playerImg.name = 'player'; 
playerImg.onload = loadGfx; 
 
ballImg.src = 'ball.png'; 
ballImg.name = 'ball'; 
ballImg.onload = loadGfx; 
 
cpuImg.src = 'paddle.png'; 
cpuImg.name = 'cpu'; 
cpuImg.onload = loadGfx; 
 
winImg.src = 'win.png'; 
winImg.name = 'win'; 
winImg.onload = loadGfx; 
 
loseImg.src = 'lose.png'; 
loseImg.name = 'lose'; 
loseImg.onload = loadGfx;

Step 23: Set Ticker

The Ticker class provides a centralized tick or heartbeat broadcast at a set interval. This can be used to trigger the game loop.

The following code sets the frame rate to 30 and defines the stage as the listener for the ticks.

The TweenJS class will listen to this tick to perform the animations. Add this to Main().

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

Step 24: Preload Function

Every time a graphic is loaded this function will run. It will assign each image to a bitmap object and check thatall the elements are loaded before proceeding.

 
function loadGfx(e) 
{ 
	if(e.target.name = 'bg'){bg = new Bitmap(bgImg);} 
	if(e.target.name = 'main'){main = new Bitmap(mainImg);} 
	if(e.target.name = 'startB'){startB = new Bitmap(startBImg);} 
	if(e.target.name = 'creditsB'){creditsB = new Bitmap(creditsBImg);} 
	if(e.target.name = 'credits'){credits = new Bitmap(creditsViewImg);} 
	if(e.target.name = 'player'){player = new Bitmap(playerImg);} 
	if(e.target.name = 'ball'){ball = new Bitmap(ballImg);} 
	if(e.target.name = 'cpu'){cpu = new Bitmap(cpuImg);} 
	if(e.target.name = 'win'){win = new Bitmap(winImg);} 
	if(e.target.name = 'lose'){lose = new Bitmap(loseImg);} 
	 
	gfxLoaded++; 
	 
	if(gfxLoaded == 10) // remember to change this if you add more images 
	{ 
		addTitleView(); 
	} 
}

Step 25: Add Title View

When all the graphics are loaded the Title View is added to the stage by the following function:

 
function addTitleView() 
{ 
	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();

Step 26: Start Button Listeners

This function adds the necessary listeners to the TitleView buttons (it is part of addTitleView()):

 
	startB.onPress = addGameView; 
	creditsB.onPress = showCredits; 
}

Step 27: Show Credits

The Credits screen is shown when the user clicks the credits button; a mouse listener is added to the full image to remove it.

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

Step 28: Hide Credits

When the Credits screen is clicked it will be tweened back and removed from the stage.

 
// Hide Credits 
 
function hideCredits(e) 
{ 
	Tween.get(credits).to({x:480}, 300).call(rmvCredits); 
} 
 
// Remove Credits 
 
function rmvCredits() 
{ 
	stage.removeChild(credits); 
}

Let's stop here to test what we've done so far. Click here for a milestone demo.

Keep in mind that some lines have been commented out as some functions haven't been created yet.

Remember that the milestone is included in the source files, so if for some reason your file doesn't mimic this one, compare your source to mine to see what could be causing that.


Step 29: Show Game View

The following lines remove the TitleView from the stage and adds the GameView items to the stage. A mouse listener is added to the background, to start the game when clicked.

 
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.maxWidth = 1000;	//fix for Chrome 17 
	playerScore.x = 211; 
	playerScore.y = 20; 
	 
	cpuScore = new Text('0', 'bold 20px Arial', '#A3FF24'); 
	cpuScore.maxWidth = 1000;	//fix for Chrome 17 
	cpuScore.x = 262; 
	cpuScore.y = 20; 
	 
	stage.addChild(playerScore, cpuScore, player, cpu, ball); 
	stage.update(); 
	 
	// Start Listener  
	 
	bg.onPress = startGame; 
}

Step 30: Player Movement

The player will move along with the mouse's vertical position:

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

Step 31: Start Game

This code runs when the player clicks the game background, it adds the mouse listener that triggers the function in the previous step, and adds a Ticker to control the game loop.

Pay attention to the way the ticker is created: it's the equivalent of a Timer event in AS3.

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

Step 32: Reset

When a point is scored (by the player or the computer), the paddles and ball wil return to their original positions and the game will be paused:

 
function reset() 
{ 
	ball.x = 240 - 15; 
	ball.y = 160 - 15; 
	player.y = 160 - 37.5; 
	cpu.y = 160 - 37.5; 
	 
	stage.onMouseMove = null; //stop listening to the mouse 
	Ticker.removeListener(tkr); //pause the game 
	bg.onPress = startGame; 
}

Step 33: Ball Movement

If the game is not paused, the ball will be moved every frame using the variables we created before.

 
function update() 
{ 
	// Ball Movement  
 
	ball.x = ball.x + xSpeed; 
	ball.y = ball.y + ySpeed;

Step 34: CPU Movement

This code controls the computer's movement; the paddle is moved in a way that it will follow the ball while still having some margin of error.

 
if(cpu.y < ball.y) { 
	cpu.y = cpu.y + 2.5; 
} 
else if(cpu.y > ball.y) { 
	cpu.y = cpu.y - 2.5; 
}

Step 35: Wall Collisions

Here we check whether the ball is at the top or bottom border of the canvas; if so, the vertical speed is reversed and a sound is played.

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

Step 36: Scores

Now the left and right sides. This code also modifies the score, calls the reset function and plays a different sound depending on the side the ball touched.

 
/* 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'); 
}

Step 37: Ball-Paddle Collisions

The following code checks whether the ball is colliding with a paddle, by comparing the paddle's position to the ball's coordinates. If the bounding boxes of the two intersect, there's a collision, so we reverse the x-speed of the ball and play a sound.

 
/* 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'); 
}

Step 38: Check for Win/Game Over

You can modify the ending condition in the next lines - it's set to 10 points by default.

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

Step 39: Alert

This function will stop the game and show an alert, the contents of which depend to the game result.

 
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 40: Test

Save your work (if you haven't) and open the HTML file in the browser to see your game working!


Conclusion

Try modifying the game's variables to create your own version of the game!

I hope you liked this tutorial, thank you for reading!

Advertisement