Drawing a Tile Map - Canvas & Javascript

Creating our Tile Map

In this series of lessons we'll look over some of the basics of creating tile-based game engines. We'll do this using Javascript and a Canvas element in a webpage. Besides being a perfectly valid platform in itself, this allows us to quickly prototype ideas without the need for compiler suites or any special software. Just a plain text editor and a modern browser, such as Firefox, is all you'll need.

View example

We'll begin this lesson by creating a simple HTML document with a Script tag in the head, and a 400x400 pixel Canvas element in the body. If you're not very familiar with HTML, don't worry too much - this is all the markup we'll need for now:


<!DOCTYPE html>
<html>
<head>

<script type="text/javascript">

</script>

</head>
<body>

<canvas id="game" width="400" height="400"></canvas>

</body>
</html>

Save this as a .htm file and launch it in your browser. This will be the canvas we'll be drawing our map to.

Setting up

Our next step is to create several global variables inside the Script tags; these variables configure our settings (tile width and height with tileW, tileH, map dimensions with mapW, mapH), store our map (gameMap), store and calculate information about the frame rate (currentSecond, frameCount, framesLastSecond), and provide a persistent reference to our Canvas drawing context (ctx):


var ctx = null;
var gameMap = [
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 1, 1, 1, 0, 1, 1, 1, 1, 0,
	0, 1, 0, 0, 0, 1, 0, 0, 0, 0,
	0, 1, 1, 1, 1, 1, 1, 1, 1, 0,
	0, 1, 0, 1, 0, 0, 0, 1, 1, 0,
	0, 1, 0, 1, 0, 1, 0, 0, 1, 0,
	0, 1, 1, 1, 1, 1, 1, 1, 1, 0,
	0, 1, 0, 0, 0, 0, 0, 1, 0, 0,
	0, 1, 1, 1, 0, 1, 1, 1, 1, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0
];
var tileW = 40, tileH = 40;
var mapW = 10, mapH = 10;
var currentSecond = 0, frameCount = 0, framesLastSecond = 0;

Hopefully a lot of this is intuitive - we're setting our tiles to be 40x40 pixels, and our map to be 10x10 tiles. The gameMap is an array of the two types of tile we'll be using to start with; solid (0) and path (1). For the sake of readability we're laying it out in the same way it'll be drawn to the screen, with 10 rows of 10 columns.

Getting the loop started

Ideally, you want to let the browser tell you when it's ready for you to draw to the canvas. The easiest way to do this is to use the requestAnimationFrame function and designate a 'callback' - a function we want executed when the browser is ready.

We do this by creating a function to be run when the page is done loading. We'll also use this function to assign our drawing context for the Canvas to the ctx variable, and in this case set the Canvas font, because we don't currently need to change this once we've set it up.


window.onload = function()
{
	ctx = document.getElementById('game').getContext("2d");
	requestAnimationFrame(drawGame);
	ctx.font = "bold 10pt sans-serif";
};

The main function

Our main function will do several things: we'll use it to count the number of frame (frame rate) per second; it will draw the map itself; the frame rate will be drawn, and finally we'll request that the function be called again when the browser is ready.

We'll open the function, and do a basic check to see our Canvas drawing context exists. If it doesn't, we exit out of the function and give up:


function drawGame()
{
	if(ctx==null) { return; }

Next, we do a really simple framecount. We see which second it currently is in Unix Time, and if it's the same one as it was last frame we add to the frame count. If not, we set the framesLastSecond to the current frame count, reset the frame count to 0, and update the current second:


	var sec = Math.floor(Date.now()/1000);
	if(sec!=currentSecond)
	{
		currentSecond = sec;
		framesLastSecond = frameCount;
		frameCount = 1;
	}
	else { frameCount++; }

Now we begin our main drawing loops!

To draw our map, we'll be using 2 nested loops; our outer loop, or y loop, goes down the map row by row, whilst our inner loop, or x loop, goes from left to right over each column within the current row:


	for(var y = 0; y < mapH; ++y)
	{
		for(var x = 0; x < mapW; ++x)
		{

To draw the current tile of the current row, we'll use the calculation (y x [map width]) + x) to find the index of the current tile in our gameMap array. We'll then use a simple switch statement to decide in which colour the tile will be drawn (depending on the value in the gameMap array at the specific index), and draw a filled rectangle of the dimensions we'd previously set in the tileW and tileH variables, with an offset of x x [tile width], y x [tile height]:


			switch(gameMap[((y*mapW)+x)])
			{
				case 0:
					ctx.fillStyle = "#000000";
					break;
				default:
					ctx.fillStyle = "#ccffcc";
			}

			ctx.fillRect( x*tileW, y*tileH, tileW, tileH);

...and close our tile drawing loops:


		}
	}

Finally, let's show the number of frames drawn last second with red text (#ff0000), and tell the browser to run this function again when it's ready to draw another frame on the Canvas, and close the function.


	ctx.fillStyle = "#ff0000";
	ctx.fillText("FPS: " + framesLastSecond, 10, 20);

	requestAnimationFrame(drawGame);
}

If you save this file and open it in your browser, you should get something like the following image:

Page loaded in 0.013 second(s).