Playing with fillStyle to create some neat game effects and text

Distant Orbit screenshot

Creating games using HTML5 and the Canvas isn’t always all about using drawImage(). For the most part it is of course because we want to display our lovingly drawn sprites and environments but there are plenty of legitimate cases where we might opt for using the primitive drawing methods such as fillRect().

I use fillRect and fillText consistently in my games and have never found it to be a performance killer. At least not using the canvas dimensions 320 x 480 which I use for mobile gaming. But you should be careful. The more you ask the API to do the more of a load you place on it and at some point something has to give. Namely your framerate !
But with a bit of planning and some consideration for what you are actually presenting it can be very visually rewarding.

Distant Orbit screenshot

Distant Orbit

In Distant Orbit I’m actually using rectangles to construct the pseudo 3D tumbling landscape. It’s a very old and well-used method of creating a moving terrain.
The theory is quite simple in that the lower half of the game screen is first filled with a solid colour and the striped terrain is then a series of rectangular ‘sprites’ that scale upwards as they fall down the screen. The effect is quite nice and I save on having to draw two sets of stripes by having the underlying layer as the alternate colour.
So the underlying layer is drawn in the lighter shade and then the striped layers are drawn over the top.

I refer to the stripes as sprites since they are derived from my base sprite class. That is they inherit specific behaviours and attributes that allow me to position and present them on the screen.
It’s just that when I get to actually drawing them I’m using fillRect instead of drawImage.

Here’s an example of how you would display a rectangle.

function drawRectangle(o) // o is the incoming sprite object from which I shall derive co-ordinates and size
{
 g.ctx.fillStyle = '#bf0000';
 g.ctx.fillRect(o.x,o.y,o.w,o.h);
};

(You can see that I store the canvas context within the global namespace g)
So g.ctx.fillStyle has a hex colour value assigned to it and the following draw instruction honours it.
When I draw the rectangle I have already passed through a move() function to return the new position and dimensions of the stripe.
It is also important to note that the fillRect method uses width and height as opposed to locations in terms of scaling your shape.

Example

g.ctx.fillRect(20,20,30,30);

produces a 30×30 square with its origin at 20,20 NOT as you might expect a 10×10 square that starts at 20,20 and finishes at 30,30.

To achieve the scaling terrain effect in Distant Orbit I initialise each stripe with a height attribute of 1 pixel. As it falls down the canvas it scales until by the time it falls off the bottom it’s around 48 pixels deep.
Rather than scale the stripes by a fixed number of pixels each time (e.g. 2px) I use a modifier. This stops the stripes looking horribly uniform. By incrementing the height attribute by a modifier that itself increments with smaller numbers I get the effect that the stripe remains quite narrow for a while as if it where lost toward the horizon:

o.modifier += 0.1;
o.h += o.modifier;

So the height of the stripe would change something like this:

0.1
0.3
0.6
1.0
1.5

which gives a far nicer effect than a uniform increment such as 1, 2, 3 etc.

The beauty of this approach to drawing on screen is that it is reflected in other forms of presentation such as writing text. Instead of fillRect you just use fillText but the style is set in the exact same way.

Take a look at this code.

<!doctype html>
<html lang=en>
<head>
	<title> fillRect() </title>
	<meta charset=utf-8>
	<script>

		var g = {};
		function init()
		{
			g.ctx = document.querySelector('canvas').getContext('2d');
			fill(0,0,320,480,"#0000ff"); // Blue background
			fill(20,20,30,30,"#ffff00"); // Yellow square (10x10)
			filla(0,80,320,20,"255,255,0",0.6); // Yellow stripe with 0.6 alpha
			write("Hello, World !",160,94,"center","#ffffff",12,null,"bold");
		};

		function fill(x,y,w,h,c)
		{
			c = c ? c : "#000000";
			g.ctx.fillStyle = c;
			g.ctx.fillRect(x,y,w,h);
		};

		function filla(x,y,w,h,c,a)
		{
			c = c ? c : "0,0,0";
			a = a ? a : 0.5;
			g.ctx.fillStyle = "rgba(" + c + "," + a + ")";
			g.ctx.fillRect(x,y,w,h);
		};

		function write(t,x,y,a,c,s,f,w)
		{
			c = c ? c : "#ffffff";
			a = a ? a : "left";
			s = s ? s : 10;
			f = f ? f : "Sans-Serif";
			w = w ? w : "normal";
			g.ctx.font = w + " " + s + "px " + f;
			g.ctx.textAlign = a;
			g.ctx.fillStyle = c;
			g.ctx.fillText(t,x,y);
		};

	</script>
</head>
<body onload="init();">

	<canvas width="320" height="480"></canvas>

</body>
</html>

The basic rectangle drawing is there along with a slightly different version that allows us to provide an alpha value. This alpha range is from 0 (100% transparency, i.e. non-existant) to 1 (opaque). So for example if you provided a value of 0.5 you’d see a 50/50 blend of the background colour and your rectangle’s colour.
The key thing to remember here is that for rgba values you must specify your colours numerically.
So for White rather than #ffffff you’d specify 255,255,255.
For White with 50% alpha opacity you’d use rgba(255,255,255,0.5);

Visit this link: fillrect.html to see the above code in action.

You can see that the text also has the standard CSS attributes of alignment, font-size, font-family and font-weight. This is extremely useful when you consider that you can pull fonts from Google’s web font repository for use in your games.

By simply adding the following line to my markup I can use the “Bangers” web font family in my game.

<link href='http://fonts.googleapis.com/css?family=Bangers' rel='stylesheet' type='text/css'>

Visit this link to see the change: fillrect-newfont.html

You can see that in my code the order in which the items are drawn is important.
Starting with the blue background is important since it allows subsequent items to be drawn over the top. If the blue background were drawn last it would simply overlay everything else.
That said there is an instance where you may wish to present a solid block of colour over your content and that’s when you wish to fade your content in and/or out.
To do this we simply draw a rectangle with progressive alpha opacity as the last draw item in our draw order.

Take a look at this code for the necessary adaptations:

<!doctype html>
<html lang=en>
<head>
	<title> Fade In/Out </title>
	<meta charset=utf-8>
	<link href='http://fonts.googleapis.com/css?family=Bangers' rel='stylesheet' type='text/css'>
	<script>

		var g = {};
		function init()
		{
			g.ctx = document.querySelector('canvas').getContext('2d');
			load();
		};

		function load()
		{
			g.img = new Image();
			g.img.onload = function() {
				g.fade = 0;
				g.ticker = setTimeout("loop()", 40); 
			};
			g.img.src = "sun.jpg";
		};
		
		function loop()
		{
			clearTimeout(g.ticker);

			g.ctx.drawImage(g.img,0,0);
			filla(0,0,320,480,"0,0,0",g.fade);
			writea("Hello, World !",160,110,"center","255,255,255",32,"Bangers","normal",g.fade);
			if (g.fade < 1) { g.fade += 0.01; }

			setTimeout("loop()", 40);
		};

		function fill(x,y,w,h,c)
		{
			c = c ? c : "#000000";
			g.ctx.fillStyle = c;
			g.ctx.fillRect(x,y,w,h);
		};

		function filla(x,y,w,h,c,a)
		{
			c = c ? c : "0,0,0";
			a = a ? a : 0.5;
			g.ctx.fillStyle = "rgba(" + c + "," + a + ")";
			g.ctx.fillRect(x,y,w,h);
		};

		function write(t,x,y,a,c,s,f,w)
		{
			c = c ? c : "#ffffff";
			a = a ? a : "left";
			s = s ? s : 10;
			f = f ? "'" + f + "'" : "Sans-Serif";
			w = w ? w : "normal";
			g.ctx.font = w + " " + s + "px " + f;
			g.ctx.textAlign = a;
			g.ctx.fillStyle = c;
			g.ctx.fillText(t,x,y);
		};

		function writea(t,x,y,a,c,s,f,w,alpha)
		{
			c = c ? c : "255,255,255";
			a = a ? a : "left";
			s = s ? s : 10;
			f = f ? f : "Sans-Serif";
			w = w ? w : "normal";
			alpha = alpha ? alpha : 1;
			g.ctx.font = w + " " + s + "px " + f;
			g.ctx.textAlign = a;
			g.ctx.fillStyle = "rgba(" + c + "," + alpha + ")";
			g.ctx.fillText(t,x,y);
		};

	</script>
</head>
<body onload="init();">

	<canvas width="320" height="480"></canvas>

</body>
</html>

Very straight forward. You can see that I set a variable called fade and have it increment slowly from zero to 1. By assigning this value to the new text writing function I achieve the effect of the background fading out whilst the foreground text fades in.

Visit this link to see it in action: fadeinout.html

This was only ever intended to be a basic introduction to using shapes and text. I’d like to elaborate on it a bit in a future post and cover gradients and arcs. Anyone familiar with my games will have seen me use gradients for sky transitions in a couple of cases. I am a big fan of them as I think they offer a useful programmatic solution to something that is typically controlled by the artist.

When Distant Orbit is complete I hope to offer a bit more of an insight in to the game’s structure with regard to the drawing.

3 Comments

    Distant Orbit – New mobile HTML5 arcade game « HTML5 Game Design and Development

    […] The game itself is essentially a shoot em' up. I started as I always do by playing with some neat feature. In this case it was all about the striped terrain tumbling down the screen. I wanted to give the impression that the starfighters were floating over a rapidly moving alien terrain. You can read about how I achieved this by reading this earlier post about fillRect(). […]

    Philip Watson (@cheersphilip)

    That’s cool. Will try and remember this info for future ref! i like the fade one.

    Isengero

    Good info. I like this effect and plan on using it on a game in the near future. Reminds me of those games from the 80s.

Leave a reply

%d bloggers like this: