May 07

Stratiscape – A layered approach to HTML5 Canvas drawing

When I created my first HTML5 game I was surprised that the canvas element in HTML5 didn’t support multiple layers that could be painted on independently. Independent stacked layers would help in many situations when animating only a portion of a game (like the main character) so that the system doesn’t have to repaint backgrounds or other elements that remain mostly static. One way to get around this limitation is to have multiple stacked canvas elements that are painted independently and serve the same layered functionality I wanted. There are some canvas libraries that already support layers, but many of them were very heavy and added much more functionality than I needed for my game. That’s when I decided to build Stratiscape, a simple HTML5 layered canvas library.

Using the Stratiscape library is straightforward, it is a matter of creating an instance of the main Stratiscape class, defining one or more drawn objects for your canvases and then adding those objects to a stratiscape layer, and finally setting up the drawing interval. This article will show you how to build a simple animated stratiscape page, in the background we’ll draw a blue rectangle, and in the foreground we’ll have some circles that will bounce around inside the rectangle.

To create an object of the Stratiscape class, we pass in a config object that defines how we want the layers created:

document.stratiscapeDraw = new Stratiscape({'containerId':'canvasContainer',
		'layers':[
			{'name':'canvasBackground', x:0, y:0, width:640, height:480, 'backgroundColor':'black'},
			{'name':'canvasForeground', x:0, y:0, width:640, height:480}
		]
	});

In our config object, one of the required properties is ‘containerId’ which should be the id of the element we want all of our generated canvas elements to reside, so we’ll want to make sure our page has a div with an id that we specified in our config object: “canvasContainer” like this:

Next let’s figure out what we want to draw and how we’ll draw it, to do this in Stratiscape, we define a subclass of the DrawnObject class and we should at least define two methods init and draw.

Here is a simple example of a “Rectangle” drawn object, the init function takes some position and size parameters as well as a couple of colors to use when we draw it.

Rectangle = Stratiscape.DrawnObject.extend({ //rectangle drawn object class

	init: function(x, y, width, height, fillColor, strokeColor) {
		this.x = x;
		this.y = y;
		this.width = width;
		this.height = height;
		this.fillColor = fillColor;
		this.strokeColor = strokeColor;
	},

	draw: function(ctx) {

		ctx.fillStyle = this.fillColor.toString();
		ctx.strokeStyle = this.strokeColor.toString();
		ctx.fillRect(this.x,this.y,this.width,this.height);
		ctx.strokeRect(this.x,this.y,this.width,this.height);
	}
});

Notice the draw method is passed a “ctx” object, which is the 2d context for the layer (canvas) that the Rectangle is a child of.

In order to make our stratiscape object actually draw, we’ll need to periodically call the main draw method on the stratiscape object like this:

window.setInterval(function() {stratiscapeDraw.draw() }, 1000 / 60);

Now all we have to add instances of our Rectangle Drawn Object to a layer of our stratiscape object and the library will take care of the rest.

document.stratiscapeDraw.getLayer('canvasBackground').addChild(new Rectangle(10, 20, 200, 150, "#369", "#FFF"));

The results of this are a bit uninteresting:

Let’s make our sample a bit more exciting and leverage the fact that we have two independent layers, lets make another class inheriting from drawn object, a circle:


Circle = Stratiscape.DrawnObject.extend({ //circle drawn object class

	init: function(x, y, radius, fillColor, strokeColor, velocity, angle) {
		this.x = x;
		this.y = y;
		this.radius = radius;
		this.fillColor = fillColor;
		this.strokeColor = strokeColor;
		this.velocity = velocity;
		this.angle = angle;
	},

	draw: function(ctx) {

		ctx.strokeStyle = this.strokeColor;
		ctx.fillStyle = this.fillColor;
		ctx.lineWidth = 1;
		ctx.beginPath();
		ctx.arc(this.x,this.y,this.radius,0,Math.PI*2,true);
		ctx.closePath();
		ctx.fill();
		ctx.stroke();
	}
});

