4. User Input

To create a little game out of part One, the user needs to able to control some paddles. In this case it's done by the keyboard buttons LEFT and RIGHT for the paddle on the bottom of the screen and the keyboard buttons A and S for the paddle on the top of the screen.

Now we need javascript to check for keyboard input. So we create a function in the javscript file for keypresses, more specifically keydown (the key is pressed down) and keyup (the key is released). And some variables to store the state for the key.

The code below adds the variables and the needed function to our existing javascript file directly after the variables section and before the function init().


// Variables for the keyboard input
var keyLeftDown, keyRightDown, keyADown, keySDown;

// Keyboard handling, set the keys to true if they're down
document.onkeydown = function onKeyDown(e) {
switch ( e.keyCode ) {
// The LEFT key is down
case 37 :
keyLeftDown = true;
break;
// The RIGHT key is down
case 39 :
keyRightDown = true;
break;
// The A key is down
case 65 :
keyADown = true;
break;
// The S key is down
case 83 :
keySDown = true;
break;
}
}

// Keyboard handling, set the keys to false if they're up
document.onkeyup = function onKeyUp(e) {
switch ( e.keyCode ) {
// The LEFT key is up
case 37 :
keyLeftDown = false;
break;
// The RIGHT key is up
case 39 :
keyRightDown = false;
break;
// The A key is up
case 65 :
keyADown = false;
break;
// The S key is up
case 83 :
keySDown = false;
break;
}
}



Each key on the keyboard has a number and you need to know which number belongs to which key, you can find a keycode list for this on the link:

http://www.cambiaresearch.com/c4/702b8cd1-e5b0-42e6-83ac-25f0306e3e25/Javascript-Char-Codes-Key-Codes.aspx

Once we know which keycodes we need we will switch to them by using this switch statement:


switch ( e.keyCode )


The number for the key being pressed or released is given by e.keyCode, the switch statement switches to the right cases. For example the case with number 37 for key LEFT and sets the variable keyLeftDown to true or false.

These variables for the keys can be used for moving the paddles horizontal along the playfield. However we need to create these paddles first, just like the ball, only with rectangles this time.

5. Move Two Paddles

The two paddles are basically rectangles with a known height, width, color, position and speed. So we need variables to store and retrieve this information. Once known we need to draw two rectangle rectangles in the draw() function.

So add these variables to the variables section:


// Variables for the two paddles
var padw, padh, paddx;
var pad1x, pad1y, pad1color;
var pad2x, pad2y, pad2color;


And set them to default values in the in the init() function, for example after setting the ball variables:


// Set the paddles variables
// The width, height and speed (dx)
padw = 128;
padh = 32;
paddx = 16;
// The position and color from paddle 1 and 2
pad1x = (width-padw)/2;
pad1y = height - 2*padh;
pad1color = rgb(255,0,0); // red color
pad2x = (width-padw)/2;
pad2y = 1*padh;
pad2color = rgb(0,0,255); // blue color


Note: I've changed the ball radius from 32 to 16.

For the draw part this code needs to added to the draw() function, for example after drawing the ball:


// Draw paddle 1
// Set the color
context.fillStyle = pad1color;
// draw the rectangle
context.fillRect(pad1x, pad1y, padw, padh);
// Draw paddle 2
// Set the color
context.fillStyle = pad2color;
// draw the rectangle
context.fillRect(pad2x, pad2y, padw, padh);



That should draw two paddles, one on top and one on the bottom.

The moving part is down by processing the keyboard inpunt with a function called: getKeyInput(), add this function somewhere in the code, for example before the init() function:


// Process the keyboard input
function getKeyInput() {
// Move paddle 1
// The LEFT key has been pressed
if ( keyLeftDown ) {
if ( pad1x > 0 ) {
pad1x-=paddx;
}
}
// The RIGHT key has been pressed
if ( keyRightDown ) {
if ( pad1x < width - padw ) {
pad1x+=paddx;
}
}
// Move paddle 2
// The A key has been pressed
if ( keyADown ) {
if ( pad2x > 0 ) {
pad2x-=paddx;
}
}
// The S key has been pressed
if ( keySDown ) {
if ( pad2x < width - padw ) {
pad2x+=paddx;
}
}
}


Note: Don't forget to insert this function in the function loop() between the functions move() and draw().

The keyboard buttons LEFT/RIGHT should move the red paddle and the buttons A/S should move the blue paddle.


6. Check For Collisions

