Making a Customizable Word Scramble Game in AS3


Source files: scramble.zip

Art and Graphics: art.fla


What you will learn:

Prerequisites:


We want to make a word scramble quiz in Flash AS3 where we drag and drop the options into place. What’s more, we may want to reuse our code for further quizzes. How can we do all this? The answers are right here in this tutorial. Read on.


Step 1


People have a funny habit of making tiny little Flash movies. But we want our quiz to be big and exciting. Open up Flash and modify the stage size to 600 x 800. This way, we get a nice big stage, but it will still display on laptop screens. The frame rate needs to be set to 30 frames per second or else we will get a jerky motion when dragging the tiles.
i1
Fine. Save the file under the name ‘scramble.fla’ and let’s move on.

Step 2


We’re going to store our code in a separate document, so we need to create a document class that we call Game.as. Click FILE>>NEW and create the document as follows:

i2

Save Game.as in a folder called classes. Now, we need to make sure that Flash looks in this folder for our code. From scramble.fla, here is how we do this:

1 Click CTRL+SHIFT+F12
2 Click on the tab that says FLASH
3 Click SETTINGS next to where it says Actionscript 3.0
4 Click the plus sign
5 Enter ./classes
6 Next to Document class, type Game

 

 

You should now see this:
i3

 

Step 3

Our documents and folders are in place. Now let’s do some artwork. We’ll need to design a tile that we can use to place the letters on. Here’s the one I designed, you can grab this image and all the following ones from: http://www.flashbynight.com/tutes/scramble/art.fla.


i4

You can use your own image, but make sure it’s 45 x 45 px.
We’ll need to display a letter on the tile, so first convert it to a movie clip (right click then Convert to Symbol). Then double click on it and add a dynamic text box with the instance name letter. Test out the placement by adding the letter x. I used Century Gothic 36-point bold white font to get the following:


i5 

Don’t place the image on the stage, but make sure it is in the library as a movie clip. We need to give it a name so that we can place its children on the stage. Give it the class name ‘Tile’ with a capital T, by right-clicking on the symbol in the library and selecting Linkage, then completing the information as follows:

i6

We need a target area to drop the tile onto. I’m using this:
i7
It’s a 40 x 40 image and I’ve assigned it the class name Placeholder (capital P).
The next art that we need is some way to give feedback. We could have crosses or ticks, but I’ll opt for two boxes as follows:
i8
Make sure they are converted to movie clips and have the class names feedback1 and feedback2
respectively.
We’ll use a similar image to inform the player that the activity is finished. Give this (or a similar image) the class name feedback3:
i9

Finally, we’ll need a button to click for the user to check the answer. I’m using the following (shown in ‘up’ and ‘over’ states:

i10
Give it a class name of button_chk and make sure it’s in the library but not on the stage, as before.

Step 4

Open up Game.as and let’s get to work on the code. We’ll set up the document class as follows:

package {
         import flash.display.MovieClip;
         import flash.text.TextField;         
         import flash.events.MouseEvent;           
               
         public class Game extends MovieClip {
                                               
                                                               
         public function Game() {trace("test");
                                               
         }
                               
  }
                               
}

Save it and compile the swf (CTRL + ENTER). The trace statement is just to test it is working. You can delete it now.

Step 5


It’s time to declare some variables. We will use a lot of them and some of them are arrays. Here goes:

 

var words:Array = new Array; //a mini database to hold the words
var rand1:int;var rand2:int; //variables used for randomization
var i:int; //variable used for loop iterations
var ii:int;
var ques_num:int; //used to track the question number
var current_word:String; //used to hold the current word being tested
var user_ans:String; //used to hold the user's answer
var tileArray:Array = new Array; //array of tile objects
var targetArray:Array = new Array; //array of target areas
var scramble_Array:Array = new Array; //array used to scramble the word
var unscramble_Array:Array = new Array; //array to hold the unscrambled word
var temp:String; //temporary variable
var flag:Boolean; //flag variable
var checker:button_chk; //CHECK button
var f1:feedback1; //feedback boxes 1 - 3
var f2:feedback2;
var f3:feedback3;
var pauser:Boolean; //used to pause the game



Step 6

Let’s plan the workflow. We need the following functions:
1 get the word to be scrambled
2 set the tiles
3 scramble the word
4 add ‘listeners’
5 pick up tiles when clicked
6 drop tiles into place
7 check whether answer is correct
8 unpause the game if the player needs to try again
9 clear the board and go back to step 1 or end the game

So we can put the functions into place before adding the actual script. This will help us to plan the code better:

package {
        import flash.display.MovieClip;
        import flash.text.TextField;
        import flash.events.MouseEvent;        


        public class Game extends MovieClip {
             var words:Array = new Array;
             var rand1:int;var rand2:int;
             var i:int;
             var ii:int;
             var ques_num:int;
             var current_word:String;
             var user_ans:String;
             var tileArray:Array = new Array;
             var targetArray:Array = new Array;
             var scramble_Array:Array = new Array;
             var unscramble_Array:Array = new Array;
             var temp:String;
             var flag:Boolean;
             var checker:button_chk;
             var f1:feedback1;
             var f2:feedback2;
             var f3:feedback3;
             var pauser:Boolean;


       public function Game() {
                               getword();
                              }
                               
       public function getword() {
                                               
                                }//getword                               

       public function setTiles() {
                                               
                                }//setTiles
                               
       public function scramble_word() {
                                               
                                }//scramble_word
                               
       public function addListeners() {
                                               
                                }//addListeners

       public function pickup() {
                                               
                                }//pick up
                               
       public function drop() {
                                               
                                }//drop
                               
       public function check_answer() {
                                               
                                }//check_answer
                               
       public function continue_on() {
                                               
                                }//continue_on   

       public function clear_board() {
                                               
                                }//clear_board
                                               
  }                             
}

   

Step 7

Let’s keep it simple for the getword() function. We will test out our game with two words: ‘candid’ and ‘camera’, which we can store in an array as follows:


words=["candid","camera"];

We’ll use the variable ques_num to track which word to use. Since we defined ques_num as an integer, but haven’t set a value, it will have the default value of 0. We can then use it to select a word from the array. This code will do the trick:


public function getword() {
                           words=["candid","camera"];
                           current_word=words[ques_num];
                           setTiles(current_word.length);
                           ques_num++;
}//getword

Step 8


You could see from Step 7 that we passed a variable to the setTiles function, this variable representing the length of the word. Let’s flesh out the setTiles function as follows:

public function setTiles(a) {
                             tileArray=[ ];
                             for(i=0;i<a;i++){
                                        var tempclip:Tile =new                                                                          Tile;addChild(tempclip);                                                                tempclip.x=100+(i*60);tempclip.y=200;                                         tempclip.tag=i;                                                                                tempclip.original_posx=tempclip.x;                                          tempclip.original_posy=tempclip.y;
                                        tileArray[i]=tempclip;
                             }//for i
                             scramble_word(a);
}//setTiles

Basically, here we set a loop equal to the number of letters. Each time we run through the loop, we add a tile and assign it the temporary name ‘tempclip’. We set the position on the screen: 60px is the spacing, with the first tile at x position 100 and all tiles at y position 200. You can adjust these values to suit your own purposes. We sneak in a variable called ‘tag’ so that we can identify the clips easily later on. We also note down the original x and y position of each clip, so that later we can snap it back into place.
Finally, we add each tile to an array. Now, whenever we wish to refer to a tile, we can reference it using the array. For example, if we wish to make the third tile invisible, we say tileArray[2].visible=false; (Remember that arrays are zero-referenced).
Let’s add a couple more lines to also set the targets into place. These three lines should come before the line }//for i :

                                                                var tempclip2:Placeholder =new Placeholder;addChild(tempclip2);
                                                                tempclip2.x=100+(i*60);tempclip2.y=280;
                                                                targetArray[i]=tempclip2;

Following the loop, we call the scramble_word function and pass it the value a, which corresponds to the length of the word.

Step 9


This is what I have so far:
i11
We need to add letters to the tiles and also scramble them. We will use the scramble_word function for this, as follows:


public function scramble_word(a) {
                             for(i=0;i<a;i++){scramble_Array[i]=current_word.slice(i,i+1);}
                             for(i=0;i<15;i++){
                                              rand1=Math.ceil(Math.random()*(a))-1;
                                              rand2=Math.ceil(Math.random()*(a))-1;             
                                              temp=scramble_Array[rand1];
                                              scramble_Array[rand1]=scramble_Array[rand2];
                                              scramble_Array[rand2]=temp;
                             }
                             trace(scramble_Array);
}//scramble_word

Can you see what we’re doing here? First we slice the word into letters, using the ‘slice’ command. Now we have an array that contains the separate letters. Next we run a loop fifteen times. In this loop, we switch any two letters at random. By the end of the loop, the word is well-scrambled. After testing it, delete the trace command and replace it with this line:


for(i=0;i<a;i++){tileArray[i].letter.text=scramble_Array[i];}


This line assigns the scrambled letters to the dynamic text fields on each tile. When I compile the movie, I get this:
i12
Of course, you get a different order of letters each time you try.
Finally, add this line:


addListeners(a);


We will use this to call a function to add listeners.

Step 10


We need to sort out the drag and drop functionality. First, let’s write a function to ‘listen’ to when a tile is clicked on and released:


public function addListeners(a){
                                for(i=0;i<a;i++){
                                   tileArray[i].mouseChildren = false;
                                   tileArray[i].addEventListener(MouseEvent.MOUSE_DOWN, pickup);
                                   tileArray[i].addEventListener(MouseEvent.MOUSE_UP, drop);
                                }//fori
}


Again, we loop through the tiles and we use tileArray to add behaviors.

tileArray[i].mouseChildren = false;

This line ensures we click on the tiles and not the text inside the tiles.
The following two lines arrange to call a function ‘pickup’ when a tile is clicked on and ‘drop’ when it is released.
Let’s flesh out these functions:


public function pickup(event:MouseEvent){
                       if(!pauser){
                               event.target.startDrag(true);
                               this.setChildIndex(MovieClip(event.target),this.numChildren-1);
                       }//pauser
}//pick up
                               
public function drop(event:MouseEvent) {
                                       event.target.stopDrag();
}//drop

The pickup function is simple: we set it to start dragging the tile. The other line swaps the depth of the tile (so it does not go behind the other tiles) by manipulating setChildIndex. We will only run the function if pauser is not true – we can use this to pause the game.
So far, with the drop function, we simply stop dragging the tile. If you test the game now, the drag and drop function works, but we can leave the tiles scattered about the board. We want the tile to snap into one of the designated places, failing which, it should snap back to where it was dragged from. Let’s do that now.


Step 11


Here is the full version of the drop function:


public function drop(event:MouseEvent) {
       event.target.stopDrag();
       flag=false;
       for(i=0;i<targetArray.length;i++){
              if(targetArray[i].hitTestObject(event.target)){event.target.x=targetArray[i].x;
              event.target.y=targetArray[i].y;flag=true;}
       }//for i
       for(i=0;i<tileArray.length;i++){
              if((tileArray[i].hitTestObject(event.target))&&(tileArray[i]!=event.target)){
                                flag=false;}

       }//for i
       if(!flag){
              event.target.x=event.target.original_posx;
              event.target.y=event.target.original_posy;}
}//drop

The code here is pretty complex, but not too complex for us to understand, right? First we set our flag variable to false. By the end of the function, it will be true if the tile has been dropped onto a target and false if dropped into empty space. It will also be false if a tile is dropped onto another tile. If it is false, we will snap the tile back into place.
The first for loop tests to see if the tile is touching a target zone using hitTestObject, which is the standard hit test in AS3, used in game design. We refer to the tile being dropped as event.target (the target of the MouseEvent).
The next for loop uses the same technique to check if the tile is touching another tile. If so, the flag variable is set back to false.


The final line (if(!flag)), sets the tile back to its original position if the flag variable is false.
Test it out. Great, it works! The drag and drop functionality is done. Now we need to add a way to check the answer.


Step 12


Let’s go all the way back to the constructor function and modify it to add our check button to the stage:


public function Game() {
                       getword();
                       checker=new button_chk;addChild(checker);
                       checker.x=150;checker.y=400;
}


There we go, now we need to add functionality, meaning we need to add a listener for when the button is clicked, and we can use that listener to call our check_answer() function, which we will write in a moment. This line comes after the fourth line above:

checker.addEventListener(MouseEvent.CLICK, check_answer);

Now for the check_answer function:

public function check_answer(e:MouseEvent) {
      if(!pauser){
            user_ans="";
            for(i=0;i<targetArray.length;i++){
                 for(ii=0;ii<tileArray.length;ii++){
                   if((tileArray[ii].x==targetArray[i].x)&&(tileArray[ii].y==targetArray[i].y)){
                     user_ans+=tileArray[ii].letter.text;}
            }}//end of nested loop
                                                            if(user_ans==current_word){trace("correct");}else{trace("wrong");}
      }//end if !pauser
}//check_answer

The code in the function is wrapped in if(!pauser){} – so that the check functionality will not run when the game is paused between rounds.

So we start by making sure the user answer (user_ans) is “”. We then have a nested loop (a loop within a loop), which in turn checks every tile against every target area. Simply put, if the x and y coordinates match, the tile matches the target area and we record the letter by adding it to the user_ans string.
At the end of the loop, if the user_ans matches the current word, we know it is correct, or else it is false. For now we have just put traces there to test it. Now let’s set it to give feedback to the user:

public function check_answer(e:MouseEvent) {
      if(!pauser){
            user_ans="";
            for(i=0;i<targetArray.length;i++){
                 for(ii=0;ii<tileArray.length;ii++){
                   if((tileArray[ii].x==targetArray[i].x)&&(tileArray[ii].y==targetArray[i].y)){
                     user_ans+=tileArray[ii].letter.text;}
            }}//end of nested loop
            if(user_ans==current_word){f1=new feedback1; addChild(f1); f1.x=100;
                  f1.y=100;pauser=true;
                  f1.addEventListener(MouseEvent.CLICK, clear_board);
            }
            else{f2=new feedback2;addChild(f2);f2.x=100;f2.y=100;pauser=true;
                 f2.addEventListener(MouseEvent.CLICK, continue_on);
                 }
       }//end if !pauser
}//check_answer

So now instead of trace(“correct”), we display the pop up that says, ‘correct’, which we called feedback1. We add a listener, so that when we click on the pop up, we can clear the board and move onto the next word.


On the other hand, if the answer is wrong, we display feedback2. When this pop up is clicked, we will call the function ‘continue_on’ to unpause the game so the player can try again.


For both of these possible situations, we set pauser=true, so that the tiles cannot be dragged.

Step 13


The final two functions will either unpause the game or clear the current tiles and go onto the next word. Here is how we do it:

public function continue_on(e:MouseEvent) {
                f2.removeEventListener(MouseEvent.CLICK, continue_on);
                removeChild(f2);
                pauser=false;
}//continue on
                               
public function clear_board(e:MouseEvent) {
                f1.removeEventListener(MouseEvent.CLICK, continue_on);
                removeChild(f1);
                pauser=false;

                for(i=0;i<tileArray.length;i++){
                      removeChild(tileArray[i]);removeChild(targetArray[i]);}
                      if(ques_num==words.length){removeChild(checker);
                             f3=new feedback3;addChild(f3);f3.x=100;f3.y=100;}else{getword();}
}//clear_board

The continue_on function removes the feedback pop up and listener and unpauses.
The clear_board function removes the pop up and listener and unpauses the game. It then removes the tiles and target areas, before checking if there are any words left. If so, the game restarts, using getword();. If not, feedback is displayed to say the game is over.

Step 14


That’s it! It’s up and running and the swf file is less than 8k. I hope it works for you, and that you learned something. Feel free to use the code as you wish, although I would really appreciate a mention to my site flashbynight.com if you do use the code.
Have a look at flashbynight.com for some interesting little flash games, all made by yours truly.