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

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

Figure 7-83
. Make a big background image.

I made a class for this new background image called
BigBackground
.

I also needed to separate the black rounded rectangle that frames the time display from the border. That's because the black rectangle should stay fixed in one position on the stage, not scroll with the background. I called this black rectangle image timeDisplay.png and created a class for it called
TimeDisplay
. The
TimeDisplay
and
BigBackground
classes are identical to the other game object classes that you looked at in this chapter.

Adding objects to the game

I created the
BigBackground
and
TimeDisplay
objects using the
BigBackground
and
TimeDisplay
classes like this:

public var background:BigBackground = new BigBackground();
public var timeDisplay:TimeDisplay = new TimeDisplay();

I then added them to the stage and positioned them like this:

addGameObjectToStage
  (
    background,
    -(background.width - stage.stageWidth) / 2,
    -(background.height - stage.stageHeight) / 2
  );
addGameObjectToStage(timeDisplay, 200, 5);

The background is centered using the same formula you used in
Chapter 6
.

Make sure that you add the
timeDisplay
object
after you add all the other objects to the stage
. That will ensure that it floats above them when the game scrolls.

With the background's position in place, you can start to add the game objects. However, now that you're dealing with a huge space, you can't position them using simple stage coordinates. You need to provide coordinates relative to where you want them to be placed on the background. Most of these objects will be off-stage when the game starts.

This is luckily very easy to do. Just add the game objects' x and y coordinates to the background's x and y coordinates. Here's how the fist box is positioned:

addGameObjectToStage(box1, background.x + 100, background.y + 100);

This places it 100 pixels to the right and 100 pixels below wherever the background's top left corner happens to be. You can see this in
Figure 7-84
.

Figure 7-84
. The game objects are positioned relative to the background object's x and y position.

This is really convenient because if you ever change the initial start position of the background, the box's position will automatically adjust its own position to compensate.

All the game objects are positioned like this, relative to the background's position:

//The boxes
addGameObjectToStage(box1, background.x + 100, background.y + 100);
addGameObjectToStage(box2, background.x + 200, background.y + 300);
addGameObjectToStage(box3, background.x + 400, background.y + 450);
addGameObjectToStage(box4, background.x + 100, background.y + 600);
addGameObjectToStage(box5, background.x + 300, background.y + 700);
addGameObjectToStage(box6, background.x + 500, background.y + 200);
addGameObjectToStage(box7, background.x + 600, background.y + 600);
addGameObjectToStage(box8, background.x + 500, background.y + 500);
addGameObjectToStage(box9, background.x + 700, background.y + 200);
addGameObjectToStage(box10, background.x + 700, background.y + 400);
addGameObjectToStage(box11, background.x + 800, background.y + 500);
addGameObjectToStage(box12, background.x + 100, background.y + 700);
addGameObjectToStage(box13, background.x + 900, background.y + 300);
addGameObjectToStage(box14, background.x + 800, background.y + 200);
//The bombs
addGameObjectToStage(bomb1, background.x + 55, background.y + 710);
addGameObjectToStage(bomb2, background.x + 255, background.y + 310);
addGameObjectToStage(bomb3, background.x + 355, background.y + 160);
addGameObjectToStage(bomb4, background.x + 855, background.y + 510);
addGameObjectToStage(bomb5, background.x + 755, background.y + 610);

For this to work, you have to make sure that you set the x and y position of the background first, before you set those of the game objects.

Scrolling and stage boundaries

Here's all the code from the
enterFrameHandler
that scrolls the background and game objects. It uses a custom method called
scroll
to do the actual work of scrolling the game objects. I'll explain how all this works in the pages ahead.

//Calculate the scroll velocity
var temporaryX:int = background.x;
var temporaryY:int = background.y;
//Check the stage boundaries
//1. Check the inner boundaries
if (character.x < leftInnerBoundary)
{
  character.x = leftInnerBoundary;
  rightInnerBoundary
    = (stage.stageWidth / 2) + (stage.stageWidth / 4);
  background.x -= vx;
}
else if (character.x + character.width > rightInnerBoundary)
{
  character.x = rightInnerBoundary - character.width
  leftInnerBoundary
    = (stage.stageWidth / 2) - (stage.stageWidth / 4);
  background.x -= vx;
}
if (character.y < topInnerBoundary)
{
  character.y = topInnerBoundary;
  bottomInnerBoundary
    = (stage.stageHeight / 2) + (stage.stageHeight / 4);
  background.y -= vy;
}
else if (character.y + character.height > bottomInnerBoundary)
{
  character.y = bottomInnerBoundary - character.height;
  topInnerBoundary
    = (stage.stageHeight / 2) - (stage.stageHeight / 4);
  background.y -= vy;
}
//2. Background stage boundaries
if (background.x > 0)
{
  background.x = 0;
  leftInnerBoundary = 0;
}
if (background.y > 0)
{
  background.y = 0;
  topInnerBoundary = 0;
}
if (background.x < stage.stageWidth - background.width)
{
  background.x = stage.stageWidth - background.width;
  rightInnerBoundary = stage.stageWidth;
}
if (background.y < stage.stageHeight - background.height)
{
  background.y = stage.stageHeight - background.height;
  bottomInnerBoundary = stage.stageHeight;
}
//3. Character stage boundaries
if (character.x < 50)
{
  character.x = 50;
}
if (character.y < 50)
{
  character.y = 50;
}
if (character.x + character.width > stage.stageWidth - 50)
{
  character.x = stage.stageWidth - character.width - 50;
}
if (character.y + character.height > stage.stageHeight -50)
{
  character.y = stage.stageHeight - character.height - 50;
}
//Calculate the scroll velocity
var scroll_Vx:int = background.x - temporaryX;
var scroll_Vy:int = background.y - temporaryY;
//Use the scroll velocity to move the game objects
scroll(box1, scroll_Vx, scroll_Vy);
scroll(box2, scroll_Vx, scroll_Vy);
scroll(box3, scroll_Vx, scroll_Vy);
scroll(box4, scroll_Vx, scroll_Vy);
scroll(box5, scroll_Vx, scroll_Vy);
scroll(box6, scroll_Vx, scroll_Vy);
scroll(box7, scroll_Vx, scroll_Vy);
scroll(box8, scroll_Vx, scroll_Vy);
scroll(box9, scroll_Vx, scroll_Vy);
scroll(box10, scroll_Vx, scroll_Vy);
scroll(box11, scroll_Vx, scroll_Vy);
scroll(box12, scroll_Vx, scroll_Vy);
scroll(box13, scroll_Vx, scroll_Vy);
scroll(box14, scroll_Vx, scroll_Vy);
scroll(bomb1, scroll_Vx, scroll_Vy);
scroll(bomb2, scroll_Vx, scroll_Vy);
scroll(bomb3, scroll_Vx, scroll_Vy);
scroll(bomb4, scroll_Vx, scroll_Vy);
scroll(bomb5, scroll_Vx, scroll_Vy);

Here's the scroll method used by the code above:

public function scroll
  (gameObject:Sprite, scroll_Vx:int, scroll_Vy:int):void
{
  gameObject.x += scroll_Vx;
  gameObject.y += scroll_Vy;
}

Most of this code is used to scroll the background and set the stage boundaries for both the character and the background. You've see all of it before—it's a combination of the scrolling code from
Chapter 5
, along with the extra modification you made in this chapter to prevent the character from crossing the playing field's 50 pixel wide borders. Review
Chapter 5
and the earlier section in this chapter if you're unsure about how any of the code works.

What's new here is that the code is making all the game objects move relative to the background. That makes them look as if they're fixed in position to the background. Of course, it's just an illusion, but a brilliant one. Let's see how it works.

First, you have to create two variables to temporarily capture the current x and y position of the background. You have to do this
before
you scroll the background. These variables are called
temporaryX
and
temporaryY
.

var temporaryX:int = background.x;
var temporaryY:int = background.y;

All they do is record the background's position before it moves.

After the background is moved with the scrolling code, you use these variables to calculate something called the
scroll velocity
. This tells you the difference between the background's previous position and its current position. The
scroll_Vx
and
scroll_Vy
variables calculate this.

var scroll_Vx:int = background.x - temporaryX;
var scroll_Vy:int = background.y - temporaryY;

Now that you know the difference, you can use it to correctly reposition the game objects. All you need to do is apply the
scroll_Vx
and
scroll_Vy
values to the objects' x and y positions, like this:

box1.x += scroll_Vx;
box1.y += scroll_Vy;

That's all you need to do. The objects will now remain fixed in place relative to the background, no matter how it scrolls.

However, the code in
TimeBombScroll
goes one step further. Rather than meticulously setting the x and y positions of 14 boxes and five bombs, it delegates this work to a method called
scroll
.

scroll(box1, scroll_Vx, scroll_Vy);

The
scroll
method's function definition then takes care of the repetitive work of setting the objects' x and y properties, like this:

public function scroll
  (gameObject:Sprite, scroll_Vx:int, scroll_Vy:int):void
{
  gameObject.x += scroll_Vx;
  gameObject.y += scroll_Vy;
}

It just saves you a bit of tedious typing. And this is really all there is to it. Here's the procedure you have to follow to make sure this works:

  1. Copy the background's x and y positions into the
    temporaryX
    and
    temporaryY
    variables.
  2. Scroll the background
  3. Calculate the
    scroll_Vx
    and
    scroll_Vy
    values to find out by how much the background has moved.
  4. Apply the
    scroll_Vx
    and
    scroll_Vy
    values to the game objects that you want to scroll along with the background.

You have to add the code in
exactly
this order for this to work. And, of course, all this code is running inside the
enterFrameHandler
.

This is actually an easy introduction to a very precise game animation technique called
Verlet integration.
Verlet integration isn't covered in this book, but you can read all about it in
Advanced Game Design with Flash.
The basic process of capturing an object's position in temporary variables, moving the object, and then calculating the difference is what Verlet integration is all about.

The rest of the code in TimeBombScroll.as is identical to TimeBombPanic.as, except for two small changes: the character is centered when the game starts and the time limit has been increased to 20 seconds. (But, come on, you should still be able to beat it in 10!) Take a look the full code in the source files so that you can see it all in context.

Summary

You now know how to make a complete game from beginning to end. This chapter showed you how to make game graphics, control them with code, and turn them into a real working game.

But there are still a lot of things you'll want your games to do that you haven't learned yet. How do you make enemy game objects that move by themselves? How can you switch game levels? And how can you fire bullets?
Chapter 8
, coming up next, will explain all this and more.

Chapter 8
Making Bigger Games

Time Bomb Panic is a good model of a small, one level game. It's a simple concept, takes only ten seconds to play, and you still have enough time in the day to play with your cats or go surfing. But it's probably not the kind of game you want to make. Those game ideas that keep you lying awaking at night planning no doubt involve hundreds of levels; take 50 hours or more to play; and contain dozens of different game objects, puzzles, and interesting scenarios. They're big games. In this chapter you'll learn how to make them.

The game you're going to look at this chapter is called Monster Mayhem. It's a two level game with just a few objects, but you can use the same structure to make games with any number of levels and objects and still keep your programming code under control. It's a good model for a big game—and for most of the smaller ones you'll make as well.

You'll find the game in the MonsterMayhem project folder in this chapter's source files. Play through it a few times to get a sense of what it's all about. In the first level, you need to kill the two monsters wandering around the stage. Press the space bar to launch a star projectile upwards; if the star hits the monsters three times they explode. The game ends if any of the monsters touch the character. If you kill them both, the second level loads. There are four monsters in level two, and they move much faster, but this time you can fire the star in four directions. Can you kill all four to beat the game? It's harder than it looks!
Figure 8-1
shows how the game plays.

Figure 8-1.
Monster Mayhem

Understanding the game structure

The structure of the code in Monster Mayhem game is radically different from any previous games you've made in the book so far. Each level of the game is contained entirely inside a single Sprite. When a level is complete, the main application class removes the current level Sprite from the stage and adds a new one. That means that none of the game objects, like the character or the monsters, are added to the main stage. They're all first added to a containing Sprite. And it's that containing Sprite that's added to the stage by the application class.

To get a clearer idea of how this works, let's compare the structure of Monster Mayhem with Time Bomb Panic. In Time Bomb Panic, all the game objects are created as separate classes. Those classes are then added directly to the stage in the main application class.
Figure 8-2
illustrates this structure at work.

Figure 8-2.
In Time Bomb Panic all the objects are added to the stage in the main application class.

In Monster Mayhem, all the game objects are first added to a class called
LevelOne
. An object called
_levelOne
is then added to the stage by the
MonsterMayhem
application class.
Figure 8-3
Illustrates this.

Figure 8-3.
In Monster Mayhem all the objects are first added to the LevelOne class. The main application class then adds a _levelOne object and adds it to the stage.

LevelOne is a Sprite. That means that all the action in the game, and all the game logic, is taking place inside the LevelOne Sprite, not the application class. All the application class does is to add the level to the
stage so that you can see it. The reason for containing the entire game level in a Sprite is so that the application class can easily switch levels by adding or removing them from the stage.

And here's something really important to know:
only the application class has access to the stage
. That means it's the only class that can make objects visible by displaying them on the stage. Infuse this into your memory cells because it's really important in understanding some technical details of the code you'll need to know soon. The application class is the only class that can use a line of code like this:

stage.addChild(gameObject);

Other classes can display objects inside themselves
but not directly on the stage
. They can only add objects to themselves by referring to
this
, which means “this class.”

this.
addChild(gameObject);

You can see from
Figure 8-3
that the
LevelOne
class is adding objects to itself using code like the following:

this.addChild(_character);
this.addChild(_monster);
this.addChild(_star);

