Funded with Kickstarter

Level Up

Moving right along! Yesterday we downloaded and installed a bunch of frequently-used game development tools. Then we created a new project, hooked up Flixel, and prepared our PlayState for action. Now we’re ready to add things to our game! We’ll start by loading a level from an external file. But first we need to create that level with Tiled.

Tiled

Open Tiled and open its Preferences (Tiled > Preferences…). Make sure “Store tile layer data as” is set to CSV (Flash can unzip and de-Base64 this data but it’s much slower in our testing). Now create a new TMX file (File > New…). Make sure the Orientation is set to Orthogonal, Tile size should be 16px by 16px. Remember that our game’s dimensions are 240x160 pixels. We want our level to be a little bit bigger than that so we can see how Flixel’s camera works. Set the Map size to 20 tiles by 15 tiles and hit the OK button. You need to manually save the new file (File > Save) as level.tmx in your project’s bin folder.

tiled-new.png

Tiled Gotcha: If you don’t see anything in the new window that appears make sure the grid is turned on (View > Show Grid) and that you’re zoomed all the way out (View > Normal Size).

Download this tileset Shaun prepared and add it to your project’s bin folder. Then create an assets folder inside your src folder and copy (not move!) bg.png into the new assets folder too.

bg.png

Back in Tiled create a new tileset (Map > New Tileset…). Browse and select the bg.png you just downloaded. Set Tile width and Tile height both to 16px and click the OK button. In the Layers palette on the right, double-click Tile Layer 1 and rename it to bg.

tiled-tile-tools.png

Now draw your level by first selecting a tile in the Tilesets palette. Then use the paint bucket to flood large areas and the stamp for detail work. The eraser works just like you expect, but the selection tool is a little weird. You can’t move a selection, but you can copy or cut it. If you then paste, your previous selection becomes a large, complex stamp that can be used to easily duplicate detailed areas of your map. When you’re done, you should have something that looks like this:

tiled-level.png

Loading the Level

Now that we’ve created a simple level we need to load it into our game. To do that we’ll use this handy TMX parser library. I’ve extracted the library and commented out some debug trace statements for this project. Download net.pixelpracht.tmx, unzip and copy the entire net folder into your project’s src folder (just like we did with Flixel’s org folder last time). Open FDT and then open PlayState.as.

Beneath:

import org.flixel.*;

add:

// TMX Loading
import net.pixelpracht.tmx.*;
import flash.events.Event;
import flash.net.URLLoader;
import flash.net.URLRequest;

Then add the following private vars to your PlayState class definition:

private var hasLoaded:Boolean = false;
private var background:FlxTilemap = new FlxTilemap();

We need to prevent our PlayState from trying to update itself before the level has loaded so add the following inside the update() function:

if (!hasLoaded) return;

Next, we add the methods that handle loading and parsing the level file to our PlayState class defintion:

// Level loading
private function loadTmxFile():void {
    var loader:URLLoader = new URLLoader();
    loader.addEventListener(Event.COMPLETE, onTmxLoaded);
    loader.load(new URLRequest('level.tmx'));
}

private function onTmxLoaded(e:Event) : void {
    var loader:URLLoader = e.target as URLLoader;
    var xml:XML = new XML(loader.data);
    var tmx:TmxMap = new TmxMap(xml);
    loadStateFromTmx(tmx);
    hasLoaded = true; // allow updates
}

private function loadStateFromTmx(tmx:TmxMap) : void {
    var csvBg:String = tmx.getLayer('bg').toCsv(tmx.getTileSet('bg'));
    background.loadMap(csvBg, ROM.ImgBg, 16, 16, FlxTilemap.OFF,0,0,2);
    add(background);
}

The loadTmxFile() function requests our external level.tmx file. Once loaded, the onTmxLoaded() function parses the file into an object representing our level data. The loadStateFromTmx() function uses that data to load our background FlxTilemap. Then we add the background to our PlayState so it will update and draw in our game!

Now add the following inside the create() function:

loadTmxFile();

After these additions your PlayState.as should look like this:

package {
    import org.flixel.*;
    // TMX Loading
    import net.pixelpracht.tmx.*;
    import flash.events.Event;
    import flash.net.URLLoader;
    import flash.net.URLRequest;

    public class PlayState extends FlxState {
        private var hasLoaded:Boolean = false;
        private var background:FlxTilemap = new FlxTilemap();

        override public function create():void {
            // initiate variables, add sprites, tilemaps, etc

            loadTmxFile();
        }

        // Level loading
        private function loadTmxFile():void {
            var loader:URLLoader = new URLLoader();
            loader.addEventListener(Event.COMPLETE, onTmxLoaded);
            loader.load(new URLRequest('level.tmx'));
        }

        private function onTmxLoaded(e:Event) : void {
            var loader:URLLoader = e.target as URLLoader;
            var xml:XML = new XML(loader.data);
            var tmx:TmxMap = new TmxMap(xml);
            loadStateFromTmx(tmx);
            hasLoaded = true; // allow updates
        }

        private function loadStateFromTmx(tmx:TmxMap) : void {
            var csvBg:String = tmx.getLayer('bg').toCsv(tmx.getTileSet('bg'));
            background.loadMap(csvBg, ROM.ImgBg, 16, 16, FlxTilemap.OFF,0,0,2);
            add(background);
        }

        override public function update():void {
            // listen for user input, move sprites, etc

            if (!hasLoaded) return;
        }
    }
}

If you run the game now you’ll receive an error regarding ROM.ImgBg. Let’s do something about that.

ROM

Time to take a quick detour into asset management. Rather than have image and audio embeds scattered across the different classes in a project, we like to create a singleton to hold all these files. That way, if more than one class needs to use an image or sound effect, they can pull from the same place. Create a new ActionScript class (File > New > ActionScript Class), name it ROM, and click the Finish button. Inside the ROM class definition, add the following:

[Embed(source="assets/bg.png")] static public var ImgBg : Class;

The full ROM.as should look like this:

package {
    public class ROM {
        [Embed(source="assets/bg.png")] static public var ImgBg : Class;
    }
}

Now run the game. You should see the top left corner of our level! In the next Primer we’ll add a player and handle user input.

Copyright © 2012–2018 Retro Game Crunch.