Notice that we’ve given the circle class a velocity and angle, this is because we want to have our circles bouncing around our canvas layer. Let’s add an update method to the circle class that we’ll call repeatedly to change the circle’s location based on the angle and velocity.

    update: function() {
		var offsetX = 0;
		var offsetY = 0;
		if(this.velocity != 0)
		{
			offsetX = this.velocity * Math.cos(this.angle * Math.PI/180);
			offsetY = this.velocity * Math.sin(this.angle * Math.PI/180);
		}

		this.x -= offsetX;
		this.y -= offsetY;

		//check for collisions with our rectangle
		var top = 15 + this.radius, left = 20  + this.radius, bottom = 465 - this.radius, right = 620 - this.radius;
		if(this.x <= left) {
			this.x = left;
			this.angle += this.angle < 90 ? 90 : -90;
		} else if(this.x >= right) {
			this.x = right;
			this.angle += this.angle > 180 ? 90 : -90;
		} else if(this.y <= top) {
			this.y = top;
			this.angle += this.angle > 90 ? 90 : -90;
		} else if(this.y >= bottom) {
			this.y = bottom;
			this.angle += this.angle > 270 ? 90 : -90;
		}

		if(this.velocity <= 0)
			this.layer.removeChild(this);

		if(this.angle > 360)
			this.angle = this.angle%360;
		if(this.angle < 0)
			this.angle = 360 + this.angle;
	},

Now all we have to do is add some of these circles to our foreground layer and setup our interval to call the update method on each circle.

document.canvasForeground = document.stratiscapeDraw.getLayer('canvasForeground');

for(var i = 9; i >= 0; i--)
{
	var radius = (i + 1) * 5;
	document.canvasForeground.addChild(new Circle( 100 + radius, 75 + radius, radius, "#008000", "#FF0", 100/radius, (45 + (i * 90))%360));
}

setInterval(function()
		{
			for(var i in document.canvasForeground.children)
			{
				document.canvasForeground.children[i].update();
			}
			document.canvasForeground.needsDisplay = true;//set "dirty" flag
		}, 1000/30);

Notice that after calling the update method on each of the foreground layer's children, I need to set the "needsDisplay" flag to true on the canvas layer. This tells Stratiscape that changes have been made to the layer and that it must be re-drawn. Stratiscape will set the needsDisplay flag for you whenever adding or removing a drawn object from the layer, however, because we are just moving our objects around we must explicitly tell the layer that updates have been made and that it should re-draw itself on the next draw cycle. We could repaint a simple scene like this as often as we like and we would likely not see any performance degradation. However, for large, complex scenes where repainting might be more intensive, only drawing when objects have changed is an important optimization to make games and animation run smoothly.

Here is our full simple sample in action:

Download the full source for this sample here: Stratiscape simple sample
Checkout the other Stratiscape library samples and download the full source from github here:
Stratiscape - Layered Canvas Library

Apr 29

HTML5 Canvas Drawing Speed Testing using Stats.js and jsperf.com

Lately I’ve been doing some performance testing of different canvas drawing functions, I specifically wanted to test the performance of drawing radial gradients vs. arcs and images.

To facilitate my testing I used Stats.js – a graphical performance monitor you can easily add to your sites to test milliseconds between javascript calls and frames per second.

Another great resource for testing javascript performance is jsPerf.com, to create a test you simply add your setup and teardown code and then specify the code you want to test the performance of.

You can see my Canvas Draw Tests jsPerf, it gives you a nice graph of the historic runs by browser.
Feel free to run your own test using your favorite browser.

HTML5 Canvas Drawing Speed Tests

I’ve also included the code and test page I wrote here so you can try it out for yourself and tweak the test for your own needs if you like.



Sorry your browser doesn’t support the HTML5 canvas!





Results

I’m not done with my tests, I’d like to do further investigating and compare the performance of drawing paths and other shapes as well. At this point, it seems image drawing is across the board faster. I was actually surprised that in some browsers it seems that drawing radial gradients is faster than drawing a filled circle (arc). As I work on my next HTML5 game these performance testing methods will be helpful to figure out how to keep the game running at high frame rates. Another good resource for tricks to keep canvas drawing as fast as possible is the very good tutorial over at HTML5 Rocks: Improving HTML5 Canvas Performance.

Apr 10

Creating a Hex Grid for HTML5 Games in Javascript

In my last post, I described how to do some of the calculations for determining the required measurements to build a hexagon. Today I’ll show you how to take that a step further and build a hex grid or game-field out of those hexagons.

Let’s start with the overview and pseudo-code for populating our grid of hexes.

Overview/Pseudocode:

  1. Populate each row while we have vertical space on the canvas
  2. After each hex (column), increment the x direction by the width of the hex + the side length until we run out of horizontal space on the canvas
  3. Every other row must be offset in the x direction by the width of the hex minus the side length divided by two
  4. After each row increment the y direction by 1/2 the height of the hex

Simple version of JavaScript code:

HT.Grid = function(/*double*/ width, /*double*/ height) {

this.Hexes = [];

var row = 0;
var y = 0.0;
while (y + HT.Hexagon.Static.HEIGHT <= height)
{
   var col = 0;
   var offset = 0.0;
   if (row % 2 == 1)
   {
      offset = (HT.Hexagon.Static.WIDTH - HT.Hexagon.Static.SIDE)/2 + HT.Hexagon.Static.SIDE;
      col = 1;
   }

   var x = offset;
   while (x + HT.Hexagon.Static.WIDTH <= width)
   {
      //first parameter of the hexagon constructor is the hexId
      //   which we'll discuss more later
      var h = new HT.Hexagon(null, x, y);
      this.Hexes.push(h);

      col+=2;
      x += HT.Hexagon.Static.WIDTH + HT.Hexagon.Static.SIDE;
   }
   row++;
   y += HT.Hexagon.Static.HEIGHT / 2;
}

};

That is pretty straight-forward, but before we have a usable grid we may want to add a few more things. If you notice my Hexagon constructor has an "Id" member, the purpose of the Id is to give each hex a unique string identifier, this makes it easy to reference each hex, either programmatically, or for debugging. I like to give my hexes a Letter prefix based on the row plus a number based on the column, but you can do whatever works for you.

Here is my simple function for getting the hex identifiers I want:


HT.Grid.Static = {Letters:'ABCDEFGHIJKLMNOPQRSTUVWXYZ'};

HT.Grid.prototype.GetHexId = function(row, col) {
	var letterIndex = row;
	var letters = "";
	while(letterIndex > 25)
	{
		letters = HT.Grid.Static.Letters[letterIndex%26] + letters;
		letterIndex -= 26;
	}

	return HT.Grid.Static.Letters[letterIndex] + letters + (col + 1);
};

Hex Distance:

Finally, since it is common in games to need to figure out the distance between two hexes, we probably want a function that takes two hexagons and returns a distance. To facilitate this, we'll need to assign some coordinates to each of our hexagons that will make it faster and easier to calculate distances when necessary. James McNeill has a very good post on his PlayTechs blog about hex grids where he describes a coordinate system for hex grids, and how to use that coordinate system to find distances between hexes.
Basically, instead of a coordinate system based on the rows and columns, we have columns (shown in green), and diagonal rows (shown in red). Once we have assigned coordinates to our hexagons as shown, we can use the following equation to calculate the distances between two hexes:
\frac{|\Delta x| + |\Delta y| + |\Delta x - \Delta y|}{2}

Here is my JavaScript function that returns the distance between two hexes in the grid:

HT.Grid.prototype.GetHexDistance = function(/*Hexagon*/ h1, /*Hexagon*/ h2) {
	var deltaX = h1.PathCoOrdX - h2.PathCoOrdX;
	var deltaY = h1.PathCoOrdY - h2.PathCoOrdY;
	return ((Math.abs(deltaX) + Math.abs(deltaY) + Math.abs(deltaX - deltaY)) / 2);
};

Note: I'm using the PathCoOrdX and PathCoOrdY members of my Hexagons in the above function, which I populate in my full version of the Grid constructor, but I won't go into how I do that in this post, basically the PathCoOrdX comes from the column variable, and PathCoOrdY is populated in a second pass. You can download the full Grid.js code to see the exact method I use.

The Fun Part:

I've put together a simple form and canvas, similar to what I did in my last post, so that you can see the Hex Grid in action, and play with the parameters. I've also added another option that allows you to switch the hex orientation that you can play with.



Orientation:

Hex Based on Side Length

Side Length (z):

Ratio of Width:Height (4:3, r = 1.333), (2/√3, r = 1.1547...)

Hex Based on Width and Height

Width:

Height:

Source Files

Grid.js - The full source code for the grid class, or you can download all code for these Hexagon articles in this HexagonTools archive.

Apr 05

Fun with Hexagon Math for Games

Hexagons are great for game grids because, compared to the standard square grid, the distance from the center of a hex to any adjacent hex is the same (assuming you have a “Normal” hex with width to height ratio of 2:√3). In a square grid, moving from one square to an adjacent diagonal square is further away then moving horizontally or vertically to an adjacent square.

The mathematics and programming of Hexagons for a game grid can get a bit more complex than the math and programming for squares. You have to create polygons instead of rectangles, when you create a grid of hexagons you have to offset each row by half the width of the hex, pathfinding can be more complex, and hit detection is more complex.

