Click here to Skip to main content
Click here to Skip to main content

HTML5 Games 101 - An introductory tutorial to HTML5 Games

, 10 Aug 2012
Rate this:
Please Sign up or sign in to vote.
In this tutorial, I will show you how to use the basic features of HTML5 and get the simple game of Snake up and ready within a couple of hours, even if you are a beginner.

In my last blog, I had posted the game of Snake that I developed as my first attempt in HTML5 programming and all I can say is... it was fun!

In this tutorial, I will show you how to use the basic features of HTML5 and get the simple game of Snake up and ready within a couple of hours, even if you are a beginner. All you require is some basic logic and any experience with programming is a huge plus. However, I must also admit here that this is more of a JavaScript work than HTML5, as it just uses the canvas and a couple more elements.

Okay, so let's begin!

First things first - Create a HTML file. Open your favorite editor, copy the snippet below and save it with a name as per your liking. I'll stick with "index.html".There is no different file extension for a HTML5 document. It simply uses one subtle and welcoming change in the "DOCTYPE" declaration (welcoming as its easy to remember compared to that from HTML4) and the browser will understand that it is HTML5.

Code Snippet

<!DOCTYPEHTML>
<html>
<head>
</head>

<body>
 <divid="wrapper">
   <h1>Snake</h1>
   <divid="score">Score:0     Level:1</div>
   <canvaswidth="300"height="400"id="canvas">
   </canvas>
   <divid="control">Controls: W = Up; A = Left; S = Down; D = Right</div>
 </div>
</body>

</html>

Lets now look at what we have written here:

<!Doctype HTML> -As I mentioned above,a very simple and neat way to tell your HTML5 enabled browser that it will be accessing a HTML5 document. Then there is a regular <head> tag followed by the <body> tag

In the body of the page, we first define a div named "wrapper" which essentially will include everything that we are about to display. Note that this is not required in our example. Then we create a div to display the game score and the level. As you'll find out later, we will also use this to inform the player if the game is over.

Then comes the main component, "Canvas", the most essential HTML5 component for that can be used for rendering graphs, game graphics, or other visual images on the fly. HTML5 defines a set of functions for drawing various shapes, creating paths, shades and gradients with help of JavaScript to display them within the Canvas. For the purpose of this tutorial, I have defined canvas with width as 300, height as 400 and id = "canvas".

Lastly, we define another div to help user with the controls or any other message that we may want to display to user related to controls during gameplay or afterwards.

Now the fun part, the JavaScript for the game. We'll start with defining all the global variables that we'll require during our game. The script goes into the <head> tag of the html file.

Code Snippet

//global constants
// Variable to hold the entire game context
var context;        

//specifying the width and height of our game area, we'll use the complete canvas
//in this case
var width = 300;
var height = 400;

//specify the initial game configuration
var snakeLength = 3;    // initial length of snake is set to 3
var level = 1;       // start from level 1
var sqSize = 10;      // step size is 10px. Also size of single body unit   
/* *************************** /
* specify the initial snake alignment and direction of movement
* Snake is starts horizontal moving towards its right
* *************************** */
//array to manage X coordinates for snake body
var bodyX = new Array(150, 150-sqSize, 150-2*sqSize);  

//array to manage Y coordinates for snake body
var bodyY = new Array(200, 200, 200);          
var vX = new Array(1, 1, 1);  //array to manage horizontal velocity for snake body
var vY = new Array(0, 0, 0);  //array to manage vertical velocity for snake body
//variables used to put rats on the canvas
var rX;
var rY;
   
// keeping the score
var score = 0;       

// to hold the context of div used to display score and level 
var scoreDiv;        

// to check if new rat needs to be placed
var eaten = true;      

// to check if the game is over and enable control to restart the game   
var gameOver = false;  

// to hold the context of div used to display game controls
var controlsDiv;       
</script>

Comments accompanying the variables are self explanatory, for those having any confusion hold on for few minutes till you reach the code where they are used and it should get clear to you. Still have any doubt, ping it to me.

Another neat feature of HTML5, in case you haven't notices till now is <script> tag, which no longer requires to be specified with type attribute.

Now let's have a look a look at couple of drawing APIs for HTML5. Below we define 3 functions:

  1. To draw the canvas boundary in order to mark our playing area
  2. To draw the points (squares) for the snake body
  3. Draw our snake

Code Snippet

/* *************************** /
* In l    =jscriptitially it was meant to mark the canvas boundary
* but this take the background color of the page (black for my blog) 
* so now am filling canvas white
* ************************** */
function drawCanvasBoundary()
{
//set canvas color to be white
context.fillStyle="#FFF";
//draws a rectangle of canvas size filled with white color. 
//This serves as our background
context.fillRect(0,0,width,height);  
context.fill();

/*Draw black boundary if working in white background*/

context.strokeStyle="#000";

context.strokeRect(0,0,width,height)
}
/* *************************** /
* Draws each body part of the snake
* x, y = provides the body position
* ************************** */
function drawPoint(x,y)
{
// First draw a square for size "sqSize" filled with black
context.fillStyle = "#000";
context.fillRect(x,y,sqSize, sqSize);
context.fill();
// Then draw the square boundary in white
context.strokeStyle="#FFFFFF";
context.strokeRect(x,y,sqSize, sqSize);
}
/* *************************** /
* Draws snake by calling the helper drawPoint function
* ************************** */
function drawSnake()
{
for(var i=0; i < snakeLength; i++)
    drawPoint(bodyX[i],bodyY[i]);
}

Here we essentially use 2 functions from the HTML5 APIs - strokeRect and fillRect and customize them using the corresponding style attributes viz. strokeStyle and fillStyle. But what are these functions for? Well as the name suggest, strokeRect is to draw a rectangle and fillRect first draws the rectangle and then fills it with the color specified in its attributes. Does this remind you of anything familiar?? MSPaint??

So over here we have defined that we need a white background for our playarea and snake has to be displayed in black color squares with white borders to give the impression of the digital displays of our older mobiles.

Now that we have written the drawing functions, let us display our canvas and snake.

Code Snippet

/* *************************** /
* Initialize the game variables and the game context
* and then sends the game to the main game loop
* *************************** */
function init()
{
// Get game context
context = document.getElementById("canvas").getContext("2d");
//draws the canvas
drawCanvasBoundary();
//draws snake
drawSnake();
}
// insert the listener for onload event to call our init function
window.addEventListener("load", init, true);

The init function first gets the "game context", 2D in this case (Canvas supports 3D as well!). This context is required to tell our browser, where all the commands needs to be executed. Then we make function calls for the 2 draw functions we previously defined. Lastly, we tell the browser to execute our init function whenever the page gets loaded with the help of addEventListener command. Now reload your page and you should see the following output:

Till now we are just displaying a static output. Now its time to add interactivity and move our snake. Lets define the functions to move snake and the controllers with their behaviors.

Code Snippet

/* *************************** /
* If the rat was eaten, it calculates new rat coordinates,
* check for collision with snake body and places new rat on canvas
* ************************** */
function moveSnake()
{
  for(var i=0; i < snakeLength; i++)
  {
    bodyX[i] += (vX[i]*sqSize);
    bodyY[i] += (vY[i]*sqSize);
  }
 for(var i=snakeLength-1; i>0; i--)
  {
    vX[i] = vX[i-1];
    vY[i] = vY[i-1];
  }
  // eatRat(); -- Lets hide it for time being, we shall address this later
}

/* *************************** /
* Handles keyboard events to control snake
* It only acknowledges the arrow keys and ignores the remaining
* Calculate the new valid direction for snake head
* for instance - if snake is moving right, it cannot move left even
* if left arrow is pressed
* ************************** */
function keydown(e)
{
  //left arrow - Changed to 'a'
  if(e.keyCode == 65 && vX[0] != 1)    
  {
    vX[0] = -1;
    vY[0] = 0;
  }
  //up arrow - changed to 'w'
  else if (e.keyCode == 87 && vY[0] != 1) 
  {
    vY[0] = -1;
    vX[0] = 0;
  }
  //right arrow - changed to 'd'
  else if (e.keyCode == 68 && vX[0] != -1) 
  {
    vX[0] = 1;
    vY[0] = 0;
  }
  //down arrow - changed to 's'
  else if (e.keyCode == 83 && vY[0] != -1) 
  {
    vY[0] = 1;
    vX[0] = 0;
  }
  else if (e.keyCode == 13 && gameOver == true)
  {
    gameOver = false;
    // Lets hide it, we shall address at last what to do when game gets over.
    // restart(); 
  }
}


/* *************************** /
* The update and draw game loop
* ************************** */
function gameProcess()
{
  // Sets the interval for next refresh. Game level defines the 
  // rate of refresh and thereby increase speed with level
  intervalId = setTimeout(gameProcess, 1000/(6*level)); 
 
  clear();
  drawCanvasBoundary();
 
 moveSnake();
 
 drawSnake();
}
/* *************************** /
* Clears the canvas to empty for redrawing
* not an ideal way but then this is just basic
* ************************** */
function clear()
{
  context.clearRect(0,0,width,height);
}