In any paddle game the ball should bounce when it hits a paddle, so this is what we're going to code. First we need to check if the ball hits the paddle with some sort of collision checking. And easy way of doing this is by checking if the center of the ball is inside a paddle. This approach isn't pixel perfect, but that doesn't matter for this tutorial.

Add a function called: checkForCollisions() to the javascript code, for example before the move() function:


// The checkForCollisions() function checks for collisions
function checkForCollisions() {
// Check for a collision between the ball and paddle 1
if ( ypos + radius >= pad1y ) {
if ( xpos + dx > pad1x && xpos + dx < pad1x + padw ) {
dy = -radius;
}
}
// Check for a collision between the ball and paddle 2
if ( ypos - radius <= pad2y + padh ) {
if ( xpos + dx > pad2x && xpos + dx < pad2x + padw ) {
dy = radius;
}
}
}


Note: Don't forget to insert this function in the function loop() between the functions move() and getKeyInput().

The ball now bounces of the borders and the paddles, this looks a little weird because sometimes it will pass through the paddle.

7. The Score Counter

When should a point be scored? Probably when the ball passes the paddles and hits the top or bottom border. So look up your these events in your javascript code, they should be in the function move() which moves ball and checks for collisions with the playfield border.

To start counting scores, a score variables should be introduced for the red and blue player. So add these to the variables section:


// Variables for scores
var score1, score2;


And the scores should be set to theire default values of zero. Add this code to the init() function:


// Set the default scores
score1 = 0;
score2 = 0;


Whenever a player scores a point the ball must be repositionned and the score from the player should be updated. The collision checks with the borders are in the function move(). These collision checks should be move to the function checkForCollision() and changed to the following code:


// Check for collisions with the vertical borders and flip the direction
if ( xpos - radius <= 0 || xpos + radius >= width ) {
dx = -dx;
}
// Check for a collision with the bottom horizontal border, update the score and reposition the ball
if ( ypos + radius > height ) {
score2++;
xpos = width/2;
ypos = height/2;
dx = -dx;
dy = -radius;
}
// Check for collisions with the top horizontal border, update score and reposition the ball
if ( ypos - radius < 0 ) {
score1++;
xpos = width/2;
ypos = height/2;
dx = -dx;;
dy = radius;
}


Note: Don't forget to insert this code in the function checkForCollision() after the collision code from the ball with paddle 1 and paddle 2.

Of course we want the see the scores so we need to display them somewhere on screen and the following code will do that, just place it at the end of the draw() function:


// Draw the scores
context.font = 16px sans-serif;
context.fillStyle = rgb(255,255,0);
// Score for player 1
context.textAlign = left;
context.fillText(Score player 1: + score1, 0, height-2);
// Score for player 2
context.textAlign = right;
context.fillText(Score player 2: + score2, width, 14);


The score information for player 1 and 2 is now displayed in the top right and bottom left corner.

The entire javascript file looks as follows:


// Variables for the canvas and the context
var canvas, context;
// Variables for the resolution
var width, height;
// Variables for the ball
var radius, xpos, ypos, dx, dy;
// Variables for the keyboard input
var keyLeftDown, keyRightDown, keyADown, keySDown;
// Variables for the two paddles
var padw, padh, paddx;
var pad1x, pad1y, pad1color;
var pad2x, pad2y, pad2color;
// Variables for scores
var score1, score2;

// Keyboard handling, set the keys to true if they're down
document.onkeydown = function onKeyDown(e) {
switch ( e.keyCode ) {
// The LEFT key is down
case 37 :
keyLeftDown = true;
break;
// The RIGHT key is down
case 39 :
keyRightDown = true;
break;
// The A key is down
case 65 :
keyADown = true;
break;
// The S key is down
case 83 :
keySDown = true;
break;
}
}

// Keyboard handling, set the keys to false if they're up
document.onkeyup = function onKeyUp(e) {
switch ( e.keyCode ) {
// The LEFT key is up
case 37 :
keyLeftDown = false;
break;
// The RIGHT key is up
case 39 :
keyRightDown = false;
break;
// The A key is up
case 65 :
keyADown = false;
break;
// The S key is up
case 83 :
keySDown = false;
break;
}
}

