Creating Games with HTML5 Canvas Part 3: Controls and Objects

In this part of the tutorial we will add the snake but before we do that we’ll create a object to handle the controls of the game. In the classical snake game you turn the snake clockwise or counterclockwise. We’ll use the left and right key for this.

The first step is to create a key object to keep track of if a certain key is pressed or released. We’ll use the pressedAndReleased method to keep track of if a key is pressed and has been released since last time we checked. If the method is called and the key is pressed we set hasBeenReleased to false, this way we know that the key has been released. We’ll reset this property later in the keyup event handler. The reason is that if we only check if it’s pressed the snake will just rotate in a circle every time the game ticks and a key is pressed. By using the pressedAndReleased method we have to release the key and press it again before we can turn.

// Key object
var Key = function(keyCode) {
	// This keys key code
	this.keyCode = keyCode;
	// Is this key pressed
	this.pressed = false;
	// Has this key been released
	this.hasBeenReleased = true;

	// Returns true if this key is pressed and has been released since the method was called last time
	this.pressedAndReleased = function() {
		var pressedAndReleased = this.pressed && this.hasBeenReleased;

		if(this.pressed) {
			this.hasBeenReleased = false;
		}

		return pressedAndReleased;
	}
};

Now let’s add a Control object to handle keys and key events. Controls has two key properties: left and right. The init method adds handlers for the keyup and keydown events. The keydown handler sets the current key as pressed and the keyup handler sets the key as not pressed and, as mentioned earlier, sets hasBeenReleased to false. This way we keep track of if the key has been released since we last check if it was pressed. The init method will be called before the game starts to set up the key event handlers.

// Controls object
var Controls = {
	// Keys used in the game
	keys: {
		left: new Key(37),
		right: new Key(39)
	},
	// Init method - sets keydown and keyup handlers
	init: function() {
		var _this = this;

		// Set keys as pressed
		document.onkeydown = function(e) {
			var keyCode = e.keyCode; 

			if(keyCode === 37) {
				_this.keys.left.pressed = true;
			}
			if(keyCode === 39) {
				_this.keys.right.pressed = true;
			}
		};

		// Set keys as not pressed
		document.onkeyup = function(e) {
			var keyCode = e.keyCode; 

			if(keyCode === 37) {
				_this.keys.left.pressed = false;
				_this.keys.left.hasBeenReleased = true;
			}
			if(keyCode === 39) {
				_this.keys.right.pressed = false;
				_this.keys.right.hasBeenReleased = true;
			}
		};
	}
};

It’s time to add the snake to the game. The snake’s body will consist of several objects so the first step is to create a base Object. It has three properties: x and y which represent the objects position and color. Since all objects in the game has the same size we won’t bother with width and height. Object also has four methods: The logic and draw method will be called from the games logic and draw methods, the erase method that fills the objects area with the background color and the collision method that checks if the object has the same location as the supplied x and y values.

// Basic object to represent a rectangle on the screen
var Object = function(x, y, color) {
	// x position
	this.x = x;
	// y position
	this.y = y;
	// Fill color of the rectangle
	this.color = color;

	// Logic method - this is where we handle controls and check for collisions
	this.logic = function() {};

	// Draws object on the canvas
	this.draw = function(context) {
		context.fillStyle = this.color;
		context.fillRect(this.x, this.y, Settings.objSize, Settings.objSize);
	};

	// Erases object on the canvas and replaces it with the background color
	this.erase = function(context) {
		context.fillStyle = Settings.backgroundColor;
		context.fillRect(this.x, this.y, Settings.objSize, Settings.objSize);
	};

	// Check if this objects x and y position matches the supplied x and y position
	this.collision = function(x, y) {
		return this.x === x && this.y === y;
	};
};

The Snake object inherits the Object properties and methods and also has a few of it’s own. The direction property keeps track of the snake’s current direction, bodyObjects is an array of all the Objects that represents snake body. When the snake is created we create Objects and add them to the bodyObjects array. The size of the snake is determined by the size variable. We also call the apply method of Object to set up properties inherited from Object (x, y and color).

