Tuesday, November 29, 2016

Simple Pong Game Using HTML Canvas

This is the third assignment that i have to post here in my blog.
i need to demonstrate the use of the HTML canvas tag, so i thought, what better way to do that than to create one of the simplest games ever?

you heard me! we're making;

PONG


first thing's first; all you need to create this game is a text editor of your choice (i use sublime), and a browser that supports canvas...
if your browser does NOT support canvas... then are you living under a rock? get yourself the latest version of google chrome or something.

so to start, i'm going to show you the end result;
Looks awesome right? RIGHT!?

here's the scource code;

<!DOCTYPE HTML>
<head>
<title> THE GAME(?) </title>
</head>
<body>
<canvas id="canvas" width = "1000" height = "700"></canvas>
</body>
<script>
var canvasMan = document.getElementById("canvas");

var player1PosX = 100;
var player2PosX = canvasMan.width - 100;

var player1PosY = canvasMan.height/2;
var player2PosY = canvasMan.height/2;

var playerSize = 100;

var speedOverall = 5;

var vx = -speedOverall;
var vy = speedOverall;
var bx = canvasMan.width/2;
var by = canvasMan.height/2;

var aiSpeed = 2;

var scorePlayer1 = 0;
var scorePlayer2 = 0;

var c=canvasMan.getContext("2d");
canvasMan.addEventListener ('mousemove', function(e){
player1PosY = e.clientY - playerSize/2;
});
setInterval (update, 10)

function runAI(){
if (player2PosY + playerSize/2 > by){
player2PosY -= aiSpeed;
}
if (player2PosY + playerSize/2 < by){
player2PosY += aiSpeed;
}
}
function reset(){
bx = canvasMan.width/2;
by = canvasMan.height/2;
vx = -speedOverall;
vy = Math.floor(Math.random () * 10 - 5 + 1);
}
function update(){
by += vy;
bx += vx;
if (by < 0){
vy *= -1;
}
if (by > canvasMan.height){
vy *= -1;
}

if (bx < player1PosX + 10 && vx < 0){
if (by > player1PosY && by < player1PosY + playerSize && bx > player1PosX){
vx *= -1;
vy = Math.floor(Math.random () * 10 - 5 + 1);
}
if (bx < 0){
scorePlayer2++;
reset();
}
}
if (bx > player2PosX - 10 && vx > 0){
if (by > player2PosY && by < player2PosY + playerSize && bx < player2PosX){
vx *= -1;
vy = Math.floor(Math.random () * 10 - 5 + 1);
}
if (bx > canvasMan.width){
scorePlayer1++;
reset();
}
}

runAI();

//draw the background
c.fillStyle = "#000000";
// test if double qoute can be used in this particular statement
c.fillRect (0, 0, canvasMan.width, canvasMan.height);

//draw the players;
c.fillStyle = "#ffffff";
c.fillRect (player1PosX, player1PosY, 10, playerSize);
c.fillRect (player2PosX, player2PosY, 10, playerSize);
c.fillRect (bx, by, 10, 10);
c.font = "50px helvetica";
c.fillText(scorePlayer1, 200, 100);
c.fillText(scorePlayer2, canvasMan.width - 200, 100);
}

</script>

now, it's pretty straight forward.
we have a 'canvas' tag (<canvas></canvas>) that has several attributes; the id, the width, and the height.
i think the later two is pretty self explanatory, the width and the height just sets the size of the canvas.
the ID is going to be used to later reference the element int the javascript code.

the rest of the HTML is not really interesting, so let's move on to the script tag (<script></script>) which indicates the start and the end of a javascript block...

inside the script tag we have several variable declerations, these are done outside a specific function because we need them to be global functions.
the first variable is the canvas variable which stores tha canvas element in the HTML, we reference to it by using the id we specified earlier and using the getElementById statement.

the next two variables (player1PosX and player2PosX) is used to later state where to draw the paddles in the canvas along the x axis, we use 100 for the player1PosX so the first paddle so that the first paddle will be positioned 100 pixels after the start of the canvas (0).

both player1PosY and player2PosY is set to half the height of the canvas to position them in the middle of the canvas.

playerSize is used to later calculate the hit boxes of the paddles and use that to figure out wether the ball hit the paddle or not.

speedOverall is the speed of the ball. which will later be modified with Math.random() to create a dynamic gameplay.

vx and vy is going to be the speed of the ball, as you can see, we set the value to speedOverall.

bx and by is the position of the ball, it will later be constantly changed during the entirety of the gameplay.

aiSpeed is just the speed the second player can move in.

and the scorePlayer1 and scorePlayer2 just keeps track of the scores...

c references to the 2d context of the canvas
The getContext() method returns an object that provides methods and properties for drawing on the canvas.
This reference will cover the properties and methods of the getContext("2d") object, which can be used to draw text, lines, boxes, circles, and more - on the canvas.

this is all the variable declerations, now let's move on to the heart of the code.

canvasMan.addEventListener ('mousemove', function(e){
player1PosY = e.clientY - playerSize/2;
});

what this does is that it creates an event listener that will listen to wether or not the mouse is moving or not, hence the 'mousemove' parameter, it could also be used for other stuff such as 'mouseover' or 'mousedown' and many more, but for this particular example, we'll stick with 'mousemove'.
the second parameter is a function that is called whenever the event is heard, in this example we'll just create an inline function because the function is only oneline. this function will take a parameter called e.
inside the function we only have one statement. but this statement will be repeated for as long as the mouse is moving. this statement updates the variable called player1PosY and assigns to it the value of the mouse in the y axis (that is what e.clientY is, note e can be changed as long as the parameter is changed) minus the size of the paddle divided by two, this is due to the fact taht canvas draws objects (in our case rectangles) from the top-left corner.

the second statement; setInterval (update, 10); creates a continuous loop that executes every 10 miliseconds, hence the second parameter, which could be changed accordingly but in this case we'll just stick with 10...
the first parameter (update) is a function that will be run every interval (the 10 miliseconds).

now let's look at the update function...
function update(){
by += vy;
bx += vx;
if (by < 0){
vy *= -1;
}
if (by > canvasMan.height){
vy *= -1;
}

if (bx < player1PosX + 10 && vx < 0){
if (by > player1PosY && by < player1PosY + playerSize && bx > player1PosX){
vx *= -1;
vy = Math.floor(Math.random () * 10 - 5 + 1);
}
if (bx < 0){
scorePlayer2++;
reset();
}
}
if (bx > player2PosX - 10 && vx > 0){
if (by > player2PosY && by < player2PosY + playerSize && bx < player2PosX){
vx *= -1;
vy = Math.floor(Math.random () * 10 - 5 + 1);
}
if (bx > canvasMan.width){
scorePlayer1++;
reset();
}
}

runAI();

//draw the background
c.fillStyle = "#000000";
// test if double qoute can be used in this particular statement
c.fillRect (0, 0, canvasMan.width, canvasMan.height);

//draw the players;
c.fillStyle = "#ffffff";
c.fillRect (player1PosX, player1PosY, 10, playerSize);
c.fillRect (player2PosX, player2PosY, 10, playerSize);
c.fillRect (bx, by, 10, 10);
c.font = "50px helvetica";
c.fillText(scorePlayer1, 200, 100);
c.fillText(scorePlayer2, canvasMan.width - 200, 100);
}

the first few lines handles game mechanics and logic,
canvas animation works by drawing new things every set interval and forgetting anything before it. so we can use this to our advantage by drawing the objects with coordinates of variables we change with functions and logic,
the bx and by is used for this, it is added by the value of vx and vy, and later it will be used as the coordinates for the "ball", after that we have several logical statements, which is mostly mathematical stuff that i will not explain much but basically the first 2 if's are used to bounce the ball if it hits the top and bottom of the canvas

the later 2 if's and it's nested if's are logics used by the game to determine wether the ball went past the paddles (on either sides) and if so add the score to the appropriate players and run the reset function, and if not bounce the ball back.
we determine if the ball hits the paddle by using the paddleSize variable earlier, since we know that the coordinates of the paddle starts from the top point (0 from the paddle) to the bottom (paddle size) added by the coordinates of the paddle, then we can just check if the ball is within the paddle Y coord and the paddle Y  coord plus the size of the paddle.

next we dwell on the code that creates the visuals of the game;

c.fillStyle = "#ffffff";

determines the colours of the elements drawn after it's statement until another fillStyle is stated.

fill rect will accept parameters of this order fillRect (xCoordinate, yCoordinate, width, height);

c.fillRect (player1PosX, player1PosY, 10, playerSize);
c.fillRect (player2PosX, player2PosY, 10, playerSize);

draws the paddles for both players. note that player1PosY will constantly change according to the user mouse Y coordinates, the player2PosY will also be modified by the runAi function, which we'll get into later.

c.fillRect (bx, by, 10, 10);

draws the ball, which is actually a rectangle, because we wanted to capture the old-ish feel of the pong game.
notice the first and second parameter is bx and by, which will be changed constantly.

c.font = "50px helvetica";
c.fillText(scorePlayer1, 200, 100);
c.fillText(scorePlayer2, canvasMan.width - 200, 100);

just shows the scores of both players...


now let's move on the the reset() function;
function reset(){
bx = canvasMan.width/2;
by = canvasMan.height/2;
vx = -speedOverall;
vy = Math.floor(Math.random () * 10 - 5 + 1);
}
this function basically runs everytime the ball hits either edge of the screen horizontally speaking, it will put the ball back in the middle of the canvas and randomize the speed it is going in the y direction.

next is the AI function that will move the second player, this will not be as interesting as it sounds;
function runAI(){
if (player2PosY + playerSize/2 > by){
player2PosY -= aiSpeed;
}
if (player2PosY + playerSize/2 < by){
player2PosY += aiSpeed;
}
}
that's right; 4 lines of code for an AI, a really dumb AI;
in a nutshell this code checks wether the ball is above the paddle, which in that case it will move up, or is it below the paddle, which it will move down.

and there you have it, a working PONG game! enjoy!!!

also, enjoy the latest installment of

"PORTAL PONG!"






5 comments: