Learning ActionScript's Basic Game Framework by Creating A Matching Game
- Placing Interactive Elements
- Game Play
- Encapsulating the Game
- Adding Scoring and a Clock
- Adding Game Effects
- Modifying the Game
- Placing Interactive Elements
- Game Play
- Encapsulating the Game
- Adding Scoring and a Clock
- Adding Game Effects
- Modifying the Game
To build our first game, I've chosen one of the most popular games you will find on the Web and in interactive and educational software: a matching game.
Matching games are simple memory games played in the physical world using a simple deck of cards with pictures on them. The idea is to place pairs of cards face down in a random arrangement. Then, try to find matches by turning over two cards at a time. When the two cards match, they are removed. If they don't match, they are turned face down again.
A good player is one who remembers what cards he or she sees when a match is not made, and can determine where pairs are located after several failed tries.
Computer versions of matching games have advantages over physical versions: You don't need to collect, shuffle, and place the cards to start each game. The computer does that for you. It is also easier and less expensive for the game developer to create different pictures for the cards with virtual cards rather than physical ones.
To create a matching game, we first work on placing the cards on the screen. To do this, we need to shuffle the deck to place the cards in a random order each time the game is played.
Then, we take the player's input and use that to reveal the pictures on a pair of cards. Then, we compare the cards and remove them if they match.
We also need to turn cards back to their face-down positions when a match is not found. And then we need to check to see when all the pairs have been found so that the game can end.
Placing Interactive Elements
Creating a matching game first requires that you create a set of cards. Because the cards need to be in pairs, we need to figure out how many cards will be displayed on the screen, and make half that many pictures.
For instance, if we want to show 36 cards in the game, there will be 18 pictures, each appearing on 2 cards.
Methods for Creating Game Pieces
There are two schools of thought when it comes to making game pieces, like the cards in the matching game.
Multiple-Symbol Method
The first method is to create each card as its own movie clip. So, in this case, there will be 18 symbols. Each symbol represents a card.
One problem with this method is that you will likely be duplicating graphics inside of each symbol. For instance, each card would have the same border and background. So, you would have 18 copies of the border and background.
Of course, you can get around this by creating a background symbol that is then used in each of the 18 card symbols.
But the multiple-symbol method still has problems when it comes to making changes. For instance, suppose you want to resize the pictures slightly. You'd need to do that 18 times for 18 different symbols.
Also, if you are a programmer teaming up with an artist, it is inconvenient to have the artist update 18 or more symbols. If the artist is a contractor, it could run up the budget as well.
Single-Symbol Method
The second method for working with a set of playing pieces, such as cards, is a single-symbol method. You would have one symbol, a movie clip, with multiple frames. Each frame contains the graphics for a different card. Shared graphics, such as a border or background, can be on a layer in the movie clip that stretches across all the frames.
This method has major advantages when it comes to updates and changes to the playing pieces. You can quickly and easily move between and edit all the frames in the movie clip. You can also easily grab an updated movie clip from an artist with whom you are working.
Setting Up the Flash Movie
Using the single-symbol method, we need to have at least one movie clip in the library. This movie clip will contain all the cards, and even a frame that represents the back of the card that we must show when the card is face down.
Create a new movie that contains a single movie clip called Cards. To create a new movie in Flash CS3, choose File, New, and then you will be presented with a list of file types. You must choose Flash File (ActionScript 3.0) to create a movie file that will work with the ActionScript 3.0 class file we are about to create.
Put at least 19 frames in that movie clip, representing the card back and 18 card fronts with different pictures on them. You can open the MatchingGame1.fla file for this exercise if you don't have your own symbol file to use.
Figure 3.1 shows a timeline for the Card movie clip we will be using in this game. The first frame is "back" of the card. It is what the player will see when the card is supposed to be face down. Then, each of the other frames shows a different picture for the front of a card.
Figure 3.1 The Card movie clip is a symbol with 37 frames. Each frame represents a different card.
After we have a symbol in the library, we need to set it up so that we can use it with our ActionScript code. To do this, we need to set its properties by selecting it in the library and bringing up the Symbol Properties dialog box (see Figure 3.2).
Figure 3.2 The Symbol Properties dialog box shows the properties for the symbol Card.
Set the symbol name to Card and its type to Movie Clip. For ActionScript to be able to work with the Cards movie clip, it needs to be assigned a class. By checking the Export for ActionScript box, we automatically get the class name Card assigned to the symbol. This will be fine for our needs here.
There is nothing else needed in the Flash movie at all. The main timeline is completely empty. The library has only one movie clip in it, the Cards movie clip. All that we need now is some ActionScript.
Creating the Basic ActionScript Class
To create an ActionScript class file, choose File, New, and then select ActionScript File from the list of file types; by doing so you create an untitled ActionScript document that you can type into.
We start off an ActionScript 3.0 file by defining it as a package. This is done in the first line, as you can see in the following code sample:
package { import flash.display.*;
Right after the package declaration, we need to tell the Flash playback engine what classes we need to accomplish our tasks. In this case, we go ahead and tell it we'll be needing access to the entire flash.display class and all its immediate subclasses. This will give us the ability to create and manipulate movie clips like the cards.
The class declaration is next. The name of the class must match the name of the file exactly. In this case, we call it MatchingGame1. We also need to define what this class will affect. In this case, it will affect the main Flash movie, which is a movie clip:
public class MatchingGame1 extends MovieClip {
Next is the declaration of any variables that will be used throughout the class. However, our first task of creating the 36 cards on the screen is so simple that we don't need to use any variables. At least not yet.
Therefore, we can move right on to the initialization function, also called the constructor function. This function runs as soon as the class is created when the movie is played. It must have exactly the same name as the class and the ActionScript file:
public function MatchingGame1():void {
This function does not need to return any value, so we can put :void after it to tell Flash that nothing will ever be returned from this function. We can also leave the :void off, and it will be assumed by the Flash compiler.
Inside the constructor function we can perform the task of creating the 36 cards on the screen. We'll make it a grid of 6 cards across by 6 cards down.
To do this, we use two nested for loops. The first moves the variable x from 0 to 5. The x will represent the column in our 6x6 grid. Then, the second loop will move y from 0 to 5, which will represent the row:
for(var x:uint=0;x<6;x++) { for(var y:uint=0;y<6;y++) {
Each of these two variables is declared as a uint, an unsigned integer, right inside the for statement. Each will start with the value 0, and then continue while the value is less than 6. And, they will increase by one each time through the loop.
So, this is basically a quick way to loop and get the chance to create 36 different Card movie clips. Creating the movie clips is just a matter of using new, plus addChild. We also want to make sure that as each new movie clip is created it is stopped on its first frame and is positioned on the screen correctly:
var thisCard:Card = new Card(); thisCard.stop(); thisCard.x = x*52+120; thisCard.y = y*52+45; addChild(thisCard); } } } } }
The positioning is based on the width and height of the cards we created. In the example movie MatchingGame1.fla, the cards are 50 by 50 with 2 pixels in between. So, by multiplying the x and y values by 52, we space the cards with a little extra space between each one. We also add 120 horizontally and 45 vertically, which happens to place the card about in the center of a 550x400 standard Flash movie.
Before we can test this code, we need to link the Flash movie to the ActionScript file. The ActionScript file should be saved as MatchingGame1.as, and located in the same directory as the MatchingGame1.fla movie.
However, that is not all you need to do to link the two. You also need to set the Flash movie's Document class property in the Property Inspector. Just select the Properties tab of the Property Inspector while the Flash movie MatchingGame1.fla is the current document. Figure 3.3 shows the Property Inspector, and you can see the Document class field at the bottom right.
Figure 3.3 You need to set the Document class of a Flash movie to the name of the AS file that contains your main script.
Figure 3.4 shows the screen after we have tested the movie. The easiest way to test is to go to the menu and choose Control, Test Movie.
Figure 3.4 The screen shows 36 cards, spaced and in the center of the stage.
Using Constants for Better Coding
Before we go any further with developing this game, let's look at how we can make what we have better. We'll copy the existing movie to MatchingGame2.fla and the code to MatchingGame2.as. Remember to change the document class of MatchingGame2.fla to MatchingGame2 and the class declaration and constructor function to MatchingGame2.
Suppose you don't want a 6x6 grid of cards. Maybe you want a simpler 4x4 grid. Or even a rectangular 6x5 grid. To do that, you just need to find the for loops in the previous code and change the loops so that they loop with different amounts.
A better way to do it is to remove the specific numbers from the code all together. Instead, have them at the top of your code, and clearly labeled, so that you can easily find and change them later on.
We've got several other hard-coded values in our programs. Let's make a list.
Horizontal Rows = 6 |
Vertical Rows = 6 |
Horizontal Spacing = 52 |
Vertical Spacing = 52 |
Horizontal Screen Offset = 120 |
Vertical Screen Offset = 45 |
Instead of placing these values in the code, let's put them in some constant variables up in our class, to make them easy to find and modify:
public class MatchingGame2 extends MovieClip { // game constants private static const boardWidth:uint = 6; private static const boardHeight:uint = 6; private static const cardHorizontalSpacing:Number = 52; private static const cardVerticalSpacing:Number = 52; private static const boardOffsetX:Number = 120; private static const boardOffsetY:Number = 45;
Now that we have constants, we can replace the code in the constructor function to use them rather than the hard-coded numbers:
public function MatchingGame2():void { for(var x:uint=0;x<boardWidth;x++) { for(var y:uint=0;y<boardHeight;y++) { var thisCard:Card = new Card(); thisCard.stop(); thisCard.x = x*cardHorizontalSpacing+boardOffsetX; thisCard.y = y*cardVerticalSpacing+boardOffsetY; addChild(thisCard); } } }
You can see that I also changed the name of the class and function to MatchingGame2. You can find these in the sample files MatchingGame2.fla and MatchingGame2.as.
In fact, open those two files. Test them one time. Then, test them again after you change some of the constants. Make the boardHeight only five cards, for instance. Scoot the cards down by 20 pixels by changing boardOffsetY. The fact that you can make these changes quickly and painlessly drives home the point of using constants.
Shuffling and Assigning Cards
Now that we can add cards to the screen, we want to assign the pictures randomly to each card. So, if there are 36 cards in the screen, there should be 18 pairs of pictures in random positions.
Chapter 2, "ActionScript Game Elements," discussed how to use random numbers. However, we can't just pick a random picture for each card. We need to make sure there are exactly two of each type of card on the screen. No more, no less; otherwise, there will not be matching pairs.
To do this, we need to create an array that lists each card, and then pick a random card from this array. The array will be 36 items in length, containing 2 of each of the 18 cards. Then, as we create the 6x6 board, we'll be removing cards from the array and placing them on the board. When we have finished, the array will be empty, and all 18 pairs of cards will be accounted for on the game board.
Here is the code to do this. A variable i is declared in the for statement. It will go from zero to the number of cards needed. This is simply the board width times the board height, divided by two (because there are two of each card). So, for a 6x6 board, there will be 36 cards. We must loop 18 times to add 18 pairs of cards:
// make a list of card numbers var cardlist:Array = new Array(); for(var i:uint=0;i<boardWidth*boardHeight/2;i++) { cardlist.push(i); cardlist.push(i); }
The push command is used to place a number in the array, twice. Here is what the array will look like:
0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,14,14,15,15,16,16,17,17
Now as we loop to create the 36 movie clips, we'll pull a random number from this list to determine which picture will display on each card:
for(var x:uint=0;x<boardWidth;x++) { // horizontal for(var y:uint=0;y<boardHeight;y++) { // vertical var c:Card = new Card(); // copy the movie clip c.stop(); // stop on first frame c.x = x*cardHorizontalSpacing+boardOffsetX; // set position c.y = y*cardVerticalSpacing+boardOffsetY; var r:uint = Math.floor(Math.random()*cardlist.length); // get a random face c.cardface = cardlist[r]; // assign face to card cardlist.splice(r,1); // remove face from list c.gotoAndStop(c.cardface+2); addChild(c); // show the card } }
The new lines are in the middle of the code. First, we use this line to get a random number between zero and the number of items remaining in the list:
var r:uint = Math.floor(Math.random()*cardlist.length);
The Math.random() function will return a number from 0.0 up to just before 1.0. Multiply this by cardlist.length to get a random number from 0.0 up to 35.9999. Then use Math.floor() to round that number down so that it is a whole number from 0 to 35—that is, of course, when there are 36 items in the cardlist array at the start of the loops.
Then, the number at the location in cardlist is assigned to a property of u named cardface. Then, we use the splice command to remove that number from the array so that it won't be used again.
In addition, the MatchingGame3.as script includes this line to test that everything is working so far:
c.gotoAndStop(c.cardface+2);
This syntax makes the Card movie clip show its picture. So, all 36 cards will be face up rather than face down. It takes the value of the property cardface, which is a number from 0 to 17, and then adds 2 to get a number from 2 to 19. This corresponds to the frames in the Card movie clip, where frame 1 is the back of the card, and frames 2 and so on are the picture faces of the cards.
Obviously, we don't want to have this line of code in our final game, but it is useful at this point to illustrate what we have accomplished. Figure 3.5 shows what the screen might look like after we run the program with this testing line in place.
Figure 3.5 The third version of our program includes code that reveals each of the cards. This is useful to get visual confirmation that your code is working so far.