Foundation Game Design with ActionScript 3.0, Second Edition (45 page)

BOOK: Foundation Game Design with ActionScript 3.0, Second Edition
9.01Mb size Format: txt, pdf, ePub

This is what the
checkGameOver
function definition looks like:

public function checkGameOver():void
{
  if (guessesRemaining < 1)
  {
    endGame();
  }
}

The method checks to see how many guesses the player has remaining. If there are still enough, nothing happens and the game continues. But if
guessesRemaining
is less than 1, the game is brought to an end by calling the
endGame
method.

public function endGame():void
{
  if (gameWon)
  {
    output.text
      = "Yes, it's " + mysteryNumber + "!" + "\n"
      + "It only took you " + guessesMade + " guesses.";
  }
  else
  {
    output.text
      = "No more guesses left!" + "\n"
      + "The number was: " + mysteryNumber + ".";
  }
}

The
endGame
method looks to see whether the game has been won or lost by checking whether the
gameWon
variable is true or false. It then displays the appropriate message. (Can you figure out how string concatenation is being used to display these messages? Compare the code with what you see in the game's output text field. I'm sure you can do it!)

If statements work by running their directives if the condition in the parentheses is true. With Boolean variables, there are two ways to check whether they're true or false. You can use an equality operator (a double-equal sign) and compare it with a true or false value, like this:

if(gameWon == true)

If
gameWon
is false, this statement is read as being false and the directives don't run. If
gameWon
is true, the statement is read as being true and the directives run.

You should be familiar with this way of checking for true or false conditions from the previous chapter. However, if the condition you're checking happens to be the value of a Boolean variable, it's much more convenient and often makes your code easier to read to use this shorthand:

if(gameWon)

Because the value of
gameWon
can be only true or false, it provides exactly the same information as the first example.

You'll be using this as the preferred way for checking the value of Boolean variables in if statements throughout the book.
if(gameWon
) is very close to the English phrase “If the game is won.” Choose the names of your variables carefully to help to make your programs are easier to read.

How does the program know whether the
gameWon
variable is true or false?

You initialized
gameWon
to false
in
the
startGame
method. That means it will always be false unless you change it to true somewhere in the game. You would change it to true only if the player actually meets the conditions for winning the game. In this game, there is only one way the player can win: by guessing the correct number.

This makes things really easy because you know that if players still have enough guesses remaining, and they've guessed the correct number, they must have won the game. So all you need to do is set the
gameWon
variable to true and call the
endGame
method in the same if/else block that checks whether the player's guess is correct. Easy!

Don't believe me? Check out the section of the
playGame
method's if/else statement that does this (shown in bold text):

public function playGame():void
{
  guessesRemaining--;
  guessesMade++;
  gameStatus = "Guess: " + guessesMade + ", Remaining: " + guessesRemaining;
  currentGuess = uint(input.text);
  if (currentGuess > mysteryNumber)
  {
    output.text = "That's too high." + "\n" + gameStatus;
    
checkGameOver();
  }
  else if (currentGuess < mysteryNumber)
  {
    output.text = "That's too low." + "\n" + gameStatus;
    
checkGameOver();
  }
  else
  {
    output.text = "You got it!";
    
gameWon = true;
    
endGame();
  }
}

Think about the logic behind what the if/else statement is saying. If players still have enough guesses remaining, the game will continue. If the game is still continuing, and players have guessed the right number, they must have won the game. The
gameWon
variable is then set to true, and the directives in the
endGame
method are run. When the
endGame
method runs, its conditional statement notices that
gameWon
is true and displays the message telling the player she's won.

Modular programming with methods

Can you see how the
checkGameOver
and
endGame
methods were used to help modularize the code? In truth, you could have written this program without them by adding all the conditions they check for in one extremely long if/else statement. But if you'd done so, you'd have to write some of the same code over twice, and it would all start to become very difficult to read and debug.

Modularizing specific tasks inside self-contained methods allows you to modify or debug those bits of code in isolation without having to change (and possibly damage) other parts of the program that are working. It also means that whenever you want to perform a certain task, you don't need to duplicate any of the code you've already written; you just have to call the method you need to do the job.

Using methods to modularize your code might take a bit of practice, and you might find it a bit of a brain-twister until you've seen a few more examples and experimented with using them in your own projects. Have a look at how the game is working so far and see if you can figure out how the interrelationships between methods and method calls are working.
Figure 4-20
is a map of how it all fits together. You can think of each code block as a module in the program.

Figure 4-20.
Use methods to modularize specific tasks.

Polishing up

As the saying goes, 30% of the time it takes to program a piece of software goes into making it work, and the other 70% goes into making it work well. There's no better example of this principle at work than the number guessing game. It's playable, but there's a lot lacking that most players would complain about.

Here are some things you can improve:

  • The game still goes on counting guesses after the player has won the game.
  • How about the mystery number? The game won't have much replay value if it's 50 every time you play it. You can solve this by randomizing the mystery number.
  • It would be nice to add some buttons to play the game, including a Play again button.

Time to roll up your sleeves and see if you can fix these problems!

Tackling random numbers

You'll tackle the random number problem first because you'll almost certainly find yourself needing to use random numbers in most of your game projects. AS3.0 has a built-in class called
Math
that includes quite a few methods that are useful for manipulating numbers. One of these is the
random
method, which generates a random number between 0 and 1. Here's what it looks like:

Math.random()

You can assign this random number to a variable just as you assign other values to variables, as in this example:

randomVariable = Math.random();

The fictitious
randomVariable
is now assigned a random number between 0 and 1, with up to 16 decimal places. So it could be anything; for example, 0.3669164208535294 or 0.5185672459021346.

What use is a random number between 0 and 1 with 16 decimal places? Well, practically none whatsoever. Fortunately, you can do a bit of tweaking to get something more useful.

First, random numbers for games usually need to be integers (whole numbers). All those decimal places have got to go! Can you imagine what a nightmare the guessing game would be to play if the mystery number were something like 33.6093155708325684? If you chop off all those decimals, you'll have something useful, such as 33, which is much more user-friendly.

The
Math
class fortunately has a few built- in methods that can help you round decimals up or down:

  • Math.round
    can be used to round numbers either up or down. For example,
    Math.round(3.4)
    returns a value of 3.
    Math.round(3.8)
    returns 4.
    Math.round(3.5)
    also returns 4.
  • Math.floor
    always rounds numbers down.
    Math.floor(3.2)
    returns 3.
    Math.floor(3.9)
    also returns 3.
  • Math.ceil
    always rounds numbers up.
    Math.ceil(3.2)
    returns 4, and
    Math.ceil(3.9)
    also returns 4. (What is ceil? It's short for
    ceiling.
    Ceilings are up; floors are down. Make sense?)

To use any of these methods along with the
Math.random
method, you need to use a format that looks like this:

Math.round(Math.random())

Think about what this is doing.
Math.random()
generates a random number between 0 and 1 with loads of decimals. So imagine that it came up with a deliciously useless number such as 0.6781340985784098. You could pretend that the preceding line of code now looks like this:

Math.round(0.6781340985784098)

How would you round that number? You'd round it up, and the result would be the following:

l

But what would happen if the random number were lower, like this?

Math.round(0.2459678308703125)

It would be rounded down to this:

0

This means that you can use the line of code,
Math.round(Math.random())
, to generate a random number that has a 50% chance of being either 0 or 1. Not yet what you're looking for in the game, but not entirely useless, either. There will be many instances where calculating a 50% chance of something happening will be really useful in your games, and you can use this little snippet of code to do exactly that.

In fact, you can use this bit of code to generate random Boolean (true/false) values. Let's pretend that you have a Boolean variable called
rainToday
. You could initialize it with a value of false.

rainToday = false;

Oh, if only that were true! So to make it a little more realistic, you can give it a 50% chance of being either true or false. All you need to do is use the
Math.round(Math. random())
code snippet in an if/else statement and compare it against a value of 1. Here's what the code might look like:

if(Math.round(Math.random()) == 1)
{
  rainToday = true;
}
else
{
  rainToday = false;
}

Math.round(Math.random())
has an exactly 50% chance of generating either the number 1 or 0. If it happens to be 1, the first directive runs and
rainToday
becomes true. If it's 0, no rain today!

So a random number between 0 and 1 is slightly more useful, but it's not exactly what you're looking for in the game. How can you get a number between 0 and 99? Here's how:

Math.floor(Math.random() * 100)

The asterisk is AS3.0's
multiplication operator
. * 100 means
multiplied by 100.
This line of code multiplies the random number by 100 and then uses
Math.floor
to round it down so the lowest number it can possibly be is 0 and the highest is 99. That gives a perfect random whole number that falls within the range of 0 to 99.

Here's another way of looking at it. Let's say that the random number is 0.3378208608542148. That would mean the code will look like this:

Math.floor(0.3378208608542148 * 100)

Multiplied by 100, the random number will then look like this:

Math.floor(33.78208608542148)

The decimal point is just moved two spaces to the right, giving you a nice big number to work with. But you still have the problem of those infuriating decimals to deal with! Not to worry;
Math.floor
comes to the rescue by rounding the whole thing down. So the result is very satisfying.

33

Perfect for the number guessing game!
Figure 4-21
shows an example of this process in action.

Other books

The Counterfeit Cowgirl by Kathryn Brocato
Trent's Last Case by E. C. Bentley
Silent Girl by Tricia Dower
Taxi Driver by Richard Elman
Windmills of the Gods by Sidney Sheldon
Angels & Sinners: The Motor City Edition by Ashley Suzanne, Bethany Lopez, Bethany Shaw, Breigh Forstner, Cori Williams, D.M. Earl, Jennifer Fisch-Ferguson, Melanie Harlow, Sara Mack, Shayne McClendon
Amanda Scott by Highland Princess
Producer by Wendy Walker
Thief of Hearts by Patricia Gaffney