blog | twitter | tutes | about
Creating a Hangman Game in Flash
Actionscript 3
What you will learn:
Prerequisites:
Goal
We are going to make a simple Hangman game using Flash Actionscript 3 (AS3). At the end of this tutorial, you will have a working Hangman game that you can use and customize as you wish. The completed code is given at the end of this document.
You can see an example of the finished game here
Download the source files here
Step 1
The first step is to prepare the basic documents that we will need. We need to create a Flash file named Hangman.fla and we need to create a document class called Main.as, where we will hold most of our code. We will create one other .as file as we work through the tutorial, but we’ll discuss that later. It’s good if you set the Flash document size to: Width – 800 and Height – 600. This makes the movie nice and big, but it will fit into most laptop screens without scrolling.
*If you are unfamiliar with document classes, there is a quick guide here.
Once you have done this, proceed with step 2.
Step 2
OK, we’re going to need to set up our .fla file and add some graphics. We will only need two frames on the main timeline. The first frame is for the introduction and the second frame is for the game.
We need to put two things on the first frame: the title and a Start button. You need to name the Start button PlayButton. (Remember that Flash is sensitive to capitalization.)
I created this button:
Make sure that PlayButton is the instance name. You can check this by examining it in the properties inspector:
My completed first page looks like this:
Go to Frame 2 on the main timeline and make sure there’s a blank keyframe there. We will need to animate the man being hanged in seven stages. I did this by creating a movie clip with seven frames. The seven frames are as follows:
Frame 1:
Frame 2:
Frame 3:
Frame 4:
Frame 5:
Frame 6:
Frame 7:
Give this clip the instance name Man – remember that capitalization counts here – and position it on the screen as follows.
We will display a clue for the word that the player needs to guess – to the right of the hangman’s noose. So create two textboxes, one with the word clue, and an empty dynamic textbox with the instance name: DisplayClue. (You can set this in the properties tab.) You should have something that looks like this:
OK, now to address an annoying little issue. In Flash CS5 and up, when you use dynamic text – especially if you do not use TLF text - you will usually need to embed which font and characters you are going to use. Failing to do this will mean that some or all of your text is not visible.
We do this by clicking on the text box and then clicking ‘Embed’ in Properties:
Then filling in the dialog box as we need:
At the bottom of the screen, we wish to display a simple line of instructions and then another empty dynamic text box, where we will display the letters that the player has gotten wrong. I am using red for this, since it will be used to display the wrong answers. We will use the instance name: BadLetters. As always, pay attention to capitalization. Notice how I have used descriptive names like ‘DisplayClue’ and ‘BadLetters’. I could save some typing by using short forms such as ‘DC’ and ‘BL’, but we always want our code to be as human-readable as possible. Trust me, it makes debugging a whole lot easier.
Now we should have something like this:
Next we need to create blank tiles where the letters will appear and we need you create a banner with the message You Win! We will recycle the same banner to display You Lose! We will use two different techniques for the tiles and the banner.
First the tiles. Anywhere on the screen, draw a line 24 pixels wide. The width is important because we will use code to control the spacing. I used a thin light blue line within a thick dark blue line to give the following effect:
Just above the line, place an empty dynamic text box with the instance name Blank. I used a blue Arial 36-point font. Now you should see something like this:
Select both the text box and the line, right click on them and choose ‘Convert to Symbol’. In the box that pops up, give the name Tile. Make sure that it is a Movie Clip and not a button. Tick the box that reads ‘Export for ActionScript’ and give it the class name tile, like this:
Now select it and delete it from the stage. Don’t worry, it will still be in the library! By giving it a class name and exporting it for actionscript code, we will be able to create it on the sly when we need it!
Now for the banner message. Create a move clip with the instance name MessageBoard and with two frames as follows.
Frame 1:
Frame 2:
I placed my clip over where the clue would normally be displayed, so the final page looks like this:
Well that’s all the graphics and layout done, now it’s time to pay attention to the code.
Step 3
For this kind of project, it is useful to lay down some pseudocode before we proceed, so we can plan the flow of our code.
1 Create Wordlist
2 Choose Word
3 Split Word into Letters
4 Display Blank Tiles
5 Display Clue
6 Add Controls
7 If a key is pressed,
check if valid letter
check if previously entered
check if letter is found in word
8 If letter exists in word,
fill in tile
check if complete word has been found
if complete word is found, display victory message
9 If letter does not exist in word,
display relevant hangman graphic
display letter in list of incorrect words
check if last chance has been used
if last chance has been used, display losing message
Great! Now we can split our project up into bite size steps, and set short-term targets to achieve. In reality of course, there will be a couple of steps or issues that we didn’t predict.
Step 4
Open or create Main.as. We will start with something like this:
package {
//place import statements here
public class Main extends MovieClip {
//define variables here
public function Main() {
}//main
}
}
I have added a couple of notes, showing where to add the import statements and where to define the variables.
When we use a document class, as opposed to putting the code inside the .fla file, it means that we may need to add some functionality. In our case, we want to add three things: MovieClip functionality, and the abilities to use the keyboard and the mouse for input. Hence we need these statements:
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.events.KeyboardEvent;
What I like to do is to define all my variables at the beginning, before the Main() function. Here are the variables that we will use:
public var wordlist:WordList;
public var CurrentWord:String;
public var CurrentClue:String;
public var rnd:int;
public var Letters:Array=new Array;
public var i:int;
public var tile:Tile;
public var TileArray:Array=new Array;
public var UserKeyPress:String;
public var Alphabet:Array=new Array;
public var Found:Boolean;
public var Win:Boolean;
public var Lose:Boolean;
public var DefeatCounter:int;
public var UsedCharacters:Array=new Array;
public var FirstPlayThrough:Boolean= true;
Notice that some of them are strings, some integers some booleans (they can hold the values true/false) and some arrays. Notice these two variables:
public var tile:Tile;
This links the the ‘Tile’ movie clip that we created in step 2.
public var wordlist:WordList;
This refers to a custom class ‘Wordlist’ that we will create in just a moment.
Step 4
Let’s flesh out the Main() function. This is the ‘constructor’ function that will play automatically when we play the flash movie. We want this function to stop the playhead, so that the movie doesn’t go to frame 2. Then we will create an instance of the wordlist. Finally, we will add functionality to the Play button, so that it will move us to frame 2. At the same time, we will call another function (SetUp();) that will set up the second screen for us. Here is our code for all that:
public function Main() {
stop();
wordlist=new WordList;
PlayButton.addEventListener(MouseEvent.CLICK, ButtonAction1);
function ButtonAction1(eventObject:MouseEvent) {gotoAndStop(2); SetUp();}
}//main
Note that if you play the flash movie now, it won’t work. This is because we haven’t created our WordList class and we haven’t written the SetUp(); function. Let’s remedy that.
Step 5
There are many ways to approach a problem, but we wish to have our wordlist as a separate class for three reasons:
1 Reusability is a key concept in programming. We can create a list of words and clues and then reuse them for other games if we wish.
2 If we were to have a large list of words, we don’t want it to clutter up our main code. My ‘Word Strips’ game at www.flashbynight.com/strips requires a list of all the four-letter words in English, for example. That’s a few thousand words.
3 This is a tutorial and you’re working through it so you can learn new stuff.
OK, use flash to create a new .as file (File>New>ActionScript 3.0 Class) and give it the name WordList. Save it as WordList.as in the same folder as the other files. Then modify the code as follows:
package {
import flash.display.MovieClip;
public class WordList extends MovieClip {
public var Words:Array = new Array;
public var Clues:Array = new Array;
public function WordList() {
Words=["kangaroo","macaroni","starbucks","washington","turtle","guillotine"];
Clues=["an animal","a kind of food","a company","a place","a creature","a machine"]
}//main
}
}
Basically what we’ve done here is to create two arrays to hold our data. Obviously, you can use different words and clues if you wish. You can add as many as you like, we will make our code flexible in that way.
Later, to access our list of words from the main code, we will use: wordlist.Words and to access the clues, we will use wordlist.Clues.
Save WordList.as and close it and we can concentrate on our main code again.
Step 6
We haven’t tested our movie yet. First set up an empty SetUp() function after our Main() function:
public function SetUp(){}
Then test the movie by pressing CTRL+ENTER.
Note:
1 You must save Main.as before testing, or the recent changes will not be in effect. (This doesn’t apply if you put the code directly into the .fla.)
2 When you go to the second screen, the hangman graphic and the banner will flicker, if you have followed the instructions to the letter. Don’t worry, we will sort that out in just a sec.
Let’s flesh out the SetUp() function.
Step 7
Here is our SetUp() function fleshed out:
public function SetUp(){
DefeatCounter=0;
Man.gotoAndStop(1);
trace(wordlist.Words);trace(wordlist.Clues);
ChooseAWord();
SplitIntoLetters();
Display();
AddControls();
}//setup
First, we set DefeatCounter to 0. We will use this variable to track the number of incorrect answers. When it equals six, we will halt the game and the player loses.
Next, we stop our Man graphic on the first frame, so that only the empty noose is showing.
Then we can trace our list of words and clues to make sure our WordList class is functioning correctly.
The last four lines all call the remaining functions necessary to begin the game. Sure, we could combine them into one big function, but it’s good coding to use many smaller functions – they are more reusable and easier to debug. It also makes our code more readable to others.
You can test out the movie at this time, but you will need to deactivate the function calls using ‘//’ as such:
//ChooseAWord();
//SplitIntoLetters();
//Display();
//AddControls();
Now reactivate the call to ChooseAWord(); and we’ll look at how to choose a word at random.
public function ChooseAWord(){
rnd=Math.random()*wordlist.Words.length;
CurrentWord=wordlist.Words[rnd];
CurrentClue=wordlist.Clues[rnd];
trace(CurrentWord);trace(CurrentClue);
}//chooseaword
To walk you through it: First we use a variable rnd to choose a random number between 0 and the length of our word list minus 1. Note that the random number starts at 0, not one. This is actually handy since arrays in flash are zero-indexed – the first item is at position zero, second item at position 1 etc.
Another thing to note is that we previously defined rnd as an integer. If we had defined rnd as a Number, the random number would have decimal places and create an error later.
We use our random number, rnd, to choose a word and a corresponding clue, then trace them to check it’s working. Simple! Test it out.
Step 8
Activate the call to SplitIntoLetters(); by removing ‘//’ and let’s do it. We need to split our word into letters and store them somewhere. Here goes:
public function SplitIntoLetters(){
Letters=CurrentWord.split("");
trace(Letters);
}//splitintoletters
Well, that was easy, wasn’t it. The split() function is really handy for creating word games. string.split(“”) splits a string into characters. string.split(“ “); is handy for splitting a sentence into words. string.split(“.”) could be used to split a paragraph into sentences ,for example.
Step 9
Activate Display(); and let’s display our stuff:
public function Display(){
MessageBoard.visible=false;
DisplayClue.text=CurrentClue;
//show tiles
for(i=0;i<Letters.length;i++){
tile=new Tile;addChild(tile);
tile.x=300+(35*i);tile.y=250;
TileArray[i]=tile;
}//for i
}//display
To walk through it, we make our MessageBoard invisible to the user. Next we display our clue on the screen in the DisplayClue text box. Finally, we add the appropriate number of tiles (blanks) on the screen.
To do this, we create a new tile and add the instance to the screen. We use the iteration variable ‘i’ to manipulate the x position on the screen, starting at the point 300 and we use the y position 250. Play around with the numbers here to change the position on the screen where the tiles are displayed.
Finally, we use another array, TileArray, to hold a reference to the tile. Therefore, TileArray is an array of objects and we can use it to easily manipulate the various tiles. Using an array of objects is also a must for arcade-style games where you need to track enemies and bullets etc.
Test the movie again now.
Step 10
Activate the call to AddControls();
We need to be able to read keystrokes from the player and respond to them. To do this, we need to add an ‘event listener’ that will listen for any keystroke and a corresponding function to act on it.
public function AddControls(){
if(FirstPlayThrough){
FirstPlayThrough=false;
stage.focus=stage;
stage.addEventListener(KeyboardEvent.KEY_UP,keyUpListener);
function keyUpListener(e:KeyboardEvent):void {trace(e.keyCode);
UserKeyPress=ConvertToLetter(e.keyCode);
ProcessAnswer(UserKeyPress);
}//keyuplistener
}//if
}//controls
Okay, one step at a time. Earlier we defined a variable called FirstPlayThrough and set it to true. We use it here to add the game controls only if FirstPlayThrough==true. We wish to avoid the situation when we add another listener if we play the game again. This can lend itself to odd behavior.
Next, we have the line: stage.focus=stage; Previous to this, the user clicked the PLAY button, so we need to refocus any listener to the stage, otherwise the user will need to manually click on the flash movie before keystrokes are recognised.
Notice that the code for a key listener is a bit different from the code we attach to a button to listen for a mouse click.
We have set it so that two things happen when a key is pressed:
1 The result of a key press is recorded as a number, so we need to do something funky to convert it to a letter. UserKeyPress = ConvertToLetter(e.keyCode); - we send the key code to a function ConvertToLetter() and use the output (which will be a string) as the value assigned to UserKeyPress. At the end of this, if the user pressed ‘a’, then UserKeyPress=”a” and so on.
2 ProcessAnswer(UserKeyPress); - We need to send our letter to another function to process it and see if this letter is part of the word or not.
Step 11
So now we have two functions to write. Let’s start with the one that converts a key code to a letter. First, we should know that the code 65 corresponds to the letter A and 66 corresponds to letter B and so on.
public function ConvertToLetter(a){
Alphabet=["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"];
if(a>64 && a<91){
return(Alphabet[a-65]);
}//if
else{return("");}
}//convert
Here what we have done is used an Alphabet array to return the corresponding string. If the key code we were passed was less than 65 or more than 90, the function will simply return a blank string: “”. So if the user pressed any other key than a letter key, UserKeyPress=””;
That’s the first of our two functions, now we need to process the user’s keystroke. This will be the longest function for this particular game and it will control all the other functions that we need to complete the game.
So let’s build this function up, bit by bit. As we mentioned in Step 10, we will pass the function the UserKeyPress, which the function will understand as ‘a’:
public function ProcessAnswer(a){
}//processanswer
Now, we only want to bother processing the user’s submission if it’s a proper letter (a!=””) and if the game is in progress:
public function ProcessAnswer(a){
if(a!="" && !Lose && !Win){
}//if a!=""...
}//processanswer
If the game has already been won or lost, we will use the key input to begin a new game with a function we will write called RestartGame():
public function ProcessAnswer(a){
if(a!="" && !Lose && !Win){
}//if a!=""...
else{
if(Win || Lose){RestartGame();}
}//else
}//processanswer
To check if the user’s letter is in the word, we will first assume it is not found but setting Found=false; Then if it is actually found, we will change Found to true and display it onscreen. We need to check it against each entry in our Letters array:
public function ProcessAnswer(a){
if(a!="" && !Lose && !Win){
Found=false;
for (i=0;i<Letters.length;i++){
if(a==Letters[i]){Found=true; TileArray[i].Blank.text=a; }
}// for
if(Found){}
if(!Found && Unused(a)){ }//if !found
}//if a!=""...
else{
if(Win || Lose){RestartGame();}
}//else
}//processanswer
Finally, if the letter is found, we need to check if the game is won
If the letter is not found and the letter is not previously used, we need to 1) change our hangman graphic with AddBodyPart() 2) Display the user’s letter on the screen 3) Check if the game is over.
public function ProcessAnswer(a){
if(a!="" && !Lose && !Win){
Found=false;
for (i=0;i<Letters.length;i++){
if(a==Letters[i]){Found=true;TileArray[i].Blank.text=a;}
}// for
if(Found){CheckForWin();}
if(!Found && Unused(a)){AddBodyPart();
DisplayWrongLetter(a);
CheckForDefeat();
}//if !found
}//if a!=""...
else{
if(Win || Lose){RestartGame();}
}//else
}//processanswer
We have six more little functions to write. For testing purposes, let’s add empty functions for each case.
public function CheckForWin(){
}//Check for win
public function Unused(a){
}//unused
public function AddBodyPart(){
}//addbodypart
public function CheckForDefeat(){
}//check for defeat
public function DisplayWrongLetter(a){
}//displaywrongletter
public function RestartGame(){
}//restartgame
We can test the game now. At this point, the correct letters will show up on the screen, but not the wrong ones.
Step 12
There are six little functions left, all quite simple.
Okay, then, to check if the game is won:
public function CheckForWin(){
Win=true;
for (i=0;i<Letters.length;i++){
if(TileArray[i].Blank.text!=Letters[i]){Win=false;}
}//for i
if(Win){MessageBoard.visible=true;
MessageBoard.gotoAndStop(1);
}//if win
}//Check for win
We assume the game is won (Win=true;) and then we check the letters displayed on the tiles against out Letters array one by one. If any do not match, then we change Win back to false. After that, if Win is still true, we display the MessageBoard at frame 1. Test out the game again. Now if you get all the letters, the victory message should be displayed.
Next, the function to check if a character has been previously used (because we don’t want to punish the player for pressing the wrong letter twice):
public function Unused(a){
for (i=0;i<UsedCharacters.length;i++){
if(UsedCharacters[i]==a){return false;}
}//for i
return true;
}//unused
We take the UsedCharacters array and check if it contains our letter. If it does, we return ‘false’ and the function stops, otherwise, we return ‘true’.
Our function to change the graphics is dead simple, we just advance our movie clip ‘Man’ to the next frame:
public function AddBodyPart(){
Man.nextFrame();
}//addbodypart
Test the movie again. You should see the graphics change when you give a wrong answer.
To check to see if the game is lost, we use this function:
public function CheckForDefeat(){
DefeatCounter++;
if(DefeatCounter==6){Lose=true;
MessageBoard.visible=true;
MessageBoard.gotoAndStop(2);}
}//check for defeat
First we increase the value of DefeatCounter, because we only access this function if a wrong letter has been entered. Next, the game is lost if DefeatCounter==6, because the user is allowed six tries. When the game is lost, we set Lose to true, which will prevent more guesses being entered and we will display the MessageBoard at frame 2, where the You Lose! Message is.
Test the movie and lose the game on purpose.
To display the list of incorrectly answered letters:
public function DisplayWrongLetter(a){
UsedCharacters[UsedCharacters.length]=a;
BadLetters.text="";
for(i=0;i<UsedCharacters.length;i++){
BadLetters.appendText(UsedCharacters[i]+" ");
}//fori
}//displaywrongletter
First we add the wrong letter to our list (in array format). Notice the code:
UsedCharacters[UsedCharacters.length]=a;
Because the array is zero-indexed, the length can be used to open a new slot. For example, if the array is equal to [“q”,”z”], then the length is 2 and the current slots are 0 and 1.
Following this, we first empty the textbox, BadLetters.text=””; and then build it up again using our array and being careful to add a space between each letter.
Test the movie again. The incorrect letters are now displayed. Also, since we have filled up our UsedCharacters array, the user will not be punished for two incorrect guesses of the same letter.
Everything is done except the bit where we restart the game. Here we go:
public function RestartGame(){
Win=false;Lose=false;BadLetters.text="";
for(i=0;i<Letters.length;i++){
removeChild(TileArray[i]);TileArray[i]=null;
}//for i
Letters=[];UsedCharacters=[];TileArray=[];
SetUp();
}//restartgame
To reset the game, we basically reset all the values and remove all the tiles from the screen and then we call our previous function SetUp(); Like the circle of life, it all begins again.
So there we have it, one fully working Hangman game. I hope you’ve enjoyed the tutorial. If so and you want to support my efforts, you can do it through Link Love. ‘Like it’ on Facebook, Tweet it, post a link on your blog, that sort of thing. I give full permission to reuse the code as you wish – but not the tutorial. Anyway, take care and happy coding.
=====================================================================================
Completed Code
Main.as
package {
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.events.KeyboardEvent;
public class Main extends MovieClip {
public var wordlist:WordList;
public var CurrentWord:String;
public var CurrentClue:String;
public var rnd:int;
public var Letters:Array=new Array;
public var i:int;
public var tile:Tile;
public var TileArray:Array=new Array;
public var UserKeyPress:String;
public var Alphabet:Array=new Array;
public var Found:Boolean;
public var Win:Boolean;
public var Lose:Boolean;
public var DefeatCounter:int;
public var UsedCharacters:Array=new Array;
public var FirstPlayThrough:Boolean= true;
public function Main() {
stop();
wordlist=new WordList;
PlayButton.addEventListener(MouseEvent.CLICK, ButtonAction1);
function ButtonAction1(eventObject:MouseEvent) {gotoAndStop(2); SetUp();}
}//main
public function SetUp(){
DefeatCounter=0;
Man.gotoAndStop(1);
trace(wordlist.Words);trace(wordlist.Clues);
ChooseAWord();
SplitIntoLetters();
Display();
AddControls();
}//setup
public function ChooseAWord(){
rnd=Math.random()*wordlist.Words.length;
CurrentWord=wordlist.Words[rnd];
CurrentClue=wordlist.Clues[rnd];
trace(CurrentWord);trace(CurrentClue);
}//chooseaword
public function SplitIntoLetters(){
Letters=CurrentWord.split("");
trace(Letters);
}//splitintoletters
public function Display(){
MessageBoard.visible=false;
DisplayClue.text=CurrentClue;//embed chars
//show tiles
for(i=0;i<Letters.length;i++){
tile=new Tile;addChild(tile);
tile.x=300+(35*i);tile.y=250;
TileArray[i]=tile;
}//for i
}//display
public function AddControls(){
if(FirstPlayThrough){
FirstPlayThrough=false;
stage.focus=stage;
stage.addEventListener(KeyboardEvent.KEY_UP,keyUpListener);
function keyUpListener(e:KeyboardEvent):void {trace(e.keyCode);
UserKeyPress=ConvertToLetter(e.keyCode);
ProcessAnswer(UserKeyPress);
}//keyuplistener
}//if
}//controls
public function ConvertToLetter(a){
Alphabet=["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"];
if(a>64 && a<91){
return(Alphabet[a-65]);
}//if
else{return("");}
}//convert
public function ProcessAnswer(a){
if(a!="" && !Lose && !Win){
Found=false;
for (i=0;i<Letters.length;i++){
if(a==Letters[i]){TileArray[i].Blank.text=a;Found=true;}
}// for
if(Found){CheckForWin();}
if(!Found && Unused(a)){AddBodyPart();
DisplayWrongLetter(a);
CheckForDefeat();
}//if !found
}//if a!=""...
else{
if(Win || Lose){RestartGame();}
}//else
}//processanswer
public function CheckForWin(){
Win=true;
for (i=0;i<Letters.length;i++){
if(TileArray[i].Blank.text!=Letters[i]){Win=false;}
}//for i
if(Win){MessageBoard.visible=true;
MessageBoard.gotoAndStop(1);
}//if win
}//Check for win
public function Unused(a){
for (i=0;i<UsedCharacters.length;i++){
if(UsedCharacters[i]==a){return false;}
}//for i
return true;
}//unused
public function AddBodyPart(){
Man.nextFrame();
}//addbodypart
public function CheckForDefeat(){
DefeatCounter++;
if(DefeatCounter==6){Lose=true;
MessageBoard.visible=true;
MessageBoard.gotoAndStop(2);}
}//check for defeat
public function DisplayWrongLetter(a){
UsedCharacters[UsedCharacters.length]=a;
BadLetters.text="";
for(i=0;i<UsedCharacters.length;i++){
BadLetters.appendText(UsedCharacters[i]+" ");
}//fori
}//displaywrongletter
public function RestartGame(){
Win=false;Lose=false;BadLetters.text="";
for(i=0;i<Letters.length;i++){
removeChild(TileArray[i]);TileArray[i]=null;
}//for i
Letters=[];UsedCharacters=[];TileArray=[];
SetUp();
}//restartgame
}}
=====================================================================================
WordList.as
package {
import flash.display.MovieClip;
public class WordList extends MovieClip {
public var Words:Array = new Array;
public var Clues:Array = new Array;
public function WordList() {
Words=["kangaroo","macaroni","starbucks","washington","turtle","guillotine"];
Clues=["an animal","a kind of food","a company","a place","a creature","a machine"]
}//wl
}
}