Read Foundation Game Design with ActionScript 3.0, Second Edition Online
Authors: Rex van der Spuy
Figure 5-8.
Precise screen boundaries using the object's width
The nice thing about using the built-in height and width properties for the character and the stage is that you don't have to know how big either of those things is. This code will work, unchanged, no matter the dimensions of the game character or the stage.
You can find the completed version of this code in the StageBoundaries file in this chapter's source files.
Screen wrapping
happens when an object disappears from one side of the stage and then reemerges from the opposite side. This is quite a fun effect and very easy to implement. In fact, the logic that's used to accomplish it is almost exactly the
inverse
of the logic you used to block movement at the stage's edges. Let's try it out!
enterFrameHandler
to match the text in bold:public function enterFrameHandler(event:Event):void
{
//Move the character
character.x += vx;
character.y += vy;
//Screen wrapping
if (character.x + character.width < 0)
{
character.x = 550;
}
if (character.y + character.height < 0)
{
character.y = 400;
}
if (character.x > stage.stageWidth)
{
character.x = 0 - character.width;
}
if (character.y > stage.stageHeight)
{
character.y = 0 - character.height;
}
}
Figure 5-9.
Screen wrapping
After the detailed look at how to stop an object at the stage edges, I'm sure you can figure out what's going on in this code already. It's almost exactly the same, except for being delightfully backward! It uses the object's width and height to figure out whether the object has completely disappeared off the edge of the stage. As soon as it detects that this is the case, it positions the object on the opposite side of the stage, just beyond the visible boundary. This creates the illusion that the object is trapped on the surface of some kind of cylindrical, never-ending plane. I usually complain about these sorts of things in this book, but this time, it's a blast! Have fun with it! Screen wrapping is, of course, a staple of many old skool games like PAC-MAN and Asteroids, and now you know how to do it if you ever need to.
You can find the complete code for this screen wrapping example in the ScreenWrapping folder in the chapter's source files.
Before you go much further, let's look at an alternate system for loading images into your games. I'm going to show you how to embed images into a program.
This is how you've been loading images into your programs up till now:
First, you created anURLRequest
andLoader
object.
public var characterURL:URLRequest
= new URLRequest("../images/character.png");
public var characterImage:Loader = new Loader();
Then you loaded the image into theLoader
object
.
characterImage.load(characterURL);
This system of loading images is called
runtime
loading. The images are loaded not when the program is compiled, but when the SWF file runs. It's actually the finished SWF file that is loading these images, not your program. The SWF file reads the paths to PNG files in your images folder and loads the images.
Runtime loading is useful because it means you can change any of the images in your game without having to recompile your program. Just drop different images into the images folder, and the finished SWF file will read the new ones automatically.
However, runtime loading has one fatal flaw. The images load slower than your code runs. That means that your program could start running before any of the images have loaded. This is especially true if you're running ENTER_FRAME events that update your code 60 times per second; it can lead to all sorts of problems.
AS3.0'sLoader
class has a number of built-in methods and properties that allow you to monitor the state of loaded images and initialize the program only when images have finished loaded. This requires quite a bit of additional coding to set up and manage, but, if you're working on a project that requires runtime loading for whatever reason, it may be the only solution. You can find out more about theLoader
class's properties and methods that allow you to do this in AS3.0's documentation athttp://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/Loader.html?filter_flash=cs5&filter_flashplayer=10.2&filter_air=2.6
For most games you'll be working on, however, there's a more reliable, simpler, and better way. You can embed images directly into the program. This means that the images are available to the program immediately, before any of the code runs. And it also means that the finished SWF file isn't dependent on an external images folder.
Here are the steps to embedding an image into your program:
DisplayObject
class.import flash.display.DisplayObject;
You add this import statement to the other import statements in your program.
[Embed(source="../images/character.png")]
public var CharacterImage:Class;
The first line is anEmbed
metatag. It tells your program where to look for the image file. (When you embed an image, the path to the file is relative to the AS file, not the SWF file.)
The second line creates aClass
variable. The image is stored directly in this variable. (Notice that its name begins with a capital letter, which indicates that it's a class.)
Very importantly, these two lines have to appear together like this, and in this order. You can't separate them or add any other code between them. This is just an odd quirk in AS3.0's syntax.
Class
variable you created in the previous step.public var characterImage:DisplayObject = new CharacterImage();
This new image object has to be typed asDisplayObject
.
character.addChild(characterImage);
Figure 5-10
illustrates how embedding images works.
Figure 5-10.
Embedding an image into a program
Because of all the problems it solves, embedding images like this is going to be the preferred system for loading images into your games for the rest of the projects in this book. You'll see a practical example of how to embed images in the next section.
You can embed any kind of file, not just image files. In
Chapter 9
, you'll learn how to embed sounds; in Chapter 11, you'll learn how to embed SWF files.
Scrolling
is an effect that allows a player to move about in an environment that is much bigger than the confines of the stage. Like an ancient Chinese scroll being unrolled over a long wooden table, the background moves to allow the character to explore the space beyond the stage edges.
Although it's hard to pick favorites, there's probably very little to learn about game design that isn't in some way embodied in one or the other of the two greatest classic game series of all time:
Super Mario Bros.
and
The Legend of Zelda.
Pretty much anything game designers need to consider about good game design can be found in these two games, and scrolling is no exception.
Super Mario Bros.
uses primarily what's known as
horizontal side-scrolling
. That's when the background moves left or right when the player reaches the left or right edges of the screen. The perspective in horizontal side-scrolling games is usually designed so that it looks as if you're viewing the environment from the side.
The Legend of Zelda
uses
overhead multi-axis scrolling
. In overhead scrolling, you view the environment from above, as if you were a bird flying in the sky and surveying the scene below. With multi-axis scrolling, the player character is free to move in any direction (up, down, left, or right), and the environment scrolls to keep up.
In truth, most games that use scrolling use a combination of these two systems. I'll first show you the more complex of the two, multi-axis scrolling, and finish the chapter with a quick look at horizontal side scrolling. Once you're comfortable with the scrolling techniques covered here, you'll be able to implement any combination of these two systems.
The first thing you need to implement in the scrolling system is some kind of background scene that is much bigger than the stage. In this example, you'll use a very large image.
http://photojournal.jpl.nasa.gov/
. Any images labeled Full Resolution are big enough.)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 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()
{
//Add the background
background.addChild(backgroundImage);
stage.addChild(background);
background.x = -1325;
background.y = -961;
//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);
}
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
{
//Move the background
background.x -= vx;
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;
}
}
}
}
[Embed(source="../images/background.jpg")]
Figure 5-11.
A holiday in space!