Video icon 64
Build your coding skills with practical video courses from Tuts+. Start your free trial today.
Advertisement

How to Create an HTML5 Hangman Game With Canvas: The Basic Gameplay

by

In this tutorial I will show you how to create an HTML5 hangman game. In this first part, we will build the basic game, using HTML buttons for input, the canvas element for graphics, and JavaScript to provide the game logic and load an external word list. In the second part, we'll add extra bells and whistles. By the time you finish this tutorial you will have learned several elements of HTML5. Let's get started!


Also available in this series:

  1. How to Create an HTML5 Hangman Game With Canvas: The Basic Gameplay
  2. How to Create an HTML5 Hangman Game With Canvas: Bells and Whistles

Step 1: Getting jQuery and Project Setup

Create a folder to store the project files, and within this folder create two new folders named "js" and "styles".

We will be using the jQuery JavaScript library in this tutorial. Head over to the jquery.com website, and on the right side of the page click on the production version. This will take you to a page with the jQuery library. Use File > Save Page As and save the page as "jquery.js" into the "js" folder you just created. Also within the "js" folder create a file named "hangman.js"

Next, inside the "styles" folder create a file named "hangman.css"

Finally, at the root of your project folder create a file named "index.html"


Step 2: Setting Up the HTML and CSS

Enter the following code within the index.html file you created in the step above.

 
<!DOCTYPE html> 
<html> 
<head> 
<title>Canvas Hangman</title> 
<link rel="stylesheet" type="text/css" href="styles/hangman.css">  
<script type="text/JavaScript" src="js/jquery.js"></script> 
<script type="text/JavaScript" src="js/hangman.js"></script> 
 
</head> 
 
<body> 
<div id="wrapper"> 
<canvas id="hangmancanvas" width="400" height="400"> 
Your browser does not support Canvas. Please Update your Browser. 
</canvas> 
<div id="buttondiv"></div> 
</div> 
</body> 
 
</html>

Here we set up the basic structure of our HTML page, including the stylesheet and JavaScript files. We setup a div with the id of "wrapper which will hold the canvas and button elements. We setup a canvas element with an id of "hangmancanvas", set its width and height to 400 and set the fallback content. If the users browser does not support canvas, the words inside the canvas element will be printed to the page. We also setup a div element with an id of "buttondiv", this is where we will place the buttons.

Now open the "hangman.css" file and enter the following.

 
#wrapper{ 
width: 400px; 
height: 650px; 
margin: 0 auto; 
} 
 
#hangmancanvas{ 
border: 1px solid black; 
}

Here we set the #wrapper div's width and height and set it to be centered in the window, we also set a border around the canvas so it will be visible.

If you open "index.html" now you should see the canvas aligned to the center of the page with a black border around it.

I am assuming you are using a modern browser that supports console.log. The Firebug plugin for firefox works quite well for this, or if you are on Chrome you can get to the console by choosing Tools > Developer Tools from the spanner menu. I am using Firefox as my main development browser.

You must also build and test this app on a live server, otherwise you will run into issues. I use WAMP for a local server on my machine.


Step 3: document.ready()

Open the "hangman.js" and enter the following code.

 
$(document).ready(function() { 
	alert("Document Ready"); 
});

Here we are using the .ready() method of the jQuery library. This method gets called when the DOM has fully loaded. It is important to use this method when coding your application, because if you tried to access any of the page elements before they were loaded it would have no effect. For example, if you tried to add a click handler to a button before it had loaded it would not work correctly.

If you open up "index.html" now you should see an alert pop up informing you that the DOM has been loaded and the page is ready.


Step 4: Adding the Buttons

In this step we will add the buttons to the page. Add the following within the $(document).ready method.

 
var hangman = { 
	alphabetArray: ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"] 
} 
for (var i = 0; i < hangman.alphabetArray.length; i++) { 
	$('<button/>', { 
		text: hangman.alphabetArray[i], 
		id: 'btn_' + hangman.alphabetArray[i], 
		width: "30px", 
		click: function (event) { 
			checkGuess(event, false); 
		} 
	}).appendTo("#buttondiv"); 
}

Here we create a hangman object literal. Within this object we create an alphabetArray containing the letters of the alphabet.

Next we use a for loop to run through our alphabetArray. We use the $() method of jQuery to create a button, set some properties, and add a click function to it. We set the text of the button equal to the current letter of the alphabetArray, set the id of the button equal to the string "btn_"+alphabetArray[i] (i.e. "btn_A", "btn_G", and so on), set the button's width to "30px", add a click method to the button that calls a checkGuess() function when the user click on one of the buttons, and finally append the button to the #buttondiv.

The appendTo(target) method of jQuery takes as a parameter a target, and inserts the element at the end of the target. Here the element is the button we create on each iteration, so in effect, on each iteration of the loop we are adding a button to the buttondiv.

The $() method, which is a shorthand alias for jQuery, can take two arguments: html and props. From the jQuery website:

If a string is passed as the parameter to $(), jQuery examines the string to see if it looks like HTML (i.e., it has <tag ... > somewhere within the string). If not, the string is interpreted as a selector expression. But if the string appears to be an HTML snippet, jQuery attempts to create new DOM elements as described by the HTML. Then a jQuery object is created and returned that refers to these elements. You can perform any of the usual jQuery methods on this object.

So in the above code the button is the html, and the text, id, width, and click are the props.

Here is a link to the jQuery( html, props ) documentation; it is about a third of the way down the page.


Step 5: checkGuess()

Add the following beneath the for loop you created in the step above.

 
function checkGuess(event,isKeyPress){ 
	var currentButton; 
	var theLetter; 
	currentButton = $(event.target); 
	$(currentButton).attr("disabled", "disabled"); 
	theLetter = $(currentButton).text().toLowerCase(); 
	alert(theLetter); 
}

This function gets called when the user clicks on one of the buttons. You will notice we pass event and isKeyPress as parameters.

The event holds information about itself, including the .target: which would be the button that was pressed. If you want to see what other information the event holds, add a console.log(event) within the checkGuess() function.

The isKeyPress is a boolean value, and is needed because both the buttons and the keyboard will use this same function. We need a way to tell whether the user clicked on one of the buttons, or whether they pressed a key on their keyboard.

Within the checkGuess() function we set two variables: currentButton and theLetter. We set currentButton equal to $(event.target) which is the the button that was clicked. We then add a "disabled" attribute to the button, which disables the button so the user cannot click on it again. Lastly we set theLetter equal to the buttons text and make sure it is lowercase by calling JavaScripts .toLowerCase() method. We are alerting out theLetter so you can see it is working correctly.

Here are some references to the attr() and text() methods on the jQuery site so you can learn more about them:


Step 6: Loading the Text File

Instead of hardcoding the word list, we will load an external text file that has over 20,000 words in it. I am using the 12dicts wordlist, which is available here on SourceForge I have included this in the download files as well.

If you download it, make sure to grab the one under the heading "Official 12Dicts Package". At the time of this writing it is named "12dicts-5.0.zip". Extract this file and within the folder you will find a text file named "2of12" copy this to the root of your project folder and rename it to "wordlist.txt".

Now add the following array to the hangman object we created earlier.

 
var hangman = { 
	alphabetArray: ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"], 
	wordList: [] 
}

Make sure you add a comma after the alphabetArray:["A","B",...] as shown above.

The wordList is an array into which we will put the words from the text file. Now add the following code above the for loop that we used to create the buttons.

 
$.ajax({ 
	type: 'GET', 
	url: "wordlist.txt", 
	success: function( data ) { 
		hangman.wordList = data.split("\r\n"); 
		if (hangman.wordList.length === 1) {  //no \r\n so assume \n alone is linebreak 
			hangman.wordList = data.split("\n"); 
		} 
		incrementAssetsLoaded(); 
	}, 
	dataType: "text" 
});

Here we are using jQuery's ajax() method to load the text file, we are using the type, url, and dataType properties. We also use the success() method. We set type to 'GET', although this is the default anyway; we set url to "wordlist.txt", which should be at the root of your project directory now; and we set the dataType to "text" - other acceptable dataType are "xml","json" and so on.

The success function takes as a parameter data, which holds the text returned from the server. Here data will hold the contents of our text file.

We are using JavaScript's String.split() method to put the contents of the text file into the wordList array. This method splits a string into an array of substrings, based on a separator passed as the first argument. So, "azbzc".split("z") becomes ["a", "b", "c"].

We first use data.split("\r\n") to attempt to split the list by a carriage return and linespace pair, and then check whether hangman.wordList.length === 1. If it is equal to 1 then we reinstantiate the wordList using data.split("\n"). The reason we are doing it this way is that Windows machines use "\r\n" as newline seperators, whereas UNIX uses just "\n".

Finally within the success() method we call incrementAssetsLoaded(), which we will code in the next step.

Here is a link to the split() method on the developer.mozilla.org JavaScript reference with some examples to help you better understand it, as well as the ajax() method on the jQuery site.


Step 7: incrementAssetsLoaded()

We will be keeping track of the assets we are loading in. When all are loaded we will begin the game. We need a way to keep track of how many of our assets have loaded, so add the following to the hangman object.

 
var hangman = { 
	alphabetArray: ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"], 
	wordList: [], 
	assetsLoaded: 0 
}

Make sure you add the comma after wordList: [].

Add the following below the ajax() method you coded in the step above.

 
function incrementAssetsLoaded() { 
	hangman.assetsLoaded += 1; 
	if(hangman.assetsLoaded == 1){ 
		startGame(); 
	} 
}

Here we increment the assetsLoaded property of the hangman object, and check whether it is equal to 1, indicating that all our current assets have loaded (we'll add more later!). If so, we call startGame() which we will code next.


Step 8: startGame()

Add the following beneath the incrementAssetsLoaded() function you created in the step above.

 
function startGame(){ 
	alert("Assets Loaded"); 
}

We are just using an alert to make sure the code is working. Test the page and you should see the alert. Now change startGame() to the following.

 
function startGame(){ 
	createGuessWord(); 
}

We will create the createGuessWord() function in the next step.


Step 9: createGuessWord()

In this step we will select the word the user has to guess. We are going to need a few new variables so add the following to the hangman object.

 
var hangman = { 
	alphabetArray: ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"], 
	wordList: [], 
	assetsLoaded: 0, 
	theWord: "", 
	guessWord: [], 
	newGuessWord: "" 
}

Make sure you put a comma after assetsLoaded. The word is the word the user will need to guess, guessWord[] will be an array in which we store each individual letter of the word, and newGuessWord will be the string the user needs to guess (shown as question marks) in the game.

The guessWord array and the newGuessWord are used together. Since strings are immutable in JavaScript (meaning they can't be changed once created), we need a way to change it, so we put the letters into an array. We change each individual letter in the array, then create a new String from the array.

Add the following beneath the startGame() function you created in the step above.

 
function createGuessWord(){ 
	hangman.guessWord = new Array(); 
	var randomWord = Math.floor(Math.random() * hangman.wordList.length); 
	hangman.theWord = hangman.wordList[randomWord]; 
	if(hangman.theWord.length < 3 || hangman.theWord.length > 12){ 
		createGuessWord(); 
	} 
	alert(hangman.theWord); 
	//console.log(hangman.theWord); 
	for(var i = 0; i < hangman.theWord.length; i++){ 
		if(hangman.theWord.charAt(i) == "-"){ 
			hangman.guessWord[i] ="-"; 
		}else{ 
			hangman.guessWord[i]="?"; 
		} 
	} 
	hangman.newGuessWord = hangman.guessWord.join(""); 
}

Here we set the guessWord property of the hangman Object to a new Array this clears out whatever was in it before.

We then get a random word from the wordList array (which contains all the words we loaded in), we use (Math.random * hangman.wordList.length) and then we use Math.floor() to round it down. Math.random returns a number between 0 (inclusive) and 1 (exclusive), so by multiplying it by the wordlist.length we make it choose a number between 0 and the length of the wordlist array.

If for example the wordlist array contained 15,000 items we would be choosing a number between 0 and 15,000.

Next we grab a word from the wordList array (using our randomWord) and set it equal to the theWord property of the hangman object. If randomWord was 55 then we would be setting theWord equal to the 55th element from the wordList array.

We check if theWord.length is less than 3 or greater than 12, and if it is we call createGuessWord() again. (The rest of in the function does not run.) A hangman word with only three letters is no fun, and I needed to make sure the word fit in the canvas and found 12 to be a good maximum length; you could probably push it up to around 16 or so if you wish.

We are alerting theWord to help with debugging and testing.

Next we loop through theWord. Inside this loop we check whether theWord.charAt(i) == "-" and if it does we set the respective guessWord element to a dash; otherwise we set the element to a "?". (Some of the words had a dash in them, using this method we make sure to add a dash in the word so the user would not have to guess a dash character.)

The charAt() method of the String class returns the specified character from a string. For example, "dog".charAt(0) is "d", charAt(1) is "o", and charAt(2) is "g". It is important to note that this is zero-based, just like an array.

Finally we set newGuessWord equal to hangman.guessWord.join(""). The join() method of the Array class joins all elements of an array into a string, like the opposite of a split(). It takes as a parameter the separator to insert between the string characters; here we use "", so we are making the string has no spaces between each character. If, for example, we would have used .join(", ") then each character of the resulting string would have a comma and a space between them.

Here are some links to the charAt() and join() methods in the JavaScript reference on developer.mozilla.org, with some examples to help you better understand them.

If you test now you should see the random word in an alert. If you refresh the page you should get a different word each time.


Step 10: drawCanvas

We are now ready to start using the canvas element. We need to get a reference to the canvas so add the following code within the hangman object.

 
var hangman = { 
	alphabetArray: ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"], 
	wordList: [], 
	assetsLoaded: 0, 
	theWord: "", 
	guessWord: [], 
	newGuessWord: "", 
	theCanvas: $("#hangmancanvas").get(0) 
} 
var ctx = hangman.theCanvas.getContext("2d");

Make sure you add a comma after the newGuessWord. Also notice how I added the ctx variable outside of the hangman Object. We need to do this because it is using theCanvas, and since the hangman Object had not been declared yet, we could not put ctx within the hangman Object and reference theCanvas.

The HTML5 canvas has a context "2d" by which all the drawing is done, we need to get a reference to this context.

Add the following within the startGame() function.

 
function startGame(){ 
	createGuessWord(); 
	drawCanvas(); 
   }

The drawCanvas() is where all the drawing for the game will take place. Add the following below the createGuessWord() function.

 
function drawCanvas(){ 
	ctx.font = "bold 35px serif"; 
	ctx.fillStyle = "#0000FF"; 
	ctx.fillText(hangman.theWord,50,27); 
}

To draw text on the canvas we need to set the context's font and fillStyle. Here we set ctx.font = "bold 35px serif", which sets the context's font to a bold 35px serif font. We set ctx.fillStyle = "#0000FF" which sets the context's fillStyle to blue, finally we call ctx.fillText(hangman.theWord,50,27) which takes as parameters the String to draw, and the X and Y coordinates of where to draw the text on the canvas.

If you test now you will see both the alert and the text drawn to the canvas.


Step 11: checkGuess() Updated

While drawCanvas() is where all the drawing logic takes place, checkGuess() is where all the game's logic takes place. In this step we will update checkGuess() to allow the player to win or lose a game.

Before we do that though, let's get the game showing the question marks instead of the actual word. Update the drawCanvas() function to the following.

 
function drawCanvas(){ 
  ctx.font = "bold 35px serif"; 
  ctx.fillStyle = "#0000FF"; 
  ctx.fillText(hangman.newGuessWord,50,27); 
}

We need a new variable, so add the following within the hangman object.

 
var hangman = { 
	alphabetArray: ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"], 
	wordList: [], 
	assetsLoaded: 0, 
	theWord: "", 
	guessWord: [], 
	newGuessWord: "", 
	theCanvas: $("#hangmancanvas").get(0), 
	numWrong:0 
}

Make sure you add the comma after theCanvas: $("#hangmancanvas").get(0)

Now update checkGuess() with the following.

 
function checkGuess(event,isKeyPress){ 
	var currentButton; 
	var theLetter; 
	var correctGuess = false; 
	currentButton = $(event.target); 
	$(currentButton).attr("disabled", "disabled"); 
	theLetter = $(currentButton).text().toLowerCase(); 
	for(var i =0; i < hangman.theWord.length; i++){ 
		if(hangman.theWord.charAt(i) == theLetter){ 
			hangman.guessWord[i] = theLetter; 
			correctGuess = true; 
		} 
	} 
	hangman.newGuessWord = hangman.guessWord.join(""); 
		 
	if(!correctGuess){ 
		hangman.numWrong++ 
	} 
	if(hangman.newGuessWord == hangman.theWord){ 
	  alert("YOU WIN"); 
	} 
	if(hangman.numWrong == 6){ 
		alert("YOU LOSE"); 
	} 
	drawCanvas(); 
}

I took out alert(theLetter), because it was distracting. We already know we are getting the correct letter.

If you play now you can win or lose, but the canvas looks all jumbled when it redraws the word after a correct guess. We will fix this in the next step.


Step 12: Fixing the Drawing Problem

The canvas is an immediate mode drawing surface, which means everything needs to be redrawn every time something changes. Contrast this to Flash, which uses a retained mode drawing surface and handles all the screen draws for you.

This helps explain why the canvas looks like it does currently (play the demo so far, and make a few correct guesses). We need to clear the drawing surface and redraw everything afresh.

The Canvas API has a method name clearRect() which clears all the pixels on the Canvas. Its signature looks like this: clearRect(x, y, w, h). The x and y params are the x and y position from which to start clearing, and the w and h and the width and height of the rectangle to clear. Since we want to clear the whole canvas we will be using ctx.clearRect(0, 0, canvas.width, canvas.height).

Sadly, however, with HTML5 being in an inconsistent state some browsers support clearRect() really well, and some do not. There is another way to clear the canvas, and this is by resetting the canvas's width. We will combine both methods to make sure we cover every situation. Add the following beneath above the drawCanvas() function:

 
function clearCanvas(context, canvas) { 
	context.clearRect(0, 0, canvas.width, canvas.height); 
	var w = canvas.width; 
	canvas.width = 1; 
	canvas.width = w; 
}

Here we pass in a context and canvas. Inside the function we call the clearRect() method we discussed above. We set a variable w equal to canvas.width, temporarily set the canvas.width = 1, and the reset the canvas.width = w. All we are doing is changing the canvas width then changing it back to its original width, so fast that you don't see it.

Lets put it to use now. Add the following within the drawCanvas() function:

 
function drawCanvas(){ 
	clearCanvas(ctx,hangman.theCanvas); 
	ctx.font = "bold 35px serif"; 
	ctx.fillStyle = "#0000FF"; 
	ctx.fillText(hangman.newGuessWord,50,27); 
}

Now if you test this the word will not appear all jumbled up when you guess a correct letter and you can actually play the game. You have to refresh the page to start a new game, and that is no fun. We will fix this in the next steps.


Step 13: disableButtons()

Having the user being able to click the buttons right at the start of the game could cause problems, if they started pressing them before all the assets were loaded. Also, if they continue to click on them after the game is over it would throw the game's logic off.

So we need a way to disable the buttons. Add the following below the for loop that creates the buttons.

 
function disableButtons(){ 
	$("#buttondiv button").attr("disabled","disabled"); 
}

Here we are getting all the button elements within the #buttondiv, and setting an their "disabled" attribute to disabled.

With that done go ahead and call this method right below its definition:

 
function disableButtons(){ 
	$("#buttondiv button").attr("disabled","disabled"); 
} 
disableButtons();

This disables the buttons when the page first loads - but we want to be able to actually play the game, so add the following within the startGame() function.

 
function startGame(){ 
	createGuessWord(); 
	drawCanvas(); 
	enableButtons(); 
}

This fixes the problem with the user pressing the buttons before the assets get fully loaded. We will fix the problem with them clicking them after the game ends in the coming steps.


Step 14: enableButtons()

We are disabling our buttons, but we need a way to re-enable them. Add the following code beneath the line disableButtons() you added in the step above.

 
function enableButtons(){ 
	$("#buttondiv button").removeAttr("disabled"); 
}

Here we use the same method to loop through all the buttons that we used in the disableButtons() function. The only thing different is that we are removing the "disabled" attribute.


Step 15: Starting a New Game After Win

Having to refresh the page after each win or loss would make for a bad user experience. In this step we will start a new game automatically after the player wins or loses. Modify the following block of code within the checkGuess() function to the following:

 
if(hangman.newGuessWord == hangman.theWord){ 
	disableButtons(); 
	setTimeout(doWin,1500); 
}

Here we disable the buttons, and use JavaScript's setTimeout() method to call a function 1500 milliseconds later. 1500 milliseconds is equal to 1.5 seconds.

Here is a link to the setTimeout() method with examples on the developer.mozilla.org site, so you can learn more about it.

We will code the doWin() function in the next step.


Step 16: doWin()

Add the following code beneath the startGame function.

 
function doWin(){ 
	doGameOver(); 
}

The doWin() function will be used for something different later in the code, but for now we are just calling the doGameOver() function. The doGameOver() will be shared between the winning and losing game functions.

Add the following beneath the doWin() function you just created above.

 
function doGameOver(){ 
	hangman.numWrong = 0; 
	startGame(); 
}

Here we reset our numWrong property of the hangman Object to 0 and call the startGame() function we created earlier. If you test now you can win a game; wait around a second and a half and a new game should start.

In the next step we will make it start a new game when the player loses as well.


Step 17: Starting a New Game After Losing

We need a new variable to tell whether the game is over or not. Add the following to the hangman Object.

 
var hangman = { 
	alphabetArray: ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"], 
	wordList: [], 
	assetsLoaded: 0, 
	theWord: "", 
	guessWord: [], 
	newGuessWord: "", 
	theCanvas: $("#hangmancanvas").get(0), 
	numWrong: 0, 
	gameOver: false 
}

(Make sure you add a comma after numWrong.)

Modify the following block of code within the checkGuess() function:

 
if(hangman.numWrong == 6){ 
	hangman.gameOver = true; 
}

Here we removed the alert("You Lose") and set the gameOver property of the hangman Object to true.

Now back inside the drawCanvas() function, add the following.

 
function drawCanvas(){ 
	clearCanvas(ctx,hangman.theCanvas); 
	ctx.font = "bold 35px serif"; 
	ctx.fillStyle = "#0000FF"; 
	ctx.fillText(hangman.newGuessWord,50,27); 
   
	if(hangman.gameOver){ 
		disableButtons(); 
		setTimeout(doGameOver,1500); 
	} 
}

Here we disable the buttons, and call doGameOver after 1500 milliseconds. If you test now, you can lose a game and have the game restart.


Step 18: drawHangman()

In this step we will setup the drawHangman() function which handles the drawing logic of the hangman. Enter the following code above the drawCanvas() function.

 
function drawHangman(drawNum){ 
  switch(drawNum) 
	{ 
	 case 0: 
		drawGallows(); 
	 break; 
	 case 1: 
		drawHead(); 
	 break; 
	 case 2: 
		drawBody(); 
	 break; 
	 case 3: 
		drawArm1(); 
	 break; 
	 case 4: 
		drawArm2(); 
	 break; 
	 case 5: 
		drawLeg1(); 
	 break; 
	 case 6: 
		drawLeg2(); 
	 break; 
   } 
}

This function takes a number as a parameter, and draws the respective part of the hangman depending on which number is passed to it. If 0 is passed we call the drawGallows() function; if 3 was passed we call the drawArm1() function. We have not created these functions yet but will do so in the next steps.


Step 19: drawGallows()

This function draws the gallows on which the hangman gets hung. Enter the following code above the drawHangman() function you created in the step above.

 
function drawGallows(){ 
	ctx.moveTo(120,305); 
	ctx.lineTo(280,305); 
	ctx.moveTo(260,305); 
	ctx.lineTo(260,70); 
	ctx.lineTo(180,70); 
	ctx.lineTo(180,96); 
	ctx.stroke(); 
}

Here we are using the moveTo(), lineTo() and stroke() methods of the canvas's context to draw the gallows.

Think of drawing on a piece of paper; the moveTo() method is similar to lifting your hand off the paper and moving to a new position, and takes as parameters the x and y coordinates to move to. Similarly, lineTo() is like drawing a line from one point to another, and also takes x and y coordinates as parameters. Finally, the stroke() method does the actual drawing.

It should be noted that calling lineTo() does not actually draw anything immediately after the call - only when you call stroke() will the lines(s) be drawn. The canvas "saves" all the moveTo() and lineTo() calls, then when you call stroke() it "runs through" all the saved commands and does the drawing. Then it forgets about everthing it just did.

Enter the following code within the drawCanvas() function and test; you should see the gallows drawn to the canvas.

 
function drawCanvas(){ 
	clearCanvas(ctx,hangman.theCanvas); 
	ctx.font = "bold 35px serif"; 
	ctx.fillStyle = "#0000FF"; 
	ctx.fillText(hangman.newGuessWord,50,27); 
   
	drawHangman(0); 
	if(hangman.gameOver){ 
		disableButtons(); 
		setTimeout(doGameOver,1500); 
	} 
}

Click here to test.


Step 20: drawHead()

Enter the following code beaneath the drawGallows() method you coded in the step above.

 
function drawHead(){ 
	ctx.beginPath(); 
	ctx.arc(180,120,23,0,Math.PI*2,false); 
	ctx.closePath(); 
	ctx.stroke(); 
}

Here we are using the canvas methods beginPath(), arc(), and closePath(). When drawing shapes on the canvas you use beginPath(), along with a combination of moveTo(), lineTo(), arc() and other similar methods. The path can be closed using closePath().

Once a path is created, you can use fill() or stroke() to render the path to the canvas. We are not discussing fill() in this tutorial, but you should know it exists should you decide to explore the HTML5 canvas element further.

The arc() method takes as parameters centerX, centerY, radius, startingAngle, endingAngle, and antiClockWise (which determines which direction to draw the arc). The startingAngle and endingAngle are in radians: there are 2*PI radians in a complete circle (so, 1 radian is equal to 180 degrees), so we set the startingAngle as 0, and the endingAngle to Math.PI*2, to draw a full circle.

Add the following code within the drawCanvas() function. When you test the page you should see both the gallows and the head drawn.

 
function drawCanvas(){ 
	clearCanvas(ctx,hangman.theCanvas); 
	ctx.font = "bold 35px serif"; 
	ctx.fillStyle = "#0000FF"; 
	ctx.fillText(hangman.newGuessWord,50,27); 
   
	drawHangman(0); 
	drawHangman(1); 
	if(hangman.gameOver){ 
		disableButtons(); 
		setTimeout(doGameOver,1500); 
	} 
}

Click here to test.


Step 21: drawBody()

Add the following code below the drawHead() function you created above. The code should be familiar by now.

 
function drawBody(){ 
	ctx.moveTo(180,143); 
	ctx.lineTo(180,248); 
	ctx.stroke(); 
}

Add the following to the drawCanvas() function. If you have been following along you should have no problem knowing where to place it. Now test the page.

 
drawHangman(2);

Step 22: drawArm1() and drawArm2()

Enter the following code below the drawBody() function you created in the step above.

 
function drawArm1(){ 
	ctx.moveTo(180,175); 
	ctx.lineTo(142,167); 
	ctx.stroke(); 
} 
function drawArm2(){ 
	ctx.moveTo(180,175); 
	ctx.lineTo(218,167); 
	ctx.stroke(); 
}

Now add the following to the drawCanvas() function, and test the page.

 
drawHangman(3); 
drawHangman(4);

Step 23: drawLeg1() and drawLeg2()

Add the following beneath the drawArm2() method you created in the step above.

 
function drawLeg1(){ 
	ctx.moveTo(180,245); 
	ctx.lineTo(145,270); 
	ctx.stroke(); 
} 
 
function drawLeg2(){ 
	ctx.moveTo(180,245); 
	ctx.lineTo(215,270); 
	ctx.stroke(); 
}

Now add the following to the drawCanvas() function, and test the page. You should have a complete hangman drawn on the canvas.

	 
drawHangman(5); 
drawHangman(6);

Click here to test.


Step 24: Drawing the Hangman Incrementally

Now that we have gotten the hangman fully drawn, we will make it draw incrementally depending on how many wrong guesses the user has made. Change the drawCanvas() function to the following.

 
function drawCanvas(){ 
	clearCanvas(ctx,hangman.theCanvas); 
	ctx.font = "bold 35px serif"; 
	ctx.fillStyle = "#0000FF"; 
	ctx.fillText(hangman.newGuessWord,50,27); 
   
	for(var i=0;i<=hangman.numWrong;i++){ 
		drawHangman(i); 
	} 
  
	if(hangman.gameOver){ 
		disableButtons(); 
		setTimeout(doGameOver,1500); 
	} 
}

Here we removed the individual draws to the drawHangman() function, and are instead using a for loop to draw the hangman. We are looping over the numWrong variable and calling drawHangman() with i. If numWrong was 2 then the for loop would be calling drawHangman(0), drawHangman(1), and drawHangman(2), thereby drawing the gallows, head, and body.

We will need to reset the gameOver property of the hangman object when the game ends, so enter the following code within the doGameOver() function.

 
function doGameOver(){ 
	hangman.numWrong = 0; 
	hangman.gameOver = false; 
	startGame(); 
}

If you test now you should see the hangman being drawn, respective to how many wrong guesses you have made.

This completes the first half of this tutorial. Let's remove the alert that shows the word at the beginning of the game, and instead show it when the player loses the game. Remove the alert(hangman.theWord) from within the createGuessWord() function and add the following within the drawCanvas() function.

 
if(hangman.gameOver){ 
	alert("The correct word was\n "+hangman.theWord) 
	disableButtons(); 
	setTimeout(doGameOver,1500); 
}

Conclusion

You should now have a basic playable hangman game without all the bells and whistles. In the next part of this series we'll be adding sound, animation, and more!

Advertisement