This means that these objects are being added to the
LevelOne
class. That's just fine, but you won't be able to actually see any of these objects until the
LevelOne
class that's containing them is visible on the stage itself. That's why it's the job of the application class to create a
_levelOne
object from the
LevelOne
class and add it to the stage, like this:

stage.addChild(_levelOne);

Let's take a look at the application class,
MonsterMayhem.as
, and see how it adds
levelOne
to the stage. Here's the entire class:

package
{
  import flash.display.Sprite;
  import flash.events.Event;
  [SWF(width="550", height="400",
  backgroundColor="#FFFFFF", frameRate="60")]
  public class MonsterMayhem extends Sprite
  {
    private var _levelOne:LevelOne;
    private var _levelTwo:LevelTwo;
    public function MonsterMayhem()
    {
      _levelOne = new LevelOne(stage);
      _levelTwo = new LevelTwo(stage);
      stage.addChild(_levelOne)
      stage.addEventListener("levelOneComplete", switchLevelHandler)
    }
    private function switchLevelHandler(event:Event):void
    {
      trace("Hello from the application class! Switch levels!");
      stage.removeChild(_levelOne);
      _levelOne = null;
      stage.addChild(_levelTwo);
    }
  }
}

The application class has these jobs:

  • It sets up the Flash Player's parameters with the SWF metatag.
  • It creates two objects called
    _levelOne
    and
    _levelTwo
    . These are the game levels. Both of these objects are passed a reference to the stage in their arguments when they're created, like this:
_levelOne = new LevelOne(stage);

I'll explain how this works and why this is important soon.

  • It adds the
    _levelOne
    object to the stage.
  • When the player has completed level one, it removes the
    _levelOne
    object from the stage and adds
    _levelTwo
    . Don't worry about this bit of code in the
    switchLevelHander
    yet'you'll look at it in detail later in the chapter.

Notice also that the application class only imports two classes:
Sprite
and
Event—
nothing else. That's because it doesn't need any other classes to do the simple jobs it does.

Using private variables and methods

There's something new in
MonsterMayhem.as
that you haven't seen yet. The two level objects,
_levelOne
and
_levelTwo
, are declared as private.

private
var _levelOne:LevelOne;
private
var _levelTwo:LevelTwo;

The
switchLevelHandler
is also declared as private.

private function switchLevelHandler(event:Event):void
{...

Note that private means that those variables and methods can be used only within the class they're defined. They can only be used in the
MonsterMayhem
class and nowhere else.

If you don't use the private keyword when you declare a property or method, AS3.0 assumes that they're public. Public properties can be accessed freely by any other classes. You can use the public keyword to make this explicit in your code if necessary.

Why should you declare a variable or method as private? Imagine that your house is a class and your oven is one of the class's variables. Your oven is having trouble switching on, so you call a repairman to take a look at it. But you're really busy and can't be home when the repairman comes, so you leave the door unlocked and trust that all will be well. Best-case scenario: you come home to find that your oven works, but a vase is lying broken on the floor, an empty pizza box is on the sofa, and a bill arrives at the end of the month for all kinds of pay-per-view movies you know you never watched. Worst-case scenario: you come home to find your house a smoldering ruin and all the other houses in the neighborhood up in flames. If only you could have been there to tell the repairman (who was standing ready with his 10,000- volt charge-jumper), “It's a gas stove, not electric!” Because your stove was public, any other class that doesn't know what it's doing, such as the repairman, can make any changes it wants to and cause havoc with your game. If the stove were declared as private, the clueless repairman would be locked out.

In a very small game with only a few classes, you could certainly get away with keeping all of your variables and methods public, and everything would be just fine. In a larger game, however, you'd be opening yourself up to a potential debugging nightmare scenario. So, in the interest of helping you develop good long-term programming habits, all the code in this book will keep a class's variables and methods private, unless another class needs to access them.

Using private properties to lock down a class in this way is an aspect of object-oriented programming called
encapsulation.
Encapsulation means that your class is completely sealed off from tampering by other classes and is as self-contained as possible. If other classes want to access or modify any properties in an encapsulated class, they have to follow very strict rules about doing so.

AS3.0 also has two special accessor methods called
get
and
set
that let classes use another class's private properties in a controlled way. They're not used in this book but you can read more about them in the chapter “Methods” from Adobe's online document
Programming ActionScript 3.0 for Flash
at
http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/
.

Other books

Love Kinection by Jennifer James
Tartarus: Kingdom Wars II by Jack Cavanaugh
His Beloved Criminal by Kady Stewart
Cherry (A Taboo Short) by Jenika Snow, Sam Crescent
Smashed by Lisa Luedeke
A New Leash on Life by Suzie Carr
L. A. Outlaws by T. Jefferson Parker
The Long Utopia by Terry Pratchett