The Snake object has a method called setDirection which is used to set a new direction depending on the supplied number. The number will be -1 or 1 depending on if we want to turn clockwise or counterclockwise.

We override logic and draw inherited from object. The logic method checks if the left or right key is pressed and released and sets a new direction. The snake’s position is then changed depending on the direction.
The draw method adds a new object at the snake’s position and calls it’s draw method to display it on the screen and then erases the last object of the snake. This way the snake moves forward.

// Snake object
// inherits Object
var Snake = function(x, y, color, size) {
	// Possible directions of the snake
	var directions = [ 'left', 'up', 'right', 'down' ];

	// The current direction
	this.direction = 'right';
	// The snakes body
	this.bodyObjects = [];

	// Add the snakes body objects
	for(var bodySize = size - 1; bodySize >= 0; bodySize--) {
		this.bodyObjects.push(new Object(x - bodySize * Settings.objSize, y, Settings.snakeColor));
	}

	// Call the base object to initalize inherited settings
	Object.apply(this, arguments);

	// Sets a new direction depending on the supplied direction and number (-1 or 1 depending on which way to turn)
	this.setDirection = function(n) {
		// Get index of the current direction
		var index = directions.indexOf(this.direction);

		// Get the new index
		index += n;

		// Reset to first or last index if the index is out of bounds of the array
		if(index >= directions.length) {
			index = 0;
		} else if(index < 0) {
			index = directions.length - 1;
		}

		this.direction = directions[index];
	};

	// Logic method - this is where we handle controls and check for collisions
	this.logic = function() {

		// Check pressed keys and change direction
		if(Controls.keys.left.pressedAndReleased()) {
			this.setDirection(-1);
		} else if(Controls.keys.right.pressedAndReleased()) {
			this.setDirection(1);
		} 

		// Set new position from direction
		switch(this.direction) {
			case 'left':
				this.x -= Settings.objSize;
				break;
			case 'up':
				this.y -= Settings.objSize;
				break;
			case 'right':
				this.x += Settings.objSize;
				break;
			case 'down':
				this.y += Settings.objSize;
				break;
		};
	};

	// Overrides draw method - Draw all the snakes body objects on the screen
	this.draw = function(context) {

		var newObj = new Object(this.x, this.y, Settings.snakeColor), // Get a new object at the snakes current position
			objToRemove = this.bodyObjects.shift(); // Remove the last item

		// Add the new object to the snakes body
		this.bodyObjects.push(newObj);

		// Draw the new object on the screen
		newObj.draw(context);

		// Erase the last object
		objToRemove.erase(context);
	};

};

// Snake inherits Object
Snake.prototype = new Object;

Now all we need to do is add a few settings and make a few changes to the Game object created in the previous part. In the init method we’ll create the snake.

var Game = {
	canvas: null, // Reference to the canvas element
	context: null, // Reference to the context
	interval: null, // Reference to the main game loop
	score: 0, // The current score
	snake: null, // Reference to the snake

	// Init method to set up the game
	init: function(canvasId) {
		// Set start position at center of the canvas
		var startX = Settings.canvasSize / 2,
			startY = Settings.canvasSize / 2;

		// Set up canvas
		this.canvas = document.getElementById(canvasId);
		this.context = this.canvas.getContext('2d');
		this.canvas.width = Settings.canvasSize;
		this.canvas.height = Settings.canvasSize;

		// Set up context
		this.context.fillStyle = Settings.backgroundColor;
		this.context.fillRect(0, 0, Settings.canvasSize, Settings.canvasSize);

		// Create the snake
		this.snake = new Snake(startX, startY, Settings.snakeColor, 4);
	},
// ...
};

In the logic method we call the snake’s logic method and in the draw method we call the snake’s draw method.

// The logic method - this is where we handle controls and check for collisions
logic: function() {
	this.snake.logic();
},

// Draws all objects on the canvas
draw: function() {
	this.snake.draw(this.context);
},

View demo and final code here.

In the next part we’ll add collision detection and some food for the snake to eat.

Creating Games With HTML5 Canvas Part 2: The Game Loop

In the previous example we used  the onkeydown event to animate objects. This works fine in many cases but when you want to be able to control the speed of the game an keep things nice you probably want to create a game loop. Let’s create an object called Game to handle the loop.

