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

Looking at the code

Let's take a look at some of the new code and concepts this program has introduced.

Embedding the character and background images

In order to embed images, the program first imports the
DisplayObject
class.

import flash.display.Sprite;
import flash.display.DisplayObject;
import flash.events.KeyboardEvent;
import flash.ui.Keyboard;
import flash.events.Event;

The images are embedded in the class definition. The code to do this is exactly the same as the example you just looked at. Keep in mind that even if you embed an image you still need to eventually copy it into a Sprite object if you want to display it on the stage. That's why the code creates accompanying Sprite objects for both the character and the background.

public class BasicScrolling extends Sprite
{
  //Embed the background image
  [Embed(source="../images/background.png")]
  public var BackgroundImage:Class;
  public var backgroundImage:DisplayObject = new BackgroundImage();
  public var background:Sprite = new Sprite();
  //Embed the character image
  [Embed(source="../images/character.png")]
  public var CharacterImage:Class;
  public var characterImage:DisplayObject = new CharacterImage();
  public var character:Sprite = new Sprite();
  //Create and initialize the vx and vy variables
  public var vx:int = 0;
  public var vy:int = 0;
  public function BasicScrolling()
  {...

The Sprites containing the images are then added to the stage in the constructor method in the same way you added them in previous programs.

public function BasicScrolling()
{
  //Add the background
  background.addChild(backgroundImage);
  stage.addChild(background);
  background.x = -1005;
  background.y = -761;
  //Add the character
  character.addChild(characterImage);
  stage.addChild(character);
  character.x = 225;
  character.y = 150;
  //Add the event listeners
  stage.addEventListener
    (KeyboardEvent.KEY_DOWN, keyDownHandler);
  stage.addEventListener
    (KeyboardEvent.KEY_UP, keyUpHandler);
  stage.addEventListener
    (Event.ENTER_FRAME, enterFrameHandler);
}

But take a look at the code that positions the background:

background.x = -1325;
background.y = -961;

This deserves some further explanation.

Positioning the background

My image of Phobos is pretty big: it's 2561 by 1922 pixels. The stage is only 550 by 400 pixels. I want to add the background to the stage so that it's centered'so that the middle point of Phobos is visible on the stage when the game first starts. That means I need to add it to the stage at a position that's the negative of half its width and half its height. Then I need to add half the stage's width to the x position, and half the stage's height to the y position. Remember that you position objects from their top left corners. And the top left corner of the stage has x and y values of 0. That means if I give my background an initial x position of -1005 and -761, it will be centered over the stage when the game starts.
Figure 5-12
illustrates why this works.

Figure 5-12.
Center the background over the stage.

Of course, most of the background is hidden off stage. But it will be centered directly over the center of the stage.

To center your own background image, divide its height and width by half then subtract half the stage's width and height. Make those numbers negative by putting a minus sign in front of them, and use those negative numbers for the background's x and y position. If you don't feel like doing that math (and why would you?) just let AS3.0 do it for you automatically like this:

background.x = -(background.width - stage.stageWidth) / 2;
background.y = -(background.height - stage.stageHeight) / 2;

This code will center any large background image, no matter its size.

The character is centered on the stage. In fact, the character never moves from its position at the center of the stage the entire time. It looks like it's moving, but it isn't. That's all part of the scrolling illusion, and I'll explain how that works next.

Scrolling the background

So if the character doesn't move, what does? The background. The
background
object is the new center of attention in the code in the
enterFrameHandler
.

All you did is reverse a bit of the logic you were using to move the character. When you press the arrow keys, they move the background object in the direction
opposite
to the one you want the character to move in. That's why you should add a minus sign to the
vx
and
vy
variables with these two lines:

background.x += -vx;
background.y += -vy;

This creates the illusion that the character is moving when it's actually the background object that's moving in the opposite direction. Oh, the wily ways of the video game programmer!

The conditional statements stop the background object from moving when its edges reach the stage edges.

if (background.x > 0)
{
  background.x = 0;
}
if (background.y > 0)
{
  background.y = 0;
}
if (background.x < stage.stageWidth - background.width)
{
  background.x = stage.stageWidth - background.width;
}
if (background.y < stage.stageHeight - background.height)
{
  background.y = stage.stageHeight - background.height;
}

I'll leave the mental gymnastics up to you to figure out why it works the way it does, but it's simply one more permutation of exactly the same logic used to stop the character at the stage edges.

Better scrolling

This simple scrolling system can actually take you quite far, but there are a few problems that some fine-tuning can help solve:

  • When the scrollable area reaches its limit, the character is prevented from moving all the way to the stage's edge. Try holding down the right arrow key and see how far you get. At some point, the background will stop moving, but the player won't be able to travel all the way to stage's right side. This seems to be an artificial constraint that would be a frustrating limitation for the player in many action or adventure games.
    Figure 5-13
    illustrates this problem.

    Figure 5-13.
    When the background has reached the limit of its scrollable area, the character can't travel all the way to the stage's edge.

  • The other potential problem is that the scrolling background
    always
    scrolls. For many games, it might make more sense if the background scrolls only when the character is
    approaching
    the edge of the stage. Otherwise, the character should explore freely without the background moving.

Here's how to solve these problems. You can set up a system that figures out whether the character should move or the background object should move, depending on where each is. If the background object has reached the limit of its movement, the character should be free to travel to the edge of the stage. Also, if the player is not near any of the stage's edges, it should be free to move around without the background object moving.

The trick of making this work is to set up an imaginary
inner boundary
, which is a rectangular area inside the stage that's exactly half the stage's height and width. The character will be free to move around within the inner boundary; when it reaches the edge, it will stop moving, and the background will start to scroll. When the background reaches its scroll limit, the inner boundary that's been blocking the player from going further will extend to the limits of the stage to allow the player to move right to the edge.

The inner boundary that you'll create isn't a real object in the way that player and background are objects. Instead, it's just four numbers that define the top, bottom, left, and right of these boundaries. In fact, the logic behind finding these numbers is exactly the same as the logic used to set the real stage boundaries; you just cut them down to half the size.

You'll build this code in two stages so that you can see how it's working. (For the complete final working version of this code, see the BetterScrolling.as file in this chapter's source files.)

  1. You can either create a new ActionScript project or modify the code from the previous example. The following is a listing of the entire program; all the new code has been highlighted in bold text.
    package
    {
      import flash.display.Sprite;
      import flash.display.DisplayObject;
      import flash.events.KeyboardEvent;
      import flash.ui.Keyboard;
      import flash.events.Event;
      [SWF(width="550", height="400",
        backgroundColor="#FFFFFF", frameRate="60")]
      public class
    BetterScrolling
    extends Sprite
      {
        //Embed the background image
        [Embed(source="../images/background.png")]
        public var BackgroundImage:Class;
        public var backgroundImage:DisplayObject
          = new BackgroundImage();
        public var background:Sprite = new Sprite();
        //Embed the character image
        [Embed(source="../images/character.png")]
        public var CharacterImage:Class;
        public var characterImage:DisplayObject = new CharacterImage();
        public var character:Sprite = new Sprite();
        //Create and initialize the vx and vy variables
        public var vx:int = 0;
        public var vy:int = 0;
        //Variables for the inner boundary
        public var rightInnerBoundary:uint;
        public var leftInnerBoundary:uint;
        public var topInnerBoundary:uint;
        
    public var bottomInnerBoundary:uint;
        public function
    BetterScrolling()
        {
          //Add the background
          background.addChild(backgroundImage);
          stage.addChild(background);
          background.x = -(background.width - stage.stageWidth) / 2;
          background.y = -(background.height - stage.stageHeight) / 2;
          //Add the character
          character.addChild(characterImage);
          stage.addChild(character);
          character.x = 225;
          character.y = 150;
          //Define the inner boundary variables
          rightInnerBoundary
            = (stage.stageWidth / 2) + (stage.stageWidth / 4);
          leftInnerBoundary
            = (stage.stageWidth / 2) - (stage.stageWidth / 4);
          topInnerBoundary
            = (stage.stageHeight / 2) - (stage.stageHeight / 4);
          bottomInnerBoundary
            = (stage.stageHeight / 2) + (stage.stageHeight / 4);
          //Add the event listeners
          stage.addEventListener
            (KeyboardEvent.KEY_DOWN, keyDownHandler);
          stage.addEventListener
            (KeyboardEvent.KEY_UP, keyUpHandler);
          stage.addEventListener
            (Event.ENTER_FRAME, enterFrameHandler);
        }
        public function keyDownHandler(event:KeyboardEvent):void
        {
          if (event.keyCode == Keyboard.LEFT)
          {
            vx = -5;
          }
          else if (event.keyCode == Keyboard.RIGHT)
          {
            vx = 5;
          }
          else if (event.keyCode == Keyboard.UP)
          {
            vy = -5;
          }
          else if (event.keyCode == Keyboard.DOWN)
          {
            vy = 5;
          }
        }
        public function keyUpHandler(event:KeyboardEvent):void
        {
          if (event.keyCode == Keyboard.LEFT
            || event.keyCode == Keyboard.RIGHT)
          {
            vx = 0;
          }
          else if (event.keyCode == Keyboard.DOWN
            || event.keyCode == Keyboard.UP)
          {
            vy = 0;
          }
        }
        public function enterFrameHandler(event:Event):void
        {
          //Disable the code that moves the background
          //background.x -= vx;
          //background.y -= vy;
          //Move the player
          character.x += vx
          character.y += vy;
          //Stop character at the inner boundary edges
          if(character.x < leftInnerBoundary)
          {
            character.x = leftInnerBoundary;
            background.x -= vx;
          }
          if(character.x + character.width > rightInnerBoundary)
          {
            character.x = rightInnerBoundary - character.width
            background.x -= vx;
          }
          if(character.y < topInnerBoundary)
          {
            character.y = topInnerBoundary;
            background.y -= vy;
          }
          if(character.y + character.height  > bottomInnerBoundary)
          {
            character.y = bottomInnerBoundary - character.height;
            background.y -= vy;
          }
          //Check the stage boundaries
          if (background.x > 0)
          {
            background.x = 0;
          }
          if (background.y > 0)
          {
            background.y = 0;
          }
          if (background.x < stage.stageWidth - background.width)
          {
            background.x = stage.stageWidth - background.width;
          }
          if(background.y < stage.stageHeight - background.height)
          {
            background.y = stage.stageHeight - background.height;
          }
        }
      }
    }
  2. Compile the program and move the character with the arrow keys. You can now move it freely within the inner boundaries of the stage. When it reaches the edge of the inner boundary, the background starts scrolling.
    Figure 5-14
    illustrates this.

    Figure 5-14.
    The character is free to move within the inner boundary. When it reaches one of the edges, the character stops moving and the background scrolls.

Other books

Just Like a Hero by Patricia Pellicane
Cavanaugh Judgment by Marie Ferrarella
The Harp of Aleth by Kira Morgana
Villa Pacifica by Kapka Kassabova
Prospect Street by Emilie Richards
Halfway to the Grave by Jeaniene Frost
Virgin Unwrapped by Christine Merrill
The Fable of Us by Nicole Williams
Deadly Descent by Charles O'Brien
Monster in Miniature by Grace, Margaret