Simple AS3 Viewport Tutorial

Simple Flash AS3 Viewport Demo
Click inside and use arrow keys to move viewport

This is a little demo I made this morning as a way to get a viewport concept working for the latest game I am developing.  I’ve done a few viewport type projects in other languages but had not implemented one in Flash AS3 yet.

This demo creates 300 random balls and sets them in motion inside the world.  The world’s dimensions are four times larger than the viewport above.

You can click in the window above and use the arrow keys to move the viewport around the world.  The source files for this tutorial are available at the end of the article.

BACKGROUND

I needed to be able to have world coordinates for my latest game along with a viewport which follows the player.  All the objects in the world must move according to various physics and be able to be mapped into the current viewport for display purposes.  After hacking around for a while unsuccessfully last night on the game code I realized this morning that it would just be better to start fresh on a little proof of concept demo until I had it all working.

I thought I’d share some of the concepts in case it could help someone else as well as get any code or design feedback from anyone who may have implemented these in a different or more efficient way.  I can already think of a ton of improvements my rudimentary solution could use but I’ll add them as necessary.

BASIC DOCUMENT CLASS VARS AND CONSTANTS:


// Viewport related constants:
private static const VIEWPORT_WIDTH:Number = 350;
private static const VIEWPORT_HEIGHT:Number = 300;
private static const VIEW_STEP:int = 25;

// World related constants:
public static const WORLD_WIDTH:Number = 1400;
public static const WORLD_HEIGHT:Number = 1200;

// Current viewport coords:
private var viewportX:Number;
private var viewportY:Number;

INITIAL SETUP OF VIEWPORT AND CREATING CIRCLES WITHIN WORLD:

This snippet is occurring inside an init() function that is called from the document class constructor. This is all one time setup.


// set view port coords to middle of world:
viewportX = WORLD_WIDTH / 2;
viewportY = WORLD_HEIGHT / 2;

// create a bunch of random circles scattered across the world:
circles = new Array();
for (var i:int = 0; i < NUM_CIRCLES; i++) {
var circle:Circle = new Circle();

// grab random x that falls within world space:
var wX:Number = GameUtils.randRange(0 + (circle.width / 2), WORLD_WIDTH - (circle.width / 2));

// grab random y that falls within world space:
var wY:Number = GameUtils.randRange(0 + (circle.height / 2), WORLD_HEIGHT - (circle.height / 2));

// set the coordinates for our circle:
circle.placeInWorld(wX, wY);

// add our circle to the display list:
addChild(circle);

// push our circle onto our array of circles:
circles.push(circle);

SNIPPET OF HANDLING KEYBOARD INPUT TO MOVE THE VIEWPORT:

This snippet is occurring inside a keyDown listener function which listens for the user hitting the arrow keys.  Each time the keys are hit it moves the viewport in the appropriate direction while performing range checking with the world borders.


if (e.keyCode == 38) {            // UP
 viewportY -= VIEW_STEP;
 if (viewportY < 0) { viewportY = 0; }
}

MAIN PROGRAM LOOP:

This is a simple enterFrame listener that loops through all the active circles and calls their update method. After the update method we alter the coordinates of the parent display object (in this case the instance of our viewport) inversely with the viewport coordinates. This allows Flash to automatically handle updating all the positions of the circles which are children of our viewport instance. Thanks to draknek for this elegant suggestion!


private function enterFrameListener(e:Event):void {
	for (var i:int = 0; i < circles.length; i++) {
		circles[i].update();
	}
	
	// pan the viewport
	x = -viewportX;
	y = -viewportY;
}

CIRCLE CLASS VARS:

Here are the variables that define my circle class.  This class is pretty basic and nothing too special has to happen here with regards to the viewport. The class will have to perform range checking with the borders of the world.


// Velocity:
private var velX:Number;
private var velY:Number;

CIRCLE UPDATE METHOD:

Here is a snippet from the update method that gives the basic idea of how to adjust the coords while range checking against the larger world borders.


// Adjust the x coord by current x velocity:
x += velX;

// Perform boundary checking with world borders and adjust
// position and velocity direction accordingly:
if (x < (width / 2)) {
	x = (width / 2);
	velX = -velX;
} else if (x > ViewPort.WORLD_WIDTH - (width / 2)) {
	x = ViewPort.WORLD_WIDTH - (width / 2);
	velX = -velX;
}

So that is my concept of one way to do a basic viewport in AS3.  I am happy to get any feedback on this code and the way I solved this problem.  I know there are many ways to implement these things but this is what I am going to run with for now as it seems to be working pretty good!

EDIT: Thank you to Draknek (see comments below) for an elegant refinement to my code that allowed me to no longer have to track world coordinates on the child objects. Now I can just let Flash handle updating all the positions for me by moving the parent display object instead of manually doing that.

Download the v0.2 source files and fla for this tutorial

Tutorials

8 comments

  1. Draknek says:

    A much neater way of doing this is to modify the x and y coordinates of the parent DisplayObject:

    world.x = -viewportX;
    world.y = -viewportY;

    You can also do easy scaling/rotation like this too, by setting the scale/rotation on the world object.

    Storing both the world location and the camera-space location is generally not a good idea IMO.

  2. Dave "HybridMind" Evans says:

    Interesting idea… I’d love not to have to store two sets of coordinates and have to do continual translation but I’m not sure I understand what you recommend I do in regards to how Flash works in this case.

    Are you recommending that I have a dedicated World displayObject or is the Document Class Sprite enough? I guess I have to mess around a little because I am missing something obvious about how Flash handles the stage space apparently.

    Thanks for leaving your thoughts! I was hoping someone would be able to present some alternative ideas.

  3. Draknek says:

    My world object is equivalent to your ViewPort instance. So my solution would be to replace your enterFrameListener function with:

    private function enterFrameListener(e:Event):void {

    for (var i:int = 0; i < circles.length; i++) {

    circles[i].update();

    // pan the viewport
    x = -viewportX;
    y = -viewportY;
    }

    }

    And then change the Circle class to use x/y rather than worldX/worldY.

    This works because an object's position is relative to its parent. Move the parent and you move all its children. I can't immediately find any brilliant examples/tutorials of this, but hopefully the concept makes sense and you can experiment a bit to see how it works.

  4. Dave "HybridMind" Evans says:

    Hey this does work great and I love the simplicity! Thanks so much for leaving your thoughts. It feels much better to me to not have to store double coordinates and continually remap them.

    I had wanted to do something like what you’ve done but for some reason didn’t realize Flash would make it that easy!

    I also like the scale and rotation idea you mentioned. Not sure why I never really thought of this before.

    I’m going to update the tutorial to include this new, simpler concept.

  5. Dave "HybridMind" Evans says:

    Yes, it does seem I had implemented something Flash already wanted to give me for free! 😉

    I’ve updated the tutorial and the zip file to use your suggestion. I’m glad you stopped by! Now to implement this in the game I’m working on…

  6. Jake Cook says:

    Fuck yeah. Cheers guys! Dranek you’re a Don, Dave your a legend, and Porter in the comments, couldn’t agree more! Peace I’m off to scroll the shit out of my game! 😀 Hooray internet!

Comments are closed.