There are some choices you have to make when creating your hex grid:
How big are my hexagons going to be?
Are my hexagons going to be symmetric? Meaning the width is equal to the height (ratio 1:1, or square). Or will I choose some other ratio of width to height for my hexagons? A “Regular Hexagon” is quite common since all the angles and side lengths are equal (consequently the width to height ratio of regular hexagons turns out to be 2/√3).

The key variables we need to know before we are ready to draw a hexagon are:
Width (w), Height (h), and side length (z). We will also define x and y to be the other sides of the triangle made using the top left angled side as shown in this image:

Width and Height are known

Let’s say we know we want our hexes to be a certain size, so we know the width and height (maybe 100×100), but we also need to know z, the length of each side.

We know from the Pythagorean theorem that we have:
x^2 + y^2 = z^2

Also, from the above image you can see that there is a relationship between x, z and the width, and y and the height, specifically:
2x + z = w
and
2y = h

Now we should be able to solve for z (our side length).
x = \frac{w - z}{2}
y = \frac{h}{2}
Plugging x and y back into pythagoras we get:
(\frac{w - z}{2})^2 + (\frac{h}{2})^2 = z^2
After expanding and simplifying:
-3z^2 - 2wz + h^2 + w^2 = 0
Remember that w and h are constants we have already chosen, so we can solve this using the quadratic formula:

Quadratic formula
x=\frac{-b \pm \sqrt {b^2-4ac}}{2a}
where
ax^2 + bx + c = 0

In our case:
a = -3
b = -2w
c = h^2 + w^2
Thus, to get z (the hex side length from the width and height we can use this formula:
z=\frac{2w - \sqrt {4w^2+12(h^2+w^2)}}{-6}


Side Length and Ratio are known

Now let’s say instead of knowing the width and height for our hexagons, we know we want a certain side length z (say 100) and a certain width to height ratio r (say of 4:3, or 1.3).

We can still use our formulas from above, pythagoras and our formulas relating x, y and z to the width and height. Now since we don’t know the exact width and height, we’ll substitute that ratio (r) variable for w and h:

r = \frac{w}{h}
And using our equations from above:
r = \frac{2x + z}{2y}
Solving for y:
y = \frac{2x + z}{2r}

Now we’ll substitute our value for y into pythagoras:
x^2 + (\frac{2x + z}{2r})^2 = z^2

After simplifying and turning this into quadratic form:
(\frac{1+r^2}{r^2})x^2 + \frac{z}{r^2}x + (\frac{1-4r^2}{4r^2})z^2 = 0

Now for the quadratic we have:
a = \frac{1+r^2}{r^2}
b = \frac{z}{r^2}
c = (\frac{1-4r^2}{4r^2})z^2

If we substitute this into the quadratic, it looks pretty ugly:
x=\frac{-(\frac{z}{r^2}) + \sqrt {(\frac{z}{r^2})^2-4(\frac{1+r^2}{r^2})(\frac{1-4r^2}{4r^2})z^2}}{2(\frac{1+r^2}{r^2})}


Other Great Hex for Game Programming Resources:

Amit Patel’s Thoughts on Grids
PlayTechs: Programming for fun Hex Grids
Hexnet.org’s Introduction to Hexagonal Geometry


Can you do this for me? My HTML5 Hexagon Tools

If you are like me, you skim over all these equations and just want to get to the good stuff, like concrete examples. So I’ve put together a simple form to do these calculations for you using JavaScript. Once you fill out the form you should see a visual representation of the computed hexagon, drawn on a canvas (assuming you have an HTML5 enabled browser).


Hex Based on Side Length

Side Length (z):

Ratio of Width:Height (4:3, r = 1.333), (2/√3, r = 1.1547…)

Hex Based on Width and Height

Width:

Height:

Oops, your browser doesn’t support the HTML5 canvas tag!

Source Files

If you want to take a look at the JavasScript for this here are my source files, you are welcome to use these for your own projects:
HexCalcs.js
HexagonTools.js

HexCalcs.js is mostly just for parsing the forms above and performing the math described in this post. HexagonTools.js defines a Hexagon class that defines all the points in a hexagon and defines a draw method for the canvas.

In a future post I’ll show how to use the Hexagon class to build a Hex grid.

Mar 22

MattPalmerlee.com has just moved

Please excuse the new (and fairly empty) new version of my personal website.  I’m currently in the process of moving some of the old content to this new wordpress site.  Hopefully I will soon have everything straightened out.