Read Foundation Game Design with ActionScript 3.0, Second Edition Online
Authors: Rex van der Spuy
Let's take a look at some of the new code and concepts this program has introduced.
In order to embed images, the program first imports theDisplayObject
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.
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.
So if the character doesn't move, what does? The background. Thebackground
object is the new center of attention in the code in theenterFrameHandler
.
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 thevx
andvy
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.
This simple scrolling system can actually take you quite far, but there are a few problems that some fine-tuning can help solve:
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.
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.)
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;
}
}
}
}
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.