Bryan Rhea

A .NET Developer in a Mobile World

HTML5 Canvas Game “Hangman” Demo

with 2 comments

I recently spent almost an hour searching for a simple word game for my daughter on the family iPad. It was, to say the least, extremely frustrating. If you’ve ever searched for apps for kids you know where I’m going with this. Every time I came across a promising game a closer look only revealed the ads and sneaky in-app purchases that must be purchased for the player to have a decent experience. So after passing on several apps my daughter, knowing what I do for a living, asked me a simple question.

“Can you make me a game?”.

I answered how any dad (or man for that matter) would when issued a challenge to “make” something: “Yes. Yes I can.” Since I haven’t built that shoe rack for my wife I have sworn for years I can build yet I decided this is my opportunity  . I have also wanted to start a technical blog for a while so this was going to be the kick in the butt I needed.

I’ve been brushing off the HTML5 hype for a while so since Visual Studio put out a Web Standards Update I figured it was time to jump in and see what it’s all about. I decided on the simple word game ‘Hangman’ and to use the HTML5 canvas element.

First, here’s the demo. To see how to get to that point keep reading.

Start with File->New Project->ASP.NET MVC 3 Web Application and on the next screen check the box “Use HTML5 semantic markup”.

s1

Next we need to create the layout. In this case I created a canvas element for the secret word and another one for the game status display. Since the canvas element has only one 2d rendering context, meaning you have to keep track of where everything is when you change objects on it, I created two elements to demonstrate a canvas layering technique that can help you later when you have to redraw the canvas a lot (think animation). I used a basic div for the letter options to show how you simply use JavaScript to interact with the canvas.

<div style="height:380px;">
<div style="position: relative;">
    <canvas id="layer1" width="460" height="400" style="position: absolute; left: 0px;
        top: 0px; z-index: 0;">
    </canvas>
    <canvas id="layer2" width="460" height="100" style="position: absolute; left: 0px;
        top: 0px; z-index: 1;">
    </canvas>
    <div id="layer3" style="width: 460px; height: 100px; position: absolute; left: 0px;
        top: 300px; z-index: 2;">
        <div id="group1" class="letterGroup">
        </div>
        <div id="group2" class="letterGroup">
        </div>
    </div>
</div>
</div>

Now we have our layout:

s3

Layer 1 is will contain the game messages and status, layer 2 will hold the word to guess, and layer 3 will contain the letters used for guessing.

So now let’s initialize the game. We will use some common canvas methods to create the secret word and game messages. The first part of the game initialization gets the canvas context (I am using jQuery), gets the secret word randomly from a big JavaScript array set up in another script file, and then draws the lines needed onto the layer2 canvas.

The first step is to get the context for the canvas elements and then clear them for the start of the game:

        canvas1 = $("#layer1")[0];
        canvas2 = $("#layer2")[0];
        ctx1 = canvas1.getContext("2d");
        ctx1.clearRect(0, 0, canvas1.width, canvas1.height);
        ctx1.font = "bold 24px sans-serif";

        ctx2 = canvas2.getContext("2d");
        ctx2.clearRect(0, 0, canvas2.width, canvas2.height);
        ctx2.font = "bold 24px sans-serif";

Get the secret word:

word = words[Math.floor(Math.random() * words.length - 1)].toUpperCase();

Draw the secret word, filling in any already guessed letters:

function updateWord() {

        var wordPosX = 20;
        var wordPosY = 60;
        var wordLineLength = 30;
        var wordLineSpacer = 10;
        for (var i = 0; i < word.length; i++) {
            ctx2.strokeStyle = "black";
            ctx2.beginPath();
            ctx2.moveTo(wordPosX, wordPosY);
            ctx2.lineTo(wordPosX + wordLineLength, wordPosY);
            ctx2.stroke();
            ctx2.closePath();

            //fill in letter if already guessed correctly
            if (wordCorrect[i] != "") {
                ctx2.fillText(word[i], wordPosX + 7, wordPosY - 5);
            }

            wordPosX = wordPosX + wordLineLength + wordLineSpacer;
        }
    }

So far we’ve already used a few canvas methods and properties to create elements, so let’s go ahead and list the ones being used in the game:

  • fillStyle – a property that can be set to a CSS color, pattern, or gradient. The context remembers this value until reset.
  • fillRect(x, y, width, height) – draws a rectangle filled with the current fillStyle.
  • strokeStyle – property like fillStyle.
  • fillText(text, x, y) – puts text onto the canvas at the position (x,y).
  • clearRect(x, y, width, height) – clears the pixels using a rectangle starting at position (x,y) with specified width and height.
  • moveTo(x, y) – moves writer to position (x,y).
  • lineTo(x, y) – draws a line to the position (x,y).
  • stroke(x, y) – actually draws the path you have set using moveTo() and lineTo() calls.

Okay, now we need to handle guessing a letter. Here we check for matches, update the secret word layer, and update the game status that checks if they have won or lost (ran out of guesses).

function guessLetter() {
        if (gameOver || $(this).hasClass("strike")) {
            return;
        }

        var noMatch = true;
        $(this).addClass("strike").attr("enabled",false);
        for (var i = 0; i < word.length; i++) {
            if ($(this)[0].innerText == word[i]) {
                wordCorrect[i] = $(this)[0].innerText;
                noMatch = false;
            }
        }
        if (noMatch) {
            guesses++;
        }

        updateWord();
        updateGameStatus();

        if (noMatch && !gameOver) {
            drawFace(guesses);
        }
    }

Although this is a ‘Hangman’ style game I decided to not go with the typical man on the gallows picture to show the player how they are doing. At one point I used an animation to move a box across the game but in the end chose to use different silly faces (remember, this was originally for my daughter) to indicate player status. This gave me a chance to use this draw image method:

  • drawImage(img, x, y) – draws the img (which is an element on the page) at position (x,y).

So we can place the images on the page and then use this method  to draw it onto the canvas.

<div style="display:none;">
    <img id="face1" src="@Url.Content(" Content/face1.png")" alt="1" title="1"/>
    <img id="face2" src="@Url.Content(" Content/face2.png")" alt="2" title="2"/>
    <img id="face3" src="@Url.Content(" Content/face3.png")" alt="3" title="3"/>
    <img id="face4" src="@Url.Content(" Content/face4.png")" alt="4" title="4"/>
    <img id="face5" src="@Url.Content(" Content/face5.png")" alt="5" title="5"/>
    <img id="face6" src="@Url.Content(" Content/face6.png")" alt="6" title="6"/>
    <img id="face7" src="@Url.Content(" Content/face7.png")" alt="7" title="7"/>
</div>

Function to draw the images:

function drawFace(guessNumber) {
        var face = $("#face" + (guessNumber+1))[0];
        if ( face != undefined) {
            ctx1.clearRect(facePosX, facePosY, 75, 75);
            ctx1.drawImage(face, facePosX, facePosY);
        }
    }

This is just one of the drawImage methods (the easiest I think because you just reference an img element on the page). The others allow you to clip and scale the image.  Just make sure the image is fully loaded before you call drawImage().

Updating the game status is done by checking if the player has used up all the guesses or has guessed all the letters and displays the game over message if necessary. If the game is over we set the timer to start a new game.

Download the zipped up source here.
View a live demo here .
Feel free to fix or add anything on GitHub .

Enjoy!

About these ads

Written by Bryan Rhea

June 24, 2011 at 1:12 pm

Posted in HTML5

Tagged with

2 Responses

Subscribe to comments with RSS.

  1. Awesome stuff!

    Peter Mourfield

    June 24, 2011 at 6:24 pm

  2. Haha, nice one man, really enjoyed reading the article! Also, love the game, keep up the good work! :)

    Claudiu

    June 24, 2011 at 9:51 pm


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: