Making a game with Flash Develop + Flixel: Part 9, More Maps

Previously:

(Want to bypass the long explanations and see the final product? Here, download this post’s code as a ZIP file.)

Moving from the last part, we notice that the number of tilemap data files has grown. We now have two (“map1.txt” and “house1.txt”) and, as we start to have a map per house, that number is only going to grow larger. As a result, an increasing amount of our MapGroup file will be devoted over to holding all of the embed information per file.

However, there is another solution to this problem. Instead having so many resources spread across different files, we can copy all of the embed information into one single file to act as a singleton access interface. Like our Registry object, this could serve to hold any changes or updates to filenames or placement.

We can call this our Assets object.

package
{
/**
* ...
* @author Dan Cox
*/
public class Assets
{
[Embed(source="data/houseTiles.png")]
public static var houseTilesPng:Class;
[Embed(source="data/house1.txt",mimeType="application/octet-stream")]
public static var house1Txt:Class;
[Embed(source="data/tilemap.png")]
public static var villagePng:Class;
[Embed(source="data/map1.txt",mimeType="application/octet-stream")]
public static var villageTxt:Class;
[Embed(source="data/NPC1.png")]
public static var NPCPng:Class;
[Embed(source="data/walk.png")]
public static var PlayerWalkPng:Class;
public function Assets()
{
}
}
}
view raw Assets.as hosted with ❤ by GitHub

Thinking about our current MapGroup object, then, we can see that, in using setTileProperties twice for both the village and house maps, we can actually combine their functionality. Instead of making multiple instances of FlxTilemap, we can abstract out something we can name an AreaGroup that has an internal tilemap, the ability to hold characters, and what tile represents the exit or door.

package
{
/**
* ...
* @author Dan Cox
*/
import org.flixel.*;
public class AreaGroup extends FlxGroup
{
private var areaMap:FlxTilemap;
private var _swapFunction:Function;
public var characters:NPCSpriteGroup;
public var doorTile:Number;
public var doorPoint:FlxPoint;
/**
* AreaGroup
* @param map The tilemap data
* @param tiles The tiles
* @param dT The door or exit tile number
*/
public function AreaGroup(map:Class, tiles:Class, dT:Number)
{
doorTile = dT;
areaMap = new FlxTilemap();
areaMap.loadMap(new map, tiles, 16, 16, 0, 0, 1, 2);
add(areaMap);
characters = new NPCSpriteGroup();
add(characters);
var points:Array = areaMap.getTileCoords(doorTile, false);
doorPoint = points[0];
}
public function set swapFunction(func:Function):void
{
_swapFunction = func;
areaMap.setTileProperties(doorTile, FlxObject.ANY, func);
}
}
}
view raw AreaGroup.as hosted with ❤ by GitHub

From there, we can update our MapGroup object to use AreaGroup objects for the village and door, supplying the arguments needed and adding characters to the public property of whatever instance of an AreaGroup needs them.

package
{
/**
* ...
* @author Dan Cox
*/
import org.flixel.*;
import org.flixel.system.FlxTile;
public class MapGroup extends FlxGroup
{
private var previousPlace:FlxPoint;
private var village:AreaGroup;
private var house1:AreaGroup;
public function MapGroup()
{
village = new AreaGroup(Assets.villageTxt, Assets.villagePng, 12);
village.swapFunction = swapHouse;
var npc:NPCSprite = new NPCSprite(48, 320, Assets.NPCPng);
npc.addSpeech("Hi, player! I'm a NPC.");
village.characters.add(npc);
add(village);
house1 = new AreaGroup(Assets.house1Txt, Assets.houseTilesPng, 10);
house1.swapFunction = swapMap;
previousPlace = new FlxPoint();
}
override public function update():void
{
super.update();
FlxG.collide(Registry.player, this);
}
private function swapHouse(tile:FlxTile, player:PlayerSprite):void
{
previousPlace.x = Registry.player.x;
previousPlace.y = Registry.player.y;
Registry.player.x = house1.doorPoint.x;
Registry.player.y = house1.doorPoint.y;
remove(village);
add(house1);
}
private function swapMap(tile:FlxTile, player:PlayerSprite):void
{
Registry.player.x = previousPlace.x;
Registry.player.y = previousPlace.y;
remove(house1);
add(village);
}
}
}
view raw MapGroup.as hosted with ❤ by GitHub

Looking back at AreaGroup, you may have noticed the array points. In the first version posted, it is only looked for the first instance of the exit tile. It expected only one way out of an particular area.

However, our village has four houses. Potentially, we need four different exits (using at least two different tile numbers) and then to associate those houses with tilemap data.

So, to start to move in that direction, let’s add three more tilemap data files for houses.

House 2

0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,5,9,9,9,9,9,4,0,
0,0,0,0,0,3,1,1,1,1,1,2,0,
0,0,0,0,0,3,1,1,1,1,1,2,0,
0,0,0,0,0,3,1,1,1,1,1,2,0,
0,0,0,0,0,3,1,1,1,1,1,2,0,
0,0,0,0,0,3,1,1,1,1,1,2,0,
0,0,0,0,0,3,1,1,1,1,1,2,0,
0,0,0,0,0,3,1,1,1,1,1,2,0,
0,0,0,0,0,3,1,1,1,1,1,2,0,
0,0,0,0,0,6,8,8,10,8,8,7,0

House 3

0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,5,9,9,9,9,9,4,0,
0,0,0,0,0,3,1,1,1,1,1,2,0,
0,0,0,0,0,3,1,1,1,1,1,2,0,
0,0,0,0,0,3,1,1,1,1,1,2,0,
0,0,0,0,0,3,1,1,1,1,1,2,0,
0,0,0,0,0,3,1,1,1,1,1,2,0,
0,0,0,0,0,6,8,8,10,8,8,7,0

House 4

0,0,0,0,0,0,0,0,0,0,0,0,
0,0,5,9,9,9,9,9,9,9,9,4,
0,0,3,1,1,1,1,1,1,1,1,2,
0,0,3,1,1,1,1,1,1,1,1,2,
0,0,3,1,1,1,1,1,1,1,1,2,
0,0,3,1,1,1,1,1,1,1,1,2,
0,0,3,1,1,1,1,1,1,1,1,2,
0,0,3,1,1,1,1,1,1,1,1,2,
0,0,3,1,1,1,1,1,1,1,1,2,
0,0,3,1,1,1,1,1,1,1,1,2,
0,0,6,8,8,8,8,8,10,8,8,7

Update the Assets file:

package
{
/**
* ...
* @author Dan Cox
*/
public class Assets
{
[Embed(source="data/houseTiles.png")]
public static var houseTilesPng:Class;
[Embed(source="data/house1.txt",mimeType="application/octet-stream")]
public static var house1Txt:Class;
[Embed(source="data/house2.txt",mimeType="application/octet-stream")]
public static var house2Txt:Class;
[Embed(source="data/house3.txt",mimeType="application/octet-stream")]
public static var house3Txt:Class;
[Embed(source="data/house4.txt",mimeType="application/octet-stream")]
public static var house4Txt:Class;
[Embed(source="data/tilemap.png")]
public static var villagePng:Class;
[Embed(source="data/map1.txt",mimeType="application/octet-stream")]
public static var villageTxt:Class;
[Embed(source="data/NPC1.png")]
public static var NPCPng:Class;
[Embed(source="data/walk.png")]
public static var PlayerWalkPng:Class;
public function Assets()
{
}
}
}
view raw Assets.as hosted with ❤ by GitHub

Then, we can update the AreaGroup object to take an array of possible doortiles and use a separate function for exposing a map index.

