The following article will be a very minimalist and straightforward guide to getting started with HTML5. While it is targeted at beginners, some coding experience is preferable since I won't bother with explaining Javascript basics. I'll leave that to other online tutorials or a Intro to Compsci course. This also is not platform specific in any way. It should run equally well on Linux, Windows or MacOS.
Since this will be a web game, an HTML file is required. This is not a proper web page; it's just enough to make it work.
The first line specifies which HTML version to use, specifically 5.
The second line is a dependency. JQuery does a lot of very useful things, but I will only be using it for mouse input in this article. The third line is a link to the script containing game logic.
The background color is not necessary but I find it useful.
Finally, you have the HTML5 canvas. The only unusual thing here is the onContextMenu; it returns false when the user right clicks on the canvas and as a result, no menu pops up.
Now to the game logic that goes in game.js. First come the declarations:
canvasID is the canvas itself. The context is an object specific to the canvas that contains all the drawing functionality.
The cursor location is self explanatory. The canvas position is necessary in order to calculate the cursor position relative to the canvas. Canvas dimensions as variables also allows for greater flexibility while coding.
These two things need to be done first, which is the only reason why they are lumped together:
The OnMouseMove function takes an Event Handler as an argument. The event handler has the cursor position relative to the upper left corner of the page, not of the canvas. This is where canvasX and canvasY come in.
The next bit is JQuery magic. When the document is ready (everything is loaded) the function is executed.
Fetching the canvasID is straightforward.
The next line is the last of the mouse input code. It adds an event listener that calls OnMouseMove when the mouse is moved. There are also mousedown and mouseup for mouse button presses and releases. e.which in the event handler specifices which button was pressed.
Context fetching is simple.
Next is where we find the actual location and dimensions of the canvas. getBoundingClientRect() returns the space which is taken up by the client. Getting the location is trivial. The rect right and bottom are the sum of the y position & height and the x position & width.
Run will be the update loop. This is where everything that needs to be executed repeatedly will be put. setInterval tells the browser that it needs to be executed every 20 ms (or whatever you specify).
Now to the drawing code. fillStyle and strokeStyle specify the color which you will be working with. The former is for filling shapes, and the latter is for drawing lines.
clearRect empties the canvas of data. This doesn't really matter when you're starting out but it becomes important when you deal with transparency and compositing. The arguments are x, y, width and height. fillRect should be self explanatory; it takes the same arguments.
We have already worked with fillStyle.
The font is specified as a string containing size and typeface. A couple of other valid typefaces are serif and monotype.
fillText takes a string, x coordinate and y coordinate as arguments. strokeText will draw an outline around the text; just be sure to specify strokeStyle instead.
Finally, call it in the Run function after you clear the canvas.
Javascript doesn't have classes like most other programming languages do, but they work. The only two things really necessary for a ping-pong game are the ball and the paddles.
The Ball
The above code includes everything the ball will need; radius, position, and velocity. It will also need a Draw function and Update function. The Draw function is the simplest:
The only slightly unusual thing about drawing circles in HTML5 is the fact that the context doesn't have a function for explicitly drawing them. Instead, one can create an arc path. First call the beginPath function, then create an arc of angle 2π, and finally close the path. The arguments are x position, y position, radius, start angle and end angle. More complicated paths are possible, but that is beyond the scope of this article.
Now to the next function:
There's nothing HTML5-specific here, just ordinary arithmetic and algebra. I didn't comment the aiPaddle code because it's pretty much the same thing.
The Paddle
Start with the simplest things; variable declarations and the Draw function:
This includes all the relevant physical properties, and a pair of AI-related properties.
The above makes sure that the paddle is not outside the boundaries of the screen. It puts a floor and a ceiling on the y position. The syntax may seem unusual but it works just the same.
The above sets the paddle's y position equal to the cursor's y position.
The AI code is a little more difficult to understand conceptually, so the lines are labeled.
0.1 tells the AI to only do stuff if the ball is moving towards it. This isn't absolutely necessary and the game will work fine without this line.
1.0 checks where the ball is relative to the paddle. It's true specifically when the ball is above the paddle. + this.h*this.aiOffset simulates error by changing where the center of the paddle actually is. aiOffset is a number between -0.45 and 0.45 so the new "center" will always be on the paddle.
1.1 checks if the y distance between the paddle "center" and the ball is greater than the maximum velocity at which the AI can move the paddle. If it is, it moves the paddle by that amount. Otherwise,
1.2 the ball is "within range" so instead of overshooting, it just moves to the ball's y position.
2.0 is pretty much the same thing just in the other direction.
3.0 makes sure that the AI doesn't actually move the paddle off screen.
Finally, just declare that last function which changes the offset. If you remember, it is called every time the ball bounces off the player's paddle. Remember to close all curly braces!
Here we create the ball and paddles.
Finally, update the Run function;
Everything here is quite straightforward. Just call all the Update and Draw functions in the objects, and draw the score. The only important thing here is to Update before Drawing, and to clear the context before Drawing as well.
This is perhaps the simplest functioning game that one can make. It has a little bit of everything: graphics, input, physics, and AI. It doesn't use any advanced features, but it's a good starting point.
The HTML
Since this will be a web game, an HTML file is required. This is not a proper web page; it's just enough to make it work.
<!DOCTYPE html> <script src="http://code.jquery.com/jquery-latest.js"></script> <script src="game.js"></script> <body bgcolor=#808080> <canvas id="gameCanvas" width="800" height="600" onContextMenu="javascript: return false;"></canvas> </body>
The first line specifies which HTML version to use, specifically 5.
The second line is a dependency. JQuery does a lot of very useful things, but I will only be using it for mouse input in this article. The third line is a link to the script containing game logic.
The background color is not necessary but I find it useful.
Finally, you have the HTML5 canvas. The only unusual thing here is the onContextMenu; it returns false when the user right clicks on the canvas and as a result, no menu pops up.
The Game Logic
Declarations
Now to the game logic that goes in game.js. First come the declarations:
var canvasID, context; var cursorX = 0, cursorY = 0; var canvasX = 0, canvasY = 0, canvasW = 800, canvasH = 600; var playerScore=0, aiScore=0;
canvasID is the canvas itself. The context is an object specific to the canvas that contains all the drawing functionality.
The cursor location is self explanatory. The canvas position is necessary in order to calculate the cursor position relative to the canvas. Canvas dimensions as variables also allows for greater flexibility while coding.
Mouse Code and Initialization
These two things need to be done first, which is the only reason why they are lumped together:
function OnMouseMove(e){ cursorX = e.pageX-canvasX; cursorY = e.pageY-canvasY; } $(document).ready(function(){ canvasID = document.getElementById("gameCanvas"); canvasID.addEventListener("mousemove", OnMouseMove, false); context = canvasID.getContext('2d'); var rect = canvasID.getBoundingClientRect(); canvasX = rect.left; canvasY = rect.top; canvasW = rect.right-canvasX; canvasH = rect.bottom-canvasY; })
The OnMouseMove function takes an Event Handler as an argument. The event handler has the cursor position relative to the upper left corner of the page, not of the canvas. This is where canvasX and canvasY come in.
The next bit is JQuery magic. When the document is ready (everything is loaded) the function is executed.
Fetching the canvasID is straightforward.
The next line is the last of the mouse input code. It adds an event listener that calls OnMouseMove when the mouse is moved. There are also mousedown and mouseup for mouse button presses and releases. e.which in the event handler specifices which button was pressed.
Context fetching is simple.
Next is where we find the actual location and dimensions of the canvas. getBoundingClientRect() returns the space which is taken up by the client. Getting the location is trivial. The rect right and bottom are the sum of the y position & height and the x position & width.
The Update Loop
function Run(){ context.fillStyle="black"; context.clearRect(0,0, canvasW, canvasH); context.fillRect(0,0, canvasW, canvasH); } setInterval(Run,20);
Run will be the update loop. This is where everything that needs to be executed repeatedly will be put. setInterval tells the browser that it needs to be executed every 20 ms (or whatever you specify).
Now to the drawing code. fillStyle and strokeStyle specify the color which you will be working with. The former is for filling shapes, and the latter is for drawing lines.
clearRect empties the canvas of data. This doesn't really matter when you're starting out but it becomes important when you deal with transparency and compositing. The arguments are x, y, width and height. fillRect should be self explanatory; it takes the same arguments.
Drawing the Score
function DrawScores(){ context.fillStyle = "white"; context.font = "16px sans-serif"; context.fillText(aiScore, 30,30); context.fillText(playerScore, canvasW-50,30); }
We have already worked with fillStyle.
The font is specified as a string containing size and typeface. A couple of other valid typefaces are serif and monotype.
fillText takes a string, x coordinate and y coordinate as arguments. strokeText will draw an outline around the text; just be sure to specify strokeStyle instead.
Finally, call it in the Run function after you clear the canvas.
function Run(){ context.fillStyle="black"; context.clearRect(0,0, canvasW, canvasH); context.fillRect(0,0, canvasW, canvasH); DrawScores(); }
The Classes
Javascript doesn't have classes like most other programming languages do, but they work. The only two things really necessary for a ping-pong game are the ball and the paddles.
The Ball
function Ball(){ this.radius = 8; this.x = canvasW/2; this.y = canvasH/2; this.vx = 5; this.vy = 0; this.Draw = function(){ //...\\ } this.Update = function(){ //...\\ } }
The above code includes everything the ball will need; radius, position, and velocity. It will also need a Draw function and Update function. The Draw function is the simplest:
this.Draw = function(){ context.fillStyle = "white"; context.beginPath(); context.arc(this.x, this.y, this.radius, 0, 3.1416*2); context.closePath(); context.fill(); }
The only slightly unusual thing about drawing circles in HTML5 is the fact that the context doesn't have a function for explicitly drawing them. Instead, one can create an arc path. First call the beginPath function, then create an arc of angle 2π, and finally close the path. The arguments are x position, y position, radius, start angle and end angle. More complicated paths are possible, but that is beyond the scope of this article.
Now to the next function:
this.Update = function(){ this.x += this.vx; this.y += this.vy; //move the ball if(this.x > playerPaddle.x - playerPaddle.w/2 - this.radius){//check if the ball has traveled //far enough to the right to possibly interact with the right paddle. if(this.y >= playerPaddle.y - playerPaddle.h/2 && this.y <= playerPaddle.y + playerPaddle.h/2){ //check if it actually hit the paddle this.vy = (playerPaddle.y-this.y)*-0.4; //change the y velocity depending on which part //of the paddle the ball hit. this.x = playerPaddle.x - playerPaddle.w/2 - this.radius; //moves the ball out of the paddle this.vx*=-1; //make the ball bounce off }else{//if the player misses the ball, incriment the ai score and reset the ball. aiScore++; this.vy=0; this.x = canvasW/2; this.y = canvasH/2; } aiPaddle.AIChangeOffset();//AI thing, will make sense later. } if(this.x < aiPaddle.x + aiPaddle.w/2 + this.radius){//same thing if(this.y >= aiPaddle.y - aiPaddle.h/2 && this.y <= aiPaddle.y + aiPaddle.h/2){ this.vy = (aiPaddle.y-this.y)*-0.2; this.x = aiPaddle.x + aiPaddle.w/2 + this.radius; this.vx*=-1; }else{ playerScore++; this.vy=0; this.x = canvasW/2; this.y = canvasH/2; } } if(this.y<this.radius){ this.vy*=-1; this.y=this.radius;} else if(this.y>canvasH-this.radius){ this.vy*=-1; this.y=canvasH-this.radius;} //have the ball bounce off the top and bottom of the screen. }
There's nothing HTML5-specific here, just ordinary arithmetic and algebra. I didn't comment the aiPaddle code because it's pretty much the same thing.
The Paddle
Start with the simplest things; variable declarations and the Draw function:
function Paddle(){ this.x=0; this.y = canvasH/2; this.w = 32; this.h=64; this.aiv=9; //the maximum velocity with which the ai paddle can move this.aiOffset = 0; //how far off center the paddle will be when the ball reaches the end this.Draw = function(){ context.fillStyle="white"; context.fillRect(this.x - this.w/2, this.y - this.h/2, this.w, this.h); }
This includes all the relevant physical properties, and a pair of AI-related properties.
this.FloorAndCeiling = function(){ if(this.y < this.h/2) this.y = this.h/2; if(this.y > canvasH-this.h/2) this.y = canvasH-this.h/2; }
The above makes sure that the paddle is not outside the boundaries of the screen. It puts a floor and a ceiling on the y position. The syntax may seem unusual but it works just the same.
this.PlayerUpdate = function(){ this.y = cursorY; this.FloorAndCeiling(); }
The above sets the paddle's y position equal to the cursor's y position.
this.AIUpdate = function(){ if(ball.vx < 0){//0.1 if(ball.y + this.h*this.aiOffset > this.y -this.aiv){//1.0 if(this.y+this.aiv<ball.y + this.h*this.aiOffset)//1.1 this.y+=this.aiv; else this.y=ball.y + this.h*this.aiOffset;//1.2 } if(ball.y + this.h*this.aiOffset < this.y + this.aiv){//2.0 if(this.y-this.aiv>ball.y + this.h*this.aiOffset) this.y-=this.aiv; else this.y=ball.y + this.h*this.aiOffset; } this.FloorAndCeiling(); //3.0 } }
The AI code is a little more difficult to understand conceptually, so the lines are labeled.
0.1 tells the AI to only do stuff if the ball is moving towards it. This isn't absolutely necessary and the game will work fine without this line.
1.0 checks where the ball is relative to the paddle. It's true specifically when the ball is above the paddle. + this.h*this.aiOffset simulates error by changing where the center of the paddle actually is. aiOffset is a number between -0.45 and 0.45 so the new "center" will always be on the paddle.
1.1 checks if the y distance between the paddle "center" and the ball is greater than the maximum velocity at which the AI can move the paddle. If it is, it moves the paddle by that amount. Otherwise,
1.2 the ball is "within range" so instead of overshooting, it just moves to the ball's y position.
2.0 is pretty much the same thing just in the other direction.
3.0 makes sure that the AI doesn't actually move the paddle off screen.
this.AIChangeOffset = function(){ this.aiOffset = (Math.random()-0.5)*0.9; } }
Finally, just declare that last function which changes the offset. If you remember, it is called every time the ball bounces off the player's paddle. Remember to close all curly braces!
Variable Initialization
var ball = new Ball(); var aiPaddle = new Paddle(), playerPaddle = new Paddle(); playerPaddle.x = canvasW; //move the paddle to the other side of the screen
Here we create the ball and paddles.
The Game Loop
Finally, update the Run function;
function Run(){ //NEW CODE PT 1 aiPaddle.AIUpdate(); playerPaddle.PlayerUpdate(); ball.Update(); //Update all the game objects //END NEW CODE PT 1 context.fillStyle="black"; context.clearRect(0,0, canvasW, canvasH); context.fillRect(0,0, canvasW, canvasH);//clear the context //NEW CODE PT 2 ball.Draw(); aiPaddle.Draw(); playerPaddle.Draw(); DrawScores();//Draw it all //END NEW CODE PT 2 }
Everything here is quite straightforward. Just call all the Update and Draw functions in the objects, and draw the score. The only important thing here is to Update before Drawing, and to clear the context before Drawing as well.
Conclusion
This is perhaps the simplest functioning game that one can make. It has a little bit of everything: graphics, input, physics, and AI. It doesn't use any advanced features, but it's a good starting point.