// Process the keyboard input
function getKeyInput() {
// Move paddle 1
// The LEFT key has been pressed
if ( keyLeftDown ) {
if ( pad1x > 0 ) {
pad1x-=paddx;
}
}
// The RIGHT key has been pressed
if ( keyRightDown ) {
if ( pad1x < width - padw ) {
pad1x+=paddx;
}
}
// Move paddle 2
// The A key has been pressed
if ( keyADown ) {
if ( pad2x > 0 ) {
pad2x-=paddx;
}
}
// The S key has been pressed
if ( keySDown ) {
if ( pad2x < width - padw ) {
pad2x+=paddx;
}
}
}

// Initialize the game
function init() {
// Get the canvas element
canvas = document.getElementById('canvas');
// Check if canvas is supported
if ( canvas.getContext ) {
// Get the 2d context
context = canvas.getContext('2d');
// Set the width and height to the width and height from the canvas element
width = canvas.width;
height = canvas.height;
// Set the ball variables
radius = 16;
dx = radius
dy = radius;
xpos = radius;
ypos = radius;
// Set the paddles variables
// The width, height and speed (dx)
padw = 128;
padh = 32;
paddx = 16;
// The position and color from paddle 1 and 2
pad1x = (width-padw)/2;
pad1y = height - 2*padh;
pad1color = rgb(255,0,0); // red color
pad2x = (width-padw)/2;
pad2y = 1*padh;
pad2color = rgb(0,0,255); // blue color
// Set the default scores
score1 = 0;
score2 = 0;
// Call the function loop every 50 ms
setInterval(loop,50);
}
}

// The checkForCollisions() function checks for collisions
function checkForCollisions() {
// Check for a collision between the ball and paddle 1
if ( ypos + radius >= pad1y ) {
if ( xpos + dx > pad1x && xpos + dx < pad1x + padw ) {
dy = -radius;
}
}
// Check for a collision between the ball and paddle 2
if ( ypos - radius <= pad2y + padh ) {
if ( xpos + dx > pad2x && xpos + dx < pad2x + padw ) {
dy = radius;
}
}
// Check for collisions with the vertical borders and flip the direction
if ( xpos - radius <= 0 || xpos + radius >= width ) {
dx = -dx;
}
// Check for a collision with the bottom horizontal border, update the score and reposition the ball
if ( ypos + radius > height ) {
score2++;
xpos = width/2;
ypos = height/2;
dx = -dx;
dy = -radius;
}
// Check for collisions with the top horizontal border, update score and reposition the ball
if ( ypos - radius < 0 ) {
score1++;
xpos = width/2;
ypos = height/2;
dx = -dx;;
dy = radius;
}
}

// The move function moves the ball
function move() {
// Move the ball
xpos+=dx;
ypos+=dy;
}

// Render the graphics
function draw() {
// Draw the black background
context.fillStyle = rgb(0,0,0);
context.fillRect(0, 0, width, height);
// Draw the green ball
context.fillStyle = rgb(0,255,0);
context.beginPath();
context.arc(xpos, ypos, radius, 0, Math.PI*2, false);
context.fill();
// Draw paddle 1
// Set the color
context.fillStyle = pad1color;
// draw the rectangle
context.fillRect(pad1x, pad1y, padw, padh);
// Draw paddle 2
// Set the color
context.fillStyle = pad2color;
// draw the rectangle
context.fillRect(pad2x, pad2y, padw, padh);
// Draw the scores
context.font = 16px sans-serif;
context.fillStyle = rgb(255,255,0);
// Score for player 1
context.textAlign = left;
context.fillText(Score player 1: + score1, 0, height-2);
// Score for player 2
context.textAlign = right;
context.fillText(Score player 2: + score2, width, 14);
}

// The main loop
function loop() {
move();
checkForCollisions();
getKeyInput();
draw();
}


A working version of this two player pong game can be found on the link below:

https://dl.dropboxusercontent.com/u/82972447/tdc/canvas-game-tutorial/canvas-game-tutorial-part-two.html

8. Further Reading

If you would like to read some more information on canvas, because I left out some important setup and drawing parts you could try the Canvas Tutorial from Mozilla Developer Center, HTML5 canvas - the basics from Opera Developer Community, LET’S CALL IT A DRAW(ING SURFACE) from Mark Pilgrim or Canvas element on Wikipedia.

https://developer.mozilla.org/en/canvas_tutorial
http://dev.opera.com/articles/view/html-5-canvas-the-basics/
http://diveintohtml5.org/canvas.html
http://en.wikipedia.org/wiki/Canvas_element

Edited by Jenswa on 2010-06-20 to clean up the code parts.
Edited by Jenswa on 2011-01-30 to change the game tutorial hyperlink.
Edit on 2013-12-23 by Jenswa: changed the link.