var Game = {
	canvas: null, // Reference to the canvas element
	context: null, // Reference to the context
	interval: null, // Reference to the main game loop
	score: 0, // The current score

	// Init method to set up the game
	init: function(canvasId) {
		// ...
	},

	// The logic method - this is where we handle controls and check for collisions
	logic: function() {
		// ...
	},

	// Draws all objects on the canvas
	draw: function() {
		// ...
	},

	// The main loop
	main: function() {
		this.logic();
		this.draw();
	},

	// Starts the game by calling the main method with setInterval
	start: function(canvasId) {
		var _this = this;
		_this.init(canvasId);
		_this.interval = setInterval(function() { _this.main(); }, 100);
	}
};

As you can see the Game object has five methods:

  • The init method is where we will do everything we need to do before the game stars.
  • The logic method is where we check for collisions and handle movement.
  • The draw method will draw all the different objects on the screen.
  • The main method is the main loop of the game. It basically calls the logic method to update all objects and then calls draw to display them on the screen.
  • The start method is called when we want to start the game. It calls the init metod to set up the game and then calls the main method with an interval of 100 milliseconds. If you take a look at the setInterval method you will see I do a little odd trick there. If I would simply call this.main we would lose our scope and this would no longer be a reference to the Game object but the global window object. By calling _this.main and wrap it in a function we create a closure and we keep the reference to our Game object.

We’ll have a closer look at the init method but before we do that let’s create a Settings object for some of our global settings.

var Settings = {
	canvasSize: 400, // Size of the canvas, same for with and height
	backgroundColor: 'black', // Background color of the context
};

Then we add some code to the init method to set up the canvas element and fill it with the background color.

init: function(canvasId) {
	this.canvas = document.getElementById(canvasId);
	this.context = this.canvas.getContext('2d');
	this.canvas.width = Settings.canvasSize;
	this.canvas.height = Settings.canvasSize;

	this.context.fillStyle = Settings.backgroundColor;
	this.context.fillRect(0, 0, Settings.canvasSize, Settings.canvasSize);
}

And finally we call the start method and pass the ID of a canvas element on the page as argument to run the game. A black rectangle should be displayed on the page. Amazing!

Game.start('game-canvas');

View the demo and have a look at the final code here.

In the next part is where the fun starts. We’ll create a Control object and add the snake.

Creating Games With HTML5 Canvas Part 1: Drawing and animating

In this tutorial we will create the classic game Snake with the HTML5 canvas element. This is the first part where I’ll go through the basics of drawing with the canvas element and also how to create simple animations.

The canvas element was added in HTML5 and it can be used for rendering graphic elements on the fly. Start of by adding a canvas element to your page.

<canvas id="game-canvas" width="300" height="300"></canvas>

The canvas element has different contexts that are used for drawing. We will use the 2D context. You can access the context like this:

var canvas = document.getElementById('game-canvas');
var context = canvas.getContext('2d');

Now that we have a context it’s time to draw something on it, let’s make it a simple rectangle. To set the visual style of the rectangle we need to use the fillStyle property of our context and specify a color. You can use several different formats for the color like RGB, hex or just a name. We use the fillRect method to draw the rectangle and pass x position, y position, width and height as arguments.

context.fillStyle = 'red';
context.fillRect(20, 20, 100, 100);

Simple enough, let’s move on to animating. We’ll use the DOM onkeydown event to animate the rectangle when we press the arrow keys. The x and y value are changed depending on which button that is pressed. After the x and y values are changed we have to check that they are inside the canvas area to prevent the rectangle from moving outside of the canvas.
The onkeydown handler will be called over and over again for as long as a button is pressed.

var canvas = document.getElementById('game-canvas'), // Our canvas element
    context = canvas.getContext('2d'), // 2D context of the canvas
	x = 50, // x position of the rectangle
    y = 50, // y position of the rectangle
    rectSize = 100, // size of the rectangle
    canvasSize = 300, // size of the canvas
    speed = 10; // animation speed of the rectangle

document.onkeydown = function(e) {
    // switch the pressed keys code to determine which direction to move
    switch(e.keyCode) {
        // left
        case 37:
            x -= speed;
            break;
        // up
        case 38:
            y -= speed;
            break;
        // right
        case 39:
            x += speed;
            break;
        // down
        case 40:
            y += speed;
            break;
    }

    // make sure we cannot move outside the canvas area
    // left side
    if(x < 0) {
        x = 0;
    }

    // right side
    if(x + rectSize > canvasSize) {
        x = canvasSize - rectSize;
    }

    // top
    if(y < 0) {
        y = 0;
    }

    // bottom
    if(y + rectSize > canvasSize) {
        y = canvasSize - rectSize;
    }

    // clear and draw the rectangle
    drawRect();
}

We create a method that clears the context by calling the clearRect method and then draws the rectangle. If you want to optimize performance you should clear only the area that is changed (the previous position of the rectangle) but clearing the entire context will do fine in this example.

function drawRect() {
    // clear the entire context
    context.clearRect(0, 0, canvasSize, canvasSize);

    // draw the rectangle at x,y position
    context.fillStyle = 'red';
    context.fillRect(x, y, rectSize, rectSize);
}

And we are done. View the demo and have a look at the final code here.

If you want to read more about the Canvas element I would recommend Mozilla’s Canvas tutorial.

In the next part we’ll create the main game loop.

jQuery.disable() plugin

This is a plugin that is used to disable form elements. Options can be passed to the disable method to determine when the element should be enabled again.

Examples:

// Simple usage
jQuery('#myButton').disable();

// Disable element and set a css class
jQuery('#myButton').disable({ cssClass: 'disabled' });

// Disable element and enable after X seconds
jQuery('#myButton').disable({ enableAfterSeconds: 5 });

// Disable element and enable once an AJAX call is completed
jQuery('#myButton').disable({ enableOnAjaxComplete: true });

// Disable element and enable once an AJAX call is successful
jQuery('#myButton').disable({ enableOnAjaxSuccess: true });

// Disable element and enable once an AJAX call is successful and returns a specific text
jQuery('#myButton').disable({ enableOnAjaxSuccess: true, responseText: 'great success' });

// Disable element and enable once an AJAX call is successful and restrict to a specific URL
jQuery('#myButton').disable({ enableOnAjaxSuccess: true, ajaxUrl: 'http://my.ajax.service/' });

View demo

Download jQuery.disable() plugin version 1.0

Stockholm Half Marathon 2011

Yesterday I ran the Stockholm Half Marathon. It was the first time I participated in the race and my time goal was 1 hour and 35 minutes.

The weather was really nice, perfect temperature and I felt strong the whole race. I had some cramps in my belly for a while after drinking to fast but I managed to tough it out and finished the race in 1:32:06. And by that I beat my old record by 8 minutes and 30 seconds.

Using HTML data attribute as configuration

Lately when I’ve been working with HTML5 I’ve started to use the data attribute as configuration for JavaScript modules (a Google map for instance). Often you need to pass values from the back end to these modules. If there’s a lot of data you might want to fetch the data from a web service with AJAX but in many cases it will be faster to just render them on the page.

The basics

The data attribute is a custom attribute added in HTML5 where you can store any additional information you need for an element. It’s used like this:

<a data-mydata="Hello world"></a>

Values can then be accessed with JavaScript like this:

var a = document.getElementsByTagName('a')[0];
var myData = a.getAttribute('data-mydata');
// or like this (doesn't work in older browsers)
var myData = a.data.mydata;

jQuery.data() and data attribute

jQuery’s data method now maps against HTML data attributes. You can access data attributes with jQuery like this:

var myData = $('a').data('mydata');
// or like this
var data = $('a').data();
var myData = data.mydata;

Using data attribute as configuration

Let’s say I want to display a google map on my site. But it should display slightly different on each page. I could use the data attribute on the map element to configure the map display like this:

<div class="map" data-showcontrols="true" data-lat="-34.397" data-lng="150.644" data-zoom="7"></div>

And then get the settings with jQuery and display the map:

