2010-11-19

Project "HoT MetaL JaZz" - Part 1

In the introduction to this series, we went over how to get started programming games with HTML5 and JavaScript. If you haven't seen that article yet, I suggest you do so before proceeding further. The code in the previous article will be the basis upon which the remaining articles rely.

So, we should now have a blank HTML5 canvas element on our Web page. It seems a shame to let it just sit there, sight-unseen, so let's learn how to use JavaScript to interact with and manipulate the canvas element.

Go to the script portion of your source (i.e. the lines between the <script> and </script> tags). Insert the following lines of code above the declaration for the startGame() function:

var primaryCanvas;
var primaryContext;
var bufferCanvas;
var bufferContext;
var animation;

We will use the first four variables to reference our drawing surfaces - the "primary" variables show our graphics, while the "buffer" variables store data that is put together in preparation for drawing. The HTML5 Canvas element has a Context property, which is what we will primarily deal with when writing games. The fifth variable will simply be used to animate the context later.

Now, after the variable definitions, go into the body of the function "startGame()", and enter the following code:

primaryCanvas = document.getElementById("gamecanvas");
if (primaryCanvas.getContext) {
  primaryContext = primaryCanvas.getContext("2d");
  primaryContext.clearRect(0, 0, 300, 300);
}

bufferCanvas = document.getElementById("backbuffer");
if (bufferCanvas.getContext) {
  bufferContext = bufferCanvas.getContext("2d");
  bufferContext.clearRect(0, 0, 300, 300);
}

So, what did we just do here? First, we set our primaryCanvas variable to a reference to the actual HTML5 canvas element in our page, "gamecanvas". Then we did a check to make sure the primaryCanvas variable has a context. If so, we get the "2d" context, and set it into our primaryContext variable. We finish up by clearing a rectangular area of the context, starting from 0/0 (top-left) to 300/300 (the size of the canvas). We repeat the same process for the backbuffer.

The "2d" context of a canvas gives us access to all 2D functions and parameters that apply to drawing. In the future, when WebGL (and possibly other technologies) become more prevalent, there may be additional contexts. For now, though, we want the "2d" context.

Notice that rather than call the width and height properties of the primaryCanvas and bufferCanvas, I put literal values (i.e. 300) for the width and height of the clearRect function. Calling canvas elements directly is expensive, in that it has a fair bit of overhead involved. Thus, I simply use literal values instead.

Now that we have our drawing contexts, we're ready to make things happen.

After the code for grabbing our contexts, enter the following new function:

function draw() {
  bufferContext.fillStyle = "rgb(0, 0, 0)";
  bufferContext.fillRect(0, 0, 300, 300);
  bufferContext.fillStyle = "rgb(255, 255, 255)";
  bufferContext.font = "24px sans-serif";
  bufferContext.textAlign = "center";
  bufferContext.fillText("Oh, HI!", 150, 150);

  primaryContext.drawImage(bufferCanvas, 0, 0, 300, 300);
}

Finally, save your html file, and open it in your Web browser. If all went well, you should be greeted by a friendly message surrounded by a black background.

Here's a review of the above code:

The [context].fillStyle property tells the context which color - in RGB format - to use for fills. This value will persist until it is given a new value later on in the code. As for [context].fillRect, that simply takes the fillStyle (which defaults to RGB 0,0,0 - black) and uses that color to draw a filled rectangle, given an X/Y origin, and a width/height.

After drawing the black background in our context, we set the fillStyle to white (RGB 255,255,255), and use [context].font to adjust the font. The font can be any CSS-compliant font style. Then, we set [context].textAlign to center. This makes the text centered on the point used to draw it. Finally, we draw our text with [context].fillText, giving it the string, X-coordinate, and Y-coordinate to start from.

The context of an HTML5 canvas has a plethora of drawing functions beyond the code we just plugged in. The context has full support for translation, rotation, scaling, and has a transform method. To add a little spice to our project, let's make the text spin around like a propeller!

Go back to your code, and enter the following line at the end of your startGame() function:

animation = setInterval("draw()", 50);

And then add the following into your draw() function, just before the primaryContext.drawImage(...) call:

bufferContext.translate(150, 150);
bufferContext.rotate(0.1);
bufferContext.translate(-150, -150);

Save your code, and open it in your browser. Voila - the text spins around like a propeller, centered on the canvas.

Of course, you may be wondering about a few things. First, the [context].translate() calls. Why do we need those? The answer is simple - because the center point of rotations is always oriented to the top-left origin of what you are rotating. We change this by translating the context - and thus the center point - to the center of the screen (X=150, Y=150). Then we apply our rotation, and clean up our translation "mess" by subtracting the translation.

You may also find it odd that the letters "jitter" a bit. This is perfectly normal - text drawing along an angle isn't always precise. Of course, there are ways around this problem (such as drawing the text to the backbuffer first, and then rotating the backbuffer itself before copying it to the primary context).

We've only scratched the surface of what the HTML5 canvas is capable of. In the next article, we will cover drawing lines and shapes, as well as drawing images to the context. See you there!

No comments:

Post a Comment