FlashPunk, DAME and Lua Tutorial (Part 5)
04/17/2011
After covering various game engines and level editors for flash, the project layout, the DAME templates, the XML format of our levels, and the Lua export script, we're finally ready to get into the ActionScript. Along with learning Lua in the last step, this was also my first experience using FlashPunk. As I mentioned in part 0, after experimenting with Flixel and PushButton, FlashPunk just seemed to work closer to the way I design games in my head (and on paper).
The overall structure of the code is pretty simple. Take a look at the following diagram, which illustrates the components used in the tutorial.

As you can see, the main class is FPDameTemplatesTutorial.as, which is the class we have set to "Always Compile" in FlashDevelop:

Let's take a look at the actual code from FPDameTemplatesTutorial.as:

package
{
import net.flashpunk.Engine;
import net.flashpunk.FP;
/**
* FlashPunk, DAME and LUA Tutorial
*
* @author Thomas Gorence
* @see http://producerism.com
*
* This tutorial was written to be followed along
* with a series on the producerism.com/blog
*/
public class FPDameTemplatesTutorial extends Engine
{
public function FPDameTemplatesTutorial():void
{
// make sure this matches your compiler settings
// you can set the numbers to almost anything
super(800, 500);
// this will display FPS and other info
// comment this line to turn it off
FP.console.enable();
}
override public function init():void
{
trace("FlashPunk has initialized");
FP.world = new TestWorld();
super.init();
}
}
}
Not including any of the imports, class definitions, or comments, there's really only 4 lines of code here that are doing anything. First of all, we only need to import 2 classes from FlashPunk: the Engine, and the global helper class, FP.
import net.flashpunk.Engine; import net.flashpunk.FP;
Engine is just a very specialized MovieClip, which handles lots of game-related functions like maintaining the frame rate, reacting to items being added to the stage, and basically anything time-related. For the most part, you won't ever need to set your own Timers or ENTER_FRAME listeners. All of that happens within this Engine class (automatically).
FP is a class with all static functions (meaning you can access them without creating an "FP" object). It's your go-to class for getting and setting global information like the camera, frame rate and screen size, along with lots of utility functions for checking collisions, sorting objects, loading, etc.
The constructor function within our main class (FPDameTemplatesTutorial.as) does two things:
public function FPDameTemplatesTutorial():void
{
// make sure this matches your compiler settings
// you can set the numbers to almost anything
super(800, 500);
// this will display FPS and other info
// comment this line to turn it off
FP.console.enable();
}
On line 23, we use super() to call the constructor function of the class we've extended (net.flashpunk.Engine). Here's a peak at what Engine is looking for (Engine.as):
public function Engine(width:uint, height:uint, frameRate:Number = 60, fixed:Boolean = false)
Since it's looking for width, height, frameRate and fixed (whether or not to set a fixed frame rate), we need to pass in at least the width and the height. So that's exactly what we do on line 23. Then on line 27, we enable the FlashPunk debugging console, which is extremely helpful in development.
After the constructor function has run, and once FlashPunk has initialized, it will fire the init() function in our FPDameTemplatesTutorial.as:
override public function init():void
{
trace("FlashPunk has initialized");
FP.world = new TestWorld();
super.init();
}
This is an override function, meaning that it's already part of the Engine class we extended, so it's being called on it's own. However, if we want to add some custom functions we need to write our own init() before letting the Engine run it's own code, called on line 34 with super.init(). On line 33, we set the global world variable by using the FP helper class. By setting the world, we are telling FlashPunk to load our TestWorld() class. FP.world is basically the current screen/state/level. For example, splash screens, menu screens, different levels, top score screens, these are all "worlds" in FlashPunk terms. I believe in Flixel they are called states. Then, when we call super.init(), our game actually starts.
TestWorld

