Tiled/TMX map loader


(der_r) #1

Here’s my implementation of a Tiled editor (.TMX) loader. It supports multiple tilesets per layer by creating one big master tileset out of all used tilesets. I’m not sure this is the most elegant implementation but it works. Maybe someone will find this useful.

package  
{
	import flash.display.BitmapData;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.utils.Dictionary;
	import net.flashpunk.Entity;
	import net.flashpunk.graphics.Image;
	import net.flashpunk.graphics.Tilemap;
	import net.flashpunk.World;
	import net.flashpunk.FP;
	
	public class TmxWorld extends World 
	{
		private var numTiles:uint;
		private var masterTileset:BitmapData;
		private var tilesets:Array;
		private var data:XML;
		
		public function TmxWorld(rawData:Class)
		{
			data = FP.getXML(rawData);
			
			// Create master tileset of all used tilesets
			RegisterTilesets(data.tileset);
			BuildMasterTileset();
			
			// Create the rest of your level here. Use CreateLayerEntity to make the tilemaps.
		}
		
		public function CreateLayerEntity(name:String, layer:int = 0):Entity
		{
			var layerData:String = "";
			var tilemap:Tilemap = new Tilemap(masterTileset, data.@width * data.@tilewidth, data.@height * data.@tileheight, data.@tilewidth, data.@tileheight);
			
			for each (var e:XML in data.layer)
			{
				if (name == e.@name)
				{
					layerData = e.data;
				}
			}
			
			if (layerData.length)
			{
				tilemap.loadFromString(layerData);
			}
			
			var ent:Entity = new Entity(0, 0, tilemap);
			ent.layer = layer;
			
			return ent;
		}
		
		public function RegisterTilesets(data:XMLList):void
		{
			if (!tilesets)
			{
				tilesets = new Array();
				numTiles = 1; // Tiled offset
			}
			
			for each(var e:XML in data)
			{
				var b:BitmapData = FP.getBitmap(Assets.GetTilesetByName(e.@name))
				tilesets.push(b);
				numTiles += (b.width / 16) * (b.height / 16);
			}
		}
		
		public function BuildMasterTileset():void
		{
			var masterWidth:uint = 16 * 8; // Modify this if you use tiles that are not 16x16;
			var masterHeight:uint = Math.ceil(numTiles / (masterWidth/16)) * 16;
			
			var masterWidthTiles:uint = masterWidth / 16;
			var masterHeightTiles:uint = masterHeight / 16;
			
			masterTileset = new BitmapData(masterWidth, masterHeight, true, 0x00000000);
			
			var destinationPoint:Point = new Point();
			var sourceRectangle:Rectangle = new Rectangle(0, 0, 16, 16);
			
			var masterCounter:uint = 1; // Tiled offset again. Leave the first one blank/black.
			
			for each(var e:BitmapData in tilesets)
			{
				var sourceWidthTiles:uint = (e.width / 16);
				var sourceHeightTiles:uint = (e.height / 16);
				var sourceTiles:uint = sourceWidthTiles * sourceHeightTiles;
				
				for (var i:uint = 0; i < sourceTiles; i++)
				{
					sourceRectangle.x = (uint)(i % sourceWidthTiles) * 16;
					sourceRectangle.y = (uint)(i / sourceWidthTiles) * 16;
					
					destinationPoint.x = (uint)(masterCounter % masterWidthTiles) * 16;
					destinationPoint.y = (uint)(masterCounter / masterWidthTiles) * 16;
					
					masterTileset.copyPixels(e, sourceRectangle, destinationPoint);
					
					masterCounter++;
				}
			}
			
		}
	}
}


// Assets

package  
{
	public class Assets 
	{
		[...]
		
		// Tilesets
		[Embed(source = "../assets/tilesets/basic.png")] public static const IMG_BASIC:Class;
		[Embed(source = "../assets/tilesets/solid.png")] public static const IMG_GRID:Class;
		[Embed(source="../assets/tilesets/crypt.png")] public static const IMG_CRYPT:Class;
		
		// Map tileset names used in Tiled to their classes
		public static function GetTilesetByName(name:String):Class
		{
			if (name == "basic") return IMG_BASIC;
			if (name == "crypt") return IMG_CRYPT;
			if (name == "solid") return IMG_GRID;
			
			return Assets.IMG_BASIC;
		}
	}

}

Xml parsing doesn't work! (followed zachs tut)
TMX tile map, is it possiable
(bnnorman) #2

Hi, I’ve been looking for something like this for a while. I know it’ an old thread but I’ve been teaching FLASH to secondary school and , well, writing a game is good motivation. I was trying to eliminate the hard coded tile width/height of 16 but came across the ‘8’ in the first line of BuildMasterTileset() method - where does this come from? Is it arbitrary? i.e. are you just creating a tileset bitmap 8 tiles wide? Thanks for any clarification.


(der_r) #3

Sorry about that. I forgot there is a “magic number” in there. :smiley:

Yes, the 8 is the number of tiles in a single row in the master tileset. I chose it arbitrarily as I was prototyping with very few tiles only.


(bnnorman) #4

many thanks for the prompt confirmation. Much appreciated.