package
{
/**
* ...
* @author Dan Cox
*/
import org.flixel.*;
import org.flixel.system.FlxTile;
public class AreaGroup extends FlxGroup
{
private var areaMap:FlxTilemap;
private var _swapFunction:Function;
private var doorTiles:Array;
public var doorPoint:FlxPoint;
public var characters:NPCSpriteGroup;
public var areaIndex:Number;
/**
* AreaGroup
* @param map The tilemap data
* @param tiles The tiles
* @param dTs The door or exit tile numbers
*/
public function AreaGroup(map:Class, tiles:Class, dTs:Array)
{
doorTiles = dTs;
areaMap = new FlxTilemap();
areaMap.loadMap(new map, tiles, 16, 16, 0, 0, 1, 2);
add(areaMap);
characters = new NPCSpriteGroup();
add(characters);
doorPoint = new FlxPoint();
}
public function set swapFunction(func:Function):void
{
_swapFunction = func;
for each(var index:Number in doorTiles)
{
areaMap.setTileProperties(index, FlxObject.ANY, setIndexSwap);
}
}
private function setIndexSwap(tile:FlxTile, player:PlayerSprite):void
{
MapGroup.mapIndex = areaIndex;
doorPoint.x = tile.x;
doorPoint.y = tile.y;
_swapFunction(doorPoint);
}
}
}
view raw AreaGroup.as hosted with ❤ by GitHub

And with that, we can update MapGroup to use these new properties and functions.

package
{
/**
* ...
* @author Dan Cox
*/
import org.flixel.*;
public class MapGroup extends FlxGroup
{
private var previousPlace:FlxPoint;
private var village:AreaGroup;
private var areas:Array;
public static var mapIndex:Number;
public function MapGroup()
{
village = new AreaGroup(Assets.villageTxt, Assets.villagePng, [11, 12]);
village.swapFunction = swapHouse;
var npc:NPCSprite = new NPCSprite(48, 320, Assets.NPCPng);
npc.addSpeech("Hi, player! I'm a NPC.");
village.characters.add(npc);
add(village);
areas = new Array();
var house1:AreaGroup = new AreaGroup(Assets.house1Txt, Assets.houseTilesPng, [10]);
house1.swapFunction = swapMap;
house1.areaIndex = 0;
areas.push(house1);
var house2:AreaGroup = new AreaGroup(Assets.house2Txt, Assets.houseTilesPng, [10]);
house2.swapFunction = swapMap;
house2.areaIndex = 1;
areas.push(house2);
var house3:AreaGroup = new AreaGroup(Assets.house3Txt, Assets.houseTilesPng, [10]);
house3.swapFunction = swapMap;
house3.areaIndex = 2;
areas.push(house3);
var house4:AreaGroup = new AreaGroup(Assets.house4Txt, Assets.houseTilesPng, [10]);
house4.swapFunction = swapMap;
house4.areaIndex = 3;
areas.push(house4);
previousPlace = new FlxPoint();
}
override public function update():void
{
super.update();
FlxG.collide(Registry.player, this);
}
private function swapHouse(index:FlxPoint):void
{
previousPlace.x = Registry.player.x;
previousPlace.y = Registry.player.y;
if (index.y == 304)
{
if (index.x == 80)
{
mapIndex = 0;
}
else
{
mapIndex = 1;
}
}
if (index.y == 480)
{
if (index.x == 112)
{
mapIndex = 2;
}
else
{
mapIndex = 3;
}
}
Registry.player.x = Registry.player.y = 128;
remove(village);
add(areas[mapIndex]);
}
private function swapMap(index:FlxPoint):void
{
Registry.player.x = previousPlace.x;
Registry.player.y = previousPlace.y;
remove(areas[mapIndex]);
add(village);
}
}
}
view raw MapGroup.as hosted with ❤ by GitHub

(Note: Right now, we are using hard-coded values for placement. Ideally, you should avoid that. Door placement should be read from a file or as part of a larger data set.)

Now, we can walk into different rooms (areas) by going through the doors to houses.

step1

In Part 10, we move to include Merchants and Enemies by extending NPCSprite as the start towards a basic battle and economic systems.