The only custom class we referenced in the main class above was TestWorld.as:
public class TestWorld extends World
{
// since the maps in DAME can be of different widths, we need
// a way to keep track of the total length of the level we generate
private var _totalMapsWidth:Number = 0.0;
// create a variable for the player, to use in the update() function
private var _player:Entity;
public function TestWorld()
{
trace("TestWorld Constructor");
super();
}
This is essentially the code which drives the entire game, by adding the background, player, and level items (tiles and enemies) which we created in DAME. To keep things simple, we've only created two private variables for this World on lines 22 and 25.
_totalMapsWidth is just a number to keep track of the entire length of the level generated. Remember that this project will create an infinitely long level, and to make use of the FlashPunk camera system, we need to keep track of the total offset. The other variable, _player, is self-explanatory; it just holds a reference to the player's character, so it can be referenced in the update() function.
Just like the override init() function from the FPDameTemplatesTutorial class (which extended the FlashPunk Engine class), our TestWorld() extends the World class. In this case we want to override two functions, begin() and update(). Let's look at begin():
override public function begin():void
{
// add the background first, so that it's on the bottom be default
add(new BackgroundEntity);
// add the player, and set depth to make sure they are always on top
_player = add(new Zeppelin);
_player.layer = -1;
// generate maps until they fill the entire screen
while (_totalMapsWidth < FP.stage.width + FP.camera.x)
{
spawnMap();
}
super.begin();
}
As you can guess, the begin() function runs when our World has been set to be the active FP.world. On line 37, we add our BackgroundEntity (this is the scrolling background image for a parallax effect). Then on line 40, we create our _player, using a Zeppelin class, and set the layer to -1, which makes sure it will always be on top (the lower the number, the higher the entity will be in the depth-sorting).
On lines 44-47, we just have a loop that will create maps until the screen is totally full. As you can see, we're checking whether our _totalMapsWidth is smaller than the with of the stage plus the x position of our camera - and if it's smaller, we just spawn another map and check again.
The update() function is pretty similar:
override public function update():void
{
// just have the camera follow the player
FP.camera.x = _player.x - 50;
// generate maps until they fill the entire screen
while (_totalMapsWidth < FP.stage.width + FP.camera.x)
{
spawnMap();
}
super.update();
}
On line 55, we simply make sure the camera is always 50 pixels behind our _player. This means we really only have to move our player around in code, and the camera will make sure everything else (backgrounds, objects, enemies, maps) pass by automatically. As before we simply call spawnMap() to generate a new map (based on the DAME templates) until the screen is full (lines 58-61).
We've already added the background and the _player to our screen, so lets look at the last function, spawnMap(), which takes care of everything else:
private function spawnMap():void
{
// generate a random number, based on the total map layers in XML exported from DAME
var randomMapNumber:Number = Math.ceil(Math.random() * Assets.LEVEL_TEMPLATES_COUNT);
var mapLayer:String = "template" + randomMapNumber;
// notice we are using add() and not create(), which is why we're also setting recycleMaps to false
var testMap:TilemapGeneratorEntity = new TilemapGeneratorEntity(Assets.LEVEL_TEMPLATES_XML, Assets.BG_TILES, mapLayer, "level", _totalMapsWidth, 0, true, false);
add(testMap);
// update the total length of the generated level so far, so we know where to place the next map
_totalMapsWidth += testMap.mapLayerWidth;
}
First, we grab a random number between 1 and the total number of maps (<tiles> nodes) in our exported XML file (line 69). I've actually stored the number of maps in a constant variable called Assets.LEVEL_TEMPLATES_COUNT, which I'll go into later. Then we just create a string for the name of the random template (e.g. "template4" will refer to "template4" in our DAME project). Next, we generate the tilemap using a custom class, TilemapGeneratorEntity (line 73) and then add it to the world (line 74). Finally, we update the _totalMapsWidth, to keep track of how wide our world is after adding the newly generated map.
BackgroundEntity

Within our TestWorld class that we just covered, we really only added three entities (any object added to a World in FlashPunk is called an Entity). The first of those entities was called BackgroundEntity.as. Since all of the code within this class is in the constructor function, let's just look at that:
public function BackgroundEntity()
{
// create tilemap
var _tiles:Tilemap = new Tilemap(Assets.BG_TILES, 32, 32, 32, 32);
_tiles.setTile(0, 0, 7);
// crate bitmapdata from tilemap
var _bitmapData:BitmapData = new BitmapData(_tiles.width, _tiles.height, true, 0x00ffffff);
_tiles.render(_bitmapData, new Point(0, 0), FP.camera.clone());
// create a backdrop from bitmapdata (seamless tile background)
var _backdrop:Backdrop = new Backdrop(_bitmapData);
_backdrop.scrollX = _backdrop.scrollY = .5;
// set the graphic to the backdrop
graphic = _backdrop;
}
First, we create a FlashPunk Tilemap, which is a class that acts as a canvas to quickly draw tiles on. When creating the Tilemap, we pass in the actual tile images (bgtiles.png), followed by the width/height of the entire tilemap, followed by the width/height of each tile. In this tutorial, our tilemap is the same size as one tile (32px by 32px), because we will just repeat that single tile as the background. If we wanted a more detailed background, we could create a much bigger tilemap. Then, since our bgtiles.png file actually has 10 32x32 pixels in it, we want to select the 8th tile (index of 7, since all arrays start at 0!) on line 26.

Now that we've created a Tilemap (which is really just a single tile right now), we want to repeat that map (again, just a single tile for this tutorial) seamlessly, and add a parallax scrolling effect to it. Luckily, there is a Backdrop class included with FlashPunk which serves this exact purpose. Since it requires a BitmapData instead of a Tilemap, we create the bitmap data on lines 29-30. We simply create a BitmapData object that is 32px by 32px, then use the _tiles.render() function (inherited from FP.graphics.Canvas) to draw the tilemap into _bitmapData.
That may seem like a convoluted way to create a Backdrop from a Tilemap in FlashPunk - and it probably is. However, I couldn't find a better way to do this, even with help from the FlashPunk IRC channel, forum and other tutorials. If you know of a better way, please let me know! Anyways, now that we have a _bitmapData, we can finally pass that into a very anti-climactic Backdrop object on line 33. Then, to add the nifty scrolling effect we simply set the _backdrop.scrollX and scrollY to 0.5, which means the background will scroll at half the rate of our camera (e.g. if our map scrolls 10px, our background will only scroll 5px).

Finally, we set the graphic of our Entity (BackgroundEntity) to the _backdrop that we went through all this trouble creating. Now when it's added to the TestWorld, it will simply show up on the screen, and will automatically scroll with the parallax effect when the camera moves.
Zeppelin

Still with me? Good. Let's take a look at our _player, which uses the Zeppelin class. Again to make it easier to follow along, we'll just look at the main functions and ignore all of the imports.
We only have two private variables for this class:
// setting an upward force to pull Zeppelin up private static const GRAVITY:Number = -35.0; // how fast Zeppelin moves up/down and forward private var _speed:Number = 200.0;
Unlike most games, we've set our GRAVITY to be negative in this tutorial. The reason is because our Zeppelin will always be rising up into the air (at a rate of -35 pixels), and the player will need to push the Zeppelin back down. We also set the _speed of our Zeppelin, which is how fast it moves up and down when the player uses the arrow keys to control it.
Our Zeppelin constructor function is pretty simple:
public function Zeppelin()
{
x = 20;
y = 50;
mask = new Pixelmask(Assets.ZEPPELIN_SPRITE);
graphic = new Image(Assets.ZEPPELIN_SPRITE);
type = "player";
}
The x and y coordinates are set so that the Zeppelin is at the top left of the screen when we start. Then, we create a mask for pixel-perfect collisions using the Pixelmask class (another awesome feature of FlashPunk). With the mask set, we just assign the same image we used for the mask, to the graphic of the Zeppelin Entity (remember, when dealing with FlashPunk Entities, the "graphic" is what gets drawn to the screen).
The only remaining function in this Zeppelin class is what makes the Zeppelin move, update():
override public function update():void
{
var xPos:Number = x;
var yPos:Number = y;
// apply gravity
yPos += GRAVITY * FP.elapsed;
Right away, we store the current position of our Zeppelin into xPos and yPos. Remember, this is an override function (line 38), so FlashPunk is calling this (Zeppelin.update()) for us every frame, while the game is game is running. We want to move our Zeppelin in sync with the camera and other entities, so we multiply any offsets by FP.elapsed. This was hard to grasp at first, but easy to think of it this way: The default FPS for the FlashPunk Engine class is 60, so instead of GRAVITY pushing our Zeppelin up by -35px each second, we're actually pushing our Zeppelin up by GRAVITY/FPS (-35/60), each time the update() function gets called.
With that out of the way, lets look at the rest of our Zeppelin:
// forward movement
xPos += _speed * FP.elapsed;
// Input checking
if (Input.check(Key.UP))
{
// move up
yPos -= _speed * FP.elapsed;
} else if (Input.check(Key.DOWN))
{
// move down
yPos += _speed * FP.elapsed;
}
// finally, set the Zeppelin's position (within limits!)
y = Math.min(FP.stage.height-height,Math.max(0,yPos));
x = xPos;
super.update();
}
Notice that our Zeppelin already has an inherent speed, so the first thing we to to the x position (xPos), is apply that forward speed. Next, we use the Input helper class to check for button presses. Then, before calling super.update(), we set the position of the actual Zeppelin. I'm using Math.min() and Math.max() (line 62) to ensure the Zeppelin never goes off the screen on the y axis, but there are lots of other ways to do this.
We've covered the main Engine class of FlashPunk (FPDameTemplatesTutorial.as), the World class (TestWorld.as), the BackgroundEntity.as, which is our scrolling background with the parallax effect, and even our Zeppelin class (Entity), which the player controls.
Of course, so far none of this code has touched on where our DAME templates come into the picture, which is all handled in the TilemapGeneratorEntity.as class. It's a good time to take a break, so that's the first thing we'll cover in the next part to this tutorial series.