- 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 need them, let's 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 misses 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);
Note that adding a text field requires us to also import the text library at the start of our class. We need to add the following line to the top:
import flash.text.*;
We could also format it and create a nicer-looking text field, as we did in Chapter 2, but we leave that part out for now.
The score itself is a simple integer variable named gameScore. We declare it at the start of the class:
private var gameScore:int;
Then, we set it to zero in the constructor function:
gameScore = 0;
In addition, it is 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 set the text of gameScoreField. The first is in the constructor function. The second is 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 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 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 makes 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 returns the time in milliseconds since the Flash movie started. 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 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 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 isn't on top of the score display:
gameTimeField = new TextField(); gameTimeField.x = 450; addChild(gameTimeField);
Before the constructor function is done, we 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 is 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 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 happens 12 times a second, which is certainly enough:
addEventListener(Event.ENTER_FRAME,showTime);
All that is left is to make the showTime function. It calculates the current time based on the current value of getTimer() and the value of gameStartTime. Then, it puts 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 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 see 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 shows the following:
Time: 2:03
The clockTime function takes the time in raw milliseconds and converts it to minutes and whole seconds. In addition, it formats it to use a colon (:) and makes sure that a zero is placed correctly when the number of seconds is fewer than ten.
The function starts off by dividing the number of milliseconds by 1,000 to get the number of seconds. It then divides 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, since123 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 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. The number of seconds is 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 now displays 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 we finish with 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 know what the score and time are, this data needs to be sent from the game to the root level.
Before we call the gotoAndStop command that advances 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 we finish with:
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 A more complete gameover screen, with the 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 we finish with displays them when the game is over. It also enables the player to play again.
Next, we finish the game by adding a variety of special effects, such as card flips, limited card-viewing time, and sound effects.