$('div.map').each(function() {
	var data = $(this).data(), // Get the data from this element
		options = { // Create map options object
			center: new google.maps.LatLng(data.lat, data.lng), // Set center from the specified lat and lng
			disableDefaultUI: data.showcontrols || false, // Show or hide default controls
			zoom: data.zoom || 10, // Set zoom level from specified zoom or default to 10
			mapTypeId: google.maps.MapTypeId.ROADMAP
		};

	// Create and display the map
	var map = new google.maps.Map(this, options);
});

View demo.

CSS3/jQuery Slideshow

I’ve been working on a simple slideshow that uses CSS3 transitions and transformations. You can see a demo of it here (works best in Firefox).

This is what the HTML looks like.

<ul class="album">
    <li><img src="http://flickholdr.com/400/280/london" /></li>
    <li><img src="http://flickholdr.com/400/280/losangeles" /></li>
    <li><img src="http://flickholdr.com/400/280/tokyo" /></li>
    <li class="active"><img src="http://flickholdr.com/400/280/newyork" /></li>
</ul>

Here’s the CSS. Each list item is rotated 5 degrees. The active list item is set to 0 degrees. Then we have the “slide” class that uses the translate transformation to move the item 150 pixels down and to the right. I also set the opacity to 0 to fade out the image.

.album {
    position: relative;
    margin: 0;
    padding: 0;
    list-style: none;
}
.album li {
    margin: 0;
    padding: 0;
    position: absolute;
    z-index: 1;
    border: 4px solid #fff;
    -ms-transform:rotate(5deg);
    -webkit-transform:rotate(5deg);
    -moz-transform:rotate(5deg);
    z-index: 100;
    opacity: 1;
}
.album li.active {
    -moz-transition: all 0.5s ease-in;
    -webkit-transition: all 0.5s ease-in;
    transition: all 0.5s ease-in;
    -webkit-transform:rotate(0deg);
    -moz-transform:rotate(0deg);
    transform:rotate(0deg);
    z-index: 200;
}
.album li.slide {
    -moz-transition: all 1s ease-in;
    -webkit-transition: all 1s ease-in;
    transition: all 1s ease-in;
    -moz-transform: translate(150px,150px);
    -webkit-transform: translate(150px,150px);
    transform: translate(150px,150px);
    opacity: 0;
    z-index: 300;
}
.album li img {
    display: block;
}

This is the jQuery code which basically adds the class “slide” to the clicked item and the class “active” to the next item.

$(function() {
    $('ul.album li.active').live('click', function() {
        /* There's a reason i call the prev item "next" and last "first".
        I'm doing everything backwards since the last item has the highest z-index */
        var $this = $(this), // the clicked item
            $next = $this.prev('li'), // the next item
            $firstItem = $this.siblings().last(); // the first item

        // Reset if we are at the last item
        if(!$next.length) {
            $next = $firstItem;
        }

        // Throw away the clicked item
        $this.addClass('slide').removeClass('active');

        // Display the next item
        $next.addClass('active');

        // Reset the clicked item after 1 second
        setTimeout(function() { $this.removeClass('slide'); }, 1000);
    });
});

Flipping images with HTML canvas

I’ve put together a demo of how you can flip images with the HTML5 canvas element over at jsFiddle.

The basic idea is to set the scale of the canvas to -1 in the direction we want to flip the image. This will draw the image inverted but the image will be outside of the visible canvas area. So we need to set the position of the image to -100% of the image width or height, depending on which direction we want to flip it.

This is the basic function that rotates the image horizontally and vertically.

function flipImage(image, ctx, flipH, flipV) {
    var scaleH = flipH ? -1 : 1, // Set horizontal scale to -1 if flip horizontal
        scaleV = flipV ? -1 : 1, // Set verical scale to -1 if flip vertical
        posX = flipH ? width * -1 : 0, // Set x position to -100% if flip horizontal
        posY = flipV ? height * -1 : 0; // Set y position to -100% if flip vertical

    ctx.save(); // Save the current state
    ctx.scale(scaleH, scaleV); // Set scale to flip the image
    ctx.drawImage(img, posX, posY, width, height); // draw the image
    ctx.restore(); // Restore the last saved state
};

View the demo.