- Placing Interactive Elements
- Game Play
- Encapsulating the Game
- Adding Scoring and a Clock
- Adding Game Effects
- Modifying the Game
Adding Scoring and a Clock
The goal of this chapter is to develop a complete game framework around the basic matching game. Two elements commonly seen in casual games are scoring and timers. Even though the matching game concept doesn't really need them, let's go ahead and add them to the game anyway to make it as full-featured as we can.
Adding Scoring
The first problem is deciding how scoring should work for a game like this. There isn't an obvious answer. However, there should be a positive reward for getting a match, and perhaps a negative response for missing. Because it is almost always the case that a player will miss more than he or she finds matches, a match should be worth far more than a miss. A good starting point is 100 points for a match and -5 points for a miss.
Instead of hard coding these amounts in the game, let's add them to the list of constants at the start of the class:
private static const pointsForMatch:int = 100; private static const pointsForMiss:int = -5;
Now, to display the score, we need a text field. Creating a text field is pretty straightforward, as you saw in Chapter 2. We first need to declare a new TextField object in the list of class variables:
private var gameScoreField:TextField;
Then, we need to create that text field and add it as a child:
gameScoreField = new TextField(); addChild(gameScoreField);
We could also format it and create a nicer-looking text field, as we did in Chapter 2, but we'll leave that part out for now.
The score itself will be a simple integer variable named gameScore. We'll declare it at the start of the class:
private var gameScore:int;
And then we'll set it to zero in the constructor function:
gameScore = 0;
In addition, it would be a good idea to immediately show the score in the text field:
gameScoreField.text = "Score: "+String(gameScore);
However, we realize at this point that there are at least several places in the code where we will set the text of gameScoreField. The first is in the constructor function. The second will be after the score changes during game play. Instead of copying and pasting the previous line of code in two places, let's move it to a function of its own. Then, we can call the same function from each of the places in the code where we need to update the score:
public function showGameScore() { gameScoreField.text = "Score: "+String(gameScore); }
We need to change the score in two places in the code. The first is right after we find a match, just before we check to see whether the game is over:
gameScore += pointsForMatch;
Then, we add an else clause to the if statement that checks for a match, and subtract points if the match is not found:
gameScore += pointsForMiss;
Here is the entire section of code so that you can see where these two lines fit in:
// compare two cards if (firstCard.cardface == secondCard.cardface) { // remove a match removeChild(firstCard); removeChild(secondCard); // reset selection firstCard = null; secondCard = null; // add points gameScore += pointsForMatch; showGameScore(); // check for game over cardsLeft -= 2; // 2 less cards if (cardsLeft == 0) { MovieClip(root).gotoAndStop("gameover"); } } else { gameScore += pointsForMiss; showGameScore(); }
Notice that we are adding points using the += operation, even if there is a miss. This is because the pointsForMiss variable is set to -5. So adding -5 is the same as subtracting 5 points.
We also put in the showGameScore() function call after each change to the score. This will make sure the player sees an up-to-date score, as shown in Figure 3.13.
Figure 3.13 The score now appears in the upper left, using the default font and style.
MatchingGame8.fla and MatchingGame8.as include this scoring code. Take a look to see it in action.
Adding a Clock
Adding a clock timer is a little harder than adding a score. For one thing, a clock needs to be updated constantly, as opposed to the score, which only needs to be updated when the user tries a match.
To have a clock, we need to use the getTimer() function. This will return the time since the Flash movie started, in milliseconds. This is a special function that requires a special Flash class that we need to import at the start of our program:
import flash.utils.getTimer;
Now we will need some new variables. We need one to record the time the game started. Then, we can simply subtract the current time from the start time to get the amount of time the player has been playing the game. We'll also use a variable to store the game time:
private var gameStartTime:uint; private var gameTime:uint;
We also need to define a new text field to display the time to the player:
private var gameTimeField:TextField;
In the constructor function, we add a new text field to display the time. We also move to the right side of the screen so that it won't be on top of the score display:
gameTimeField = new TextField(); gameTimeField.x = 450; addChild(gameTimeField);
Before the constructor function is done, we'll want to set the gameStartTime variable. We can also set the gameTime to zero:
gameStartTime = getTimer(); gameTime = 0;
Now we need to figure out a way for the game time to update. It will be changing constantly, so we don't want to wait for user action to display the time.
One way to do it is to create a Timer object, as in Chapter 2. However, it isn't critical that the clock be updated at regular intervals, only that the clock be updated often enough so that players get an accurate sense of how long they have been playing.
Instead of using a Timer, we can just have the ENTER_FRAME event trigger a function that updates the clock. In a default Flash movie, this will happen 12 times a second, which is certainly enough:
addEventListener(Event.ENTER_FRAME,showTime);
All that is left is to make the showTime function. It will calculate the current time based on the current value of getTimer() and the value of gameStartTime. Then, it will put it in the text field for display:
public function showTime(event:Event) { gameTime = getTimer()-gameStartTime; gameTimeField.text = "Time: "+gameTime; }
Figure 3.14 shows the screen with both the score and the current time. However, the time format uses a semicolon and two digits for the seconds. You'll see how to do this next.
Figure 3.14 The time is now displayed at the upper right.
Displaying Time
The showTime function displays the number of milliseconds since the game started. Typical players don't care about milliseconds; they want to see a normal clock, with minutes and seconds displayed as they would on a digital watch.
Let's break this out in another function. Instead of just including the raw gameTime in the text field as in the preceding code example, we can call a function to return a nicer output:
gameTimeField.text = "Time: "+clockTime(gameTime);
The idea is that the old code would show this:
Time: 123726
The new code will show:
Time: 2:03
The clockTime function will take the time in raw milliseconds and convert it to minutes and whole seconds. In addition, it will format it to use a colon (:) and make sure that a zero is placed correctly when the number of seconds is fewer than ten.
The function will start off by simply dividing the number of milliseconds by 1,000 to get the number of seconds. It will then divide that by 60 to get the number of minutes.
Next, it must subtract the minutes from the seconds. For instance, if there are 123 seconds, that means there are 2 minutes. So, subtract 2*60 from 123 to get 3 seconds left over. 123 is 2 minutes and 3 seconds:
public function clockTime(ms:int) { var seconds:int = Math.floor(ms/1000); var minutes:int = Math.floor(seconds/60); seconds -= minutes*60;
Now that we have the number of minutes and seconds, we want to make sure that we insert a colon between them, and make sure that the seconds are always two digits.
I use a trick to do this. The substr function enables you to grab a set number of characters from a string. So, the number of seconds will be between 0 and 59. Add 100 to that, and you have a number between 100 and 159. Grab the second and third characters from that as a string, and you have a range of 00 to 59. The following line is how it looks in ActionScript:
var timeString:String = minutes+":"+String(seconds+100).substr(1,2);
Now just return the value.
return timeString; }
The time will now display at the top of the screen in a familiar digital watch format, rather than just as a number of milliseconds.
Displaying Score and Time after the Game Is Over
Before we finish with MatchingGame9.fla, let's take the new score and time displays and carry them over to the gameover screen.
This is a little tricky because the gameover screen exists on the main timeline, outside of the game movie clip. To have the main timeline even know what the score and time are, this data needs to be sent from the game to the root level.
Just before we call the gotoAndStop command that will advance the movie to the gameover screen, we pass these two values up to root:
MovieClip(root).gameScore = gameScore; MovieClip(root).gameTime = clockTime(gameTime);
Notice that we pass the score up as a raw value, but we run the time through the handy clockTime function so that it is a string with a colon and a two-digit second.
At the root level, we need to define those new variables, which use the same names as the game variables: gameTime and gameScore. I've added this code to the first frame:
var gameScore:int; var gameTime:String;
Then, on the gameover frame, we use these variables to place values in new text fields:
showScore.text = "Score: "+String(gameScore); showTime.text = "Time: "+gameTime;
We don't need to use code to create the showScore and showTime dynamic text fields; we can simply do that on the stage with the Flash editing tools. Figure 3.15 shows what the gameover screen now looks like, when a game is complete.
Figure 3.15 The gameover screen, complete with final score and time.
This completes MatchingGame9.fla and MatchingGameObject9.fla. We now have a game with an intro and gameover screen. It keeps track of score and time, and displays them when the game is over. It also allows the player to play again.
Next we finish off the game by adding a variety of special effects, such as card flips, limited card-viewing time, and sound effects.