Okay, so we defined a lot of things here... lets take it one by one clear() - As the comments say, this function simply clears the entire canvas which is then redrawn. keydown(e) - Function to handle the key strokes for guiding our snake through the game, currently it only listens to "W,A,S,D" for directions and "Enter" to restart the game after it gets over. Note: I had to change controls from arrow keys to WASD and include a restart key from what I learnt from last blog post - 1. arrow keys were moving the page up n down; and 2. to restart the game, the user had to reload the entire page. moveSnake() - unlike any other object, different parts of Snake's body can move indirectionsdifferent than its head which forces us to keep direction coordinates for each and every body part in the Vx and Vy arrays. The motion is however constrained by that of preceding body part. So the direction vector gets simply copied downwards. gameProcess() or the "Game Loop", every game has this which basically performs - (update-draw), (update-draw),.... repeatedly. The function sets the time for next refresh, clean the canvas, calls moveSnake to calculate the updated coordinates (the update function of game) and finally draws snake (the draw function).

Now, let's refresh the page again... what happened? there is something wrong here... we forgot to initialize the game loop in our init function, so lets do it now.

Code Snippet

//setTimeout calls the game loop i.e. gameProcess function after the specified time
intervalId = setTimeout(gameProcess, 1000/6);
//get handle to the div containing our score and level details
scoreDiv = document.getElementById("score");
//get handle to the div containing our score and level details
controlDiv = document.getElementById("control");
//specify the function to handle the keypress
window.onkeydown = keydown;

Add these 4 commands in the init functions and reload the page. Yippiee!!! our snake is now moving. So where is the RAT?? lets start putting the rats in the game and make our snake eat them.

Code Snippet

/* *************************** /
* If the rat was eaten, calculates new rat coordinates,
* check for collision with snake body and place it on canvas
* ************************** */
function placeRat()
{
      if(eaten)
      {
        rX = Math.floor(width*Math.random()/sqSize)*sqSize;
        rY = Math.floor(height*Math.random()/sqSize)*sqSize;
        if(checkFoodCollision(rX,rY))
          placeRat();
        else
          eaten = false;
      }
      drawPoint(rX, rY);
};    /* *************************** /
* Iterates through all body parts and compares their x& y coordinates
* with those of new Rat location sent as the parameter(x & y)
* ************************** */
function checkFoodCollision(x, y)
{
      for (var i = 0;i<snakeLength; i++)
        if(x == bodyX[i]&& y == bodyY[i])
        {
          return true;
        }
      return false;
}


/* *************************** /
* Checks whether the head has reached the rat
* in case its true, sets flag for calculation of new Rat location
* and calculates and add a body part at the tail increasing the snakeLength
* Thereafter, it increments score and check if level needs to be incremented
* ************************** */
function eatRat()
{
  if(bodyX[0] == rX && bodyY[0] == rY)
  {
    eaten = true;
    // calculate the new X & Y location for tail
    var newX = bodyX[snakeLength-1]-vX[snakeLength-1]*sqSize;
    var newY = bodyY[snakeLength-1]-vY[snakeLength-1]*sqSize;
   
    //Add the new tail part in respective arrays
    bodyX.push(newX);
    bodyY.push(newY);
   
    //Initial velocity of the new part will be same as that of old tail
    //so just copy from last part
    vX.push(vX[snakeLength-1]);
    vY.push(vY[snakeLength-1]);
    snakeLength++;   // increment the snakelength
   
    score += 10;    // increment score
   
    // check and increment level
    if((score%100) == 0)
      level++;
   
    // update score on webpage
    scoreDiv.innerHTML = "Score: " 
    +score+"     Level: "+level;
  }
}

Again lets look at each function one-by-one: placeRat() - This function uses the math library to generate a random position (x,y) coordinate on the canvas. It then checks if Snake's body is not hiding the new Rat position, if it does - it calls for generating fresh set of coordinates otherwise, it sets eaten Rat parameter to be false and draws the new Rat. checkFoodCollision() - Function called by placeRat() to check if the new rat position is not hidden under snake's body eatRat() - Checks if Snake's head is at the location of Rat. If yes, it raise the rat is eaten flag to tell our program to generate new rat position. Then it increments snake length and add new body part at the end. Finally it increments score and determine if the level needs to be increased as well, and then display both of them.

Also unhide the call to "eatRat()" from moveSnake() function and Add, the call to placeRat() to gameProcess() function

Our game of snake is almost ready, but for one last bit of work. Currently if you try to move the snake past the boundary, it will go and disappear from the skin. Also try folding snake on itself, it will do because we haven't worked on the collision/termination conditions. Lets do it now!

Code Snippet

/* *************************** /
* Checks snake colliding with the boundary walls
* Snake can collide with itself only if its length is 5
* else if condition checks for snake length and calls for self collision check
* ************************** */
function checkCollision()
{
  if(bodyX[0] >= width || bodyX[0] < 0 || bodyY[0] < 0 || bodyY[0] >= height)
  {
    scoreDiv.innerHTML = "Score: " 
    +score+"     Level: "
        +level+"     <b>Game Over</b>";
    controlDiv.innerHTML = "Press \"Enter\" to restart";
    gameOver = true;
    clearTimeout(intervalId);
  }
  else if(snakeLength > 4)
    {
      if(checkSelfCollision(bodyX[0],bodyY[0]))
      {
        scoreDiv.innerHTML = "Score: " 
        +score+"     Level: "
        +level+"     <b>Game Over</b>";
        controlDiv.innerHTML = "Press \"Enter\" to restart";
        gameOver = true;
        clearTimeout(intervalId);
      }
    }
}
   
/* *************************** /
* Iterates through all body parts starting from 5
* and compares their x 
& y coordinates with that of head sent as the parameter(x & y)
* ************************** */
function checkSelfCollision(x, y)
{
  for (var i = 4; i < snakeLength; i++)
    if(x == bodyX[i] && y == bodyY[i])
    {
      return true;
    }
  return false;
}

We need to check for 2 different types of collision, one is snake colliding with the boundary walls and the other being, self collision.

  • checkSelfCollision() - This function iterates over the body parts starting from 5 (why?) and checks whether the snake's head has not collided with the body part or not.
  • checkCollision() - Checks if the snake has collided with the boundary, if not calls for checking selfCollision

Now, just add a call to checkCollision before we drawSnake() in the gameProcess() function, so that the final gameProcess function looks like this

Code Snippet

function gameProcess()
{
 intervalId = setTimeout(gameProcess, 1000/(6*level)); 

 clear();
 drawCanvasBoundary();
 placeRat();
 moveSnake();
 checkCollision();
 drawSnake();
}

Lastly, lets enable the control to restart the game when it gets over. Unhide the call to restart() function from keydown() function (look at the end where we handle case for keycode 13 i.e. "Enter Key") and then add then define the restart function as below -

Code Snippet

/* *************************** /
* Restart the game
* ************************** */
function restart()
{
    bodyX = new Array(150, 150-sqSize, 150-2*sqSize);
    bodyY = new Array(200, 200, 200);

    vX = new Array(1, 1, 1);
    vY = new Array(0, 0, 0);

    snakeLength = 3;

    score = 0;
    level  = 1;

    eaten = true;

    scoreDiv.innerHTML = "Score: " +score+"Level: "+level;
    controlDiv.innerHTML = "Controls: W = Up; A = Left; S = Down; D = Right";

    intervalId = setTimeout(gameProcess, 1000/6);
}

And we are done!!! Complete source code can be obtained from here Enjoy the game here and do write your suggestions/comments 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Aniruddha Loya
Student EPFL
Switzerland Switzerland
After almost 4 years of experience varying from finance to building & running a startup... I decided to take a break, go back to college and be a (academic) student again.
However, I continue to do freelance work and am currently working on HTML5 games for Code-Heads, a UK based studio.
http://aniruddhaloya.blogspot.com
Follow on   Twitter

Comments and Discussions

 
QuestionNice so far but lots of typos PinmemberTravis Stewart18-Oct-13 14:48 
AnswerRe: Nice so far but lots of typos PinmemberAniruddha Loya19-Oct-13 9:03 
GeneralMy vote of 5 PinmemberErnesto Oltra13-Aug-12 0:20 
GeneralRe: My vote of 5 PinmemberAniruddha Loya13-Aug-12 1:00 
GeneralRe: My vote of 5 PinmemberErnesto Alejo Oltra13-Aug-12 1:26 
GeneralRe: My vote of 5 PinmemberAniruddha Loya13-Aug-12 3:27 
GeneralRe: My vote of 5 PinmemberErnesto Alejo Oltra13-Aug-12 5:33 
GeneralMy vote of 5 PinmemberChristian Amado10-Aug-12 3:09 
GeneralRe: My vote of 5 PinmemberAniruddha Loya10-Aug-12 5:27 
SuggestionCode please Pinmembervivek poomala9-Aug-12 19:53 
GeneralRe: Code please PinmemberAniruddha Loya10-Aug-12 2:11 
SuggestionRe: Code please PinmemberSlyDolphin14-Aug-12 4:18 
GeneralRe: Code please PinmemberAniruddha Loya14-Aug-12 5:16 
GeneralMy vote of 5 Pinmembervishakhakhadse24-Apr-12 20:03 
GeneralRe: My vote of 5 PinmemberAniruddha Loya25-Apr-12 0:59 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140827.1 | Last Updated 10 Aug 2012
Article Copyright 2011 by Aniruddha Loya
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid