Parsing TILED XML in flashpunk, can't seem to add bitmaps and shapes?


(John Andersson) #1

I “converted” the code from this tutorial:

to work with flashpunk.

package levels.level1
{
import blocks.*;
import enemies.*;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Shape;
import flash.events.*;
import flash.events.Event;
import flash.geom.*;
import flash.geom.Point;
import flash.net.URLLoader;
import flash.net.URLRequest;
import hero.*;
import net.flashpunk.Entity;
import net.flashpunk.FP;

public class Level1 extends Entity
{
	private var xmlLoader:URLLoader; // for reading the tmx file
	private var xml:XML; // for storing the tmx data as xml
	private var screenBitmap:Bitmap; // for drawing the map
	public var screenBitmapTopLayer:Bitmap; // data of an image, for drawing the map that the character will move under
	
	public var mapWidth:uint;
	public var mapHeight:uint;
	private var tileWidth:uint;
	private var tileHeight:uint;
	
	private var tileSets:Array = new Array();
	
	private var totalTileSets:uint = 0;
	private var tileSetsLoaded:uint = 0;
	
	public var collisionTiles:Array = new Array();
	private var eventLoaders:Array = new Array();
	
	public function Level1(xml:Class)
	{			
		type = "ground";
		
		xmlLoader = new URLLoader();
		xmlLoader.addEventListener(Event.COMPLETE, xmlLoadComplete);
		xmlLoader.load(new URLRequest("../Level_1/level_1.tmx"));
	}
	
	private function xmlLoadComplete(e:Event):void
	{
		xml = new XML(e.target.data);
		mapWidth = xml.attribute("width");
		mapHeight = xml.attribute("height");
		tileWidth = xml.attribute("tilewidth");
		tileHeight = xml.attribute("tileheight");
		
		var xmlCounter:uint = 0;
		for each (var tileset:XML in xml.tileset)
		{
			var imageWidth:uint = xml.tileset.image.attribute("width")[xmlCounter];
			var imageHeight:uint = xml.tileset.image.attribute("height")[xmlCounter];
			var firstGid:uint = xml.tileset.attribute("firstgid")[xmlCounter];
			var tilesetName:String = xml.tileset.attribute("name")[xmlCounter];
			var tilesetTileWidth:uint = xml.tileset.attribute("tilewidth")[xmlCounter];
			var tilesetTileHeight:uint = xml.tileset.attribute("tileheight")[xmlCounter];
			var tilesetImagePath:String = xml.tileset.image.attribute("source")[xmlCounter];
			tileSets.push(new TileSet(firstGid, tilesetName, tilesetTileWidth, tilesetTileHeight, tilesetImagePath, imageWidth, imageHeight));
			xmlCounter++;
		}
		totalTileSets = xmlCounter;
		
		// load images for tileset
		for (var i:int = 0; i < totalTileSets; i++)
		{
			trace("load tilset at " + tileSets[i].source);
			var loader:TileCodeEventLoader = new TileCodeEventLoader();
			loader.contentLoaderInfo.addEventListener(Event.COMPLETE, tilesLoadComplete);
			loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, progressHandler);
			loader.tileSet = tileSets[i];
			loader.load(new URLRequest("../assets/" + tileSets[i].source));
			eventLoaders.push(loader);
		}
		screenBitmap = new Bitmap(new BitmapData(mapWidth * tileWidth, mapHeight * tileHeight, false, 0x22ffff));
		screenBitmapTopLayer = new Bitmap(new BitmapData(mapWidth * tileWidth, mapHeight * tileHeight, true, 0));
	}
	
	private function progressHandler(event:ProgressEvent):void
	{
		trace("progressHandler: bytesLoaded=" + event.bytesLoaded + " bytesTotal=" + event.bytesTotal);
	}
	
	private function tilesLoadComplete(e:Event):void
	{
		var currentTileset:TileSet = e.target.loader.tileSet;
		currentTileset.bitmapData = Bitmap(e.target.content).bitmapData;
		tileSetsLoaded++;
		// wait until all the tileset images are loaded before we combine them layer by layer into one bitmap
		if (tileSetsLoaded == totalTileSets)
		{
			addTileBitmapData();
		}
	}
	
	private function addTileBitmapData():void
	{
		// load each layer
		for each (var layer:XML in xml.layer)
		{
			var tiles:Array = new Array();
			var tileLength:uint = 0;
			// assign the gid to each location in the layer
			for each (var tile:XML in layer.data.tile)
			{
				var gid:Number = tile.attribute("gid");
				// if gid > 0
				if (gid > 0)
				{
					tiles[tileLength] = gid;
				}
				tileLength++;
			}
			
			var useBitmap:BitmapData;
			
			var layerName:String = layer.attribute("name")[0];
			
			// decide where we're going to put the layer
			var layerMap:int = 0;
			switch (layerName)
			{
				case "Top": 
					layerMap = 1;
					break;
				default: 
				//trace("using base layer");
			}
			
			// store the gid into a 2d array
			var tileCoordinates:Array = new Array();
			for (var tileX:int = 0; tileX < mapWidth; tileX++)
			{
				tileCoordinates[tileX] = new Array();
				for (var tileY:int = 0; tileY < mapHeight; tileY++)
				{
					tileCoordinates[tileX][tileY] = tiles[(tileX + (tileY * mapWidth))];
				}
			}
			
			for (var spriteForX:int = 0; spriteForX < mapWidth; spriteForX++)
			{
				for (var spriteForY:int = 0; spriteForY < mapHeight; spriteForY++)
				{
					var tileGid:int = int(tileCoordinates[spriteForX][spriteForY]);
					var currentTileset:TileSet;
					// only use tiles from this tileset (we get the source image from here)
					for each (var tileset1:TileSet in tileSets)
					{
						if (tileGid >= tileset1.firstgid - 1 && tileGid <= tileset1.lastgid)
						{
							// we found the right tileset for this gid!
							currentTileset = tileset1;
							break;
						}
					}
					var destY:int = spriteForY * tileWidth;
					var destX:int = spriteForX * tileWidth;
					// basic math to find out where the tile is coming from on the source image
					tileGid -= currentTileset.firstgid - 1;
					var sourceY:int = Math.ceil(tileGid / currentTileset.tileAmountWidth) - 1;
					var sourceX:int = tileGid - (currentTileset.tileAmountWidth * sourceY) - 1;
					// copy the tile from the tileset onto our bitmap
					if (layerMap == 0)
					{
						screenBitmap.bitmapData.copyPixels(currentTileset.bitmapData, new Rectangle(sourceX * currentTileset.tileWidth, sourceY * currentTileset.tileWidth, currentTileset.tileWidth, currentTileset.tileHeight), new Point(destX, destY), null, null, true);
					}
					else if (layerMap == 1)
					{
						screenBitmapTopLayer.bitmapData.copyPixels(currentTileset.bitmapData, new Rectangle(sourceX * currentTileset.tileWidth, sourceY * currentTileset.tileWidth, currentTileset.tileWidth, currentTileset.tileHeight), new Point(destX, destY), null, null, true);
					}
				}
			}
		}
		
		/*for each (var objectgroup:XML in xml.objectgroup)
		{
			var objectGroup:String = objectgroup.attribute("name");
			switch (objectGroup)
			{
				case "Collision": 
					for each (var object:XML in objectgroup.object)
					{
						var rectangle:Shape = new Shape();
						rectangle.graphics.beginFill(0x0099CC, 1);
						rectangle.graphics.drawRect(0, 0, object.attribute("width"), object.attribute("height"));
						rectangle.graphics.endFill();
						rectangle.x = object.attribute("x");
						rectangle.y = object.attribute("y");
						collisionTiles.push(rectangle);
						FP.world.add(rectangle);
					}
					break;
				default: 
					trace("unrecognized object type:", objectgroup.attribute("name"));
			}
		}*/
		
		// load background layer
		FP.world.add(screenBitmap);
		
		// load player, enemies, powerups, etc
		
		// rectangle just to demonstrate how something would look in-between layers
		var playerExample:Shape = new Shape();
		playerExample.graphics.beginFill(0x0099CC, 1);
		playerExample.graphics.lineStyle(2); // outline rectangle
		playerExample.graphics.drawRect(0, 0, 100, 100);
		playerExample.graphics.endFill();
		playerExample.x = 420;
		playerExample.y = 260;
		collisionTiles.push(playerExample);
		FP.world.add(playerExample);
		
		// load top layer
		FP.world.add(screenBitmapTopLayer);
	}

}

}

Level1.as is in the same fashion as in the tutorial,

tileset.as and tilecodeventloader.as too.

I get these errors:

col: 17 Error: Implicit coercion of a value of type flash.display:Bitmap to an unrelated type net.flashpunk:Entity.
col: 17 Error: Implicit coercion of a value of type flash.display:Shape to an unrelated type net.flashpunk:Entity
col: 17 Error: Implicit coercion of a value of type flash.display:Bitmap to an unrelated type net.flashpunk:Entity..

(Jacob Albano) #2

We just talked about this exact same error in your last thread.

Error 1: Bitmaps are not Entities.
Error 2: Shapes are not Entities
Error 3: Bitmaps are (again) not Entities.

You will have to create a graphic from each bitmap or shape, add it to an entity, and add that entity to the world.


(John Andersson) #5

Thank you.

I fixed the errors but now I get this:

Error #2044: Unhandled ioError:. text=Error #2032: Stream Error. URL: file:///C|/Game/Level_1/level_1.tmx
at levels.level1::Level1()
at game_handling::GameWorld/begin()
at net.flashpunk::Engine/checkWorld()
at net.flashpunk::Engine/update()
at net.flashpunk::Engine/onEnterFrame()

I have no idea what it means


(I'll tell you what I want, what I really really want) #6

Error #2032 : "Are you dynamically pulling in audio files or something like that?

I think that error means that you tried to load in an external file but that the URL is wrong, and that no file was found at that location. Double-check to make sure that any URLs you are pulling in are accurate!"

from http://stackoverflow.com/questions/5515301/error-2044-unhandled-ioerrorevent-text-error-2032-stream-error

and

Error #2044

http://forums.adobe.com/thread/999454


(John Andersson) #7

I see, I changed the url to simply:

xmlLoader.load(new URLRequest("level_1.tmx"));

and put the tmx file in the same folder as Level1.as, but I still get the error


(Jacob Albano) #8

URLRequests are sourced out of the working directory, where your SWF ends up. Either move the file there or embed the XML and use FP.getXML() on the asset class.


(John Andersson) #9

The swf ends up in a folder called bin, so I put the tmx file there.

Now I get this error:

Error #2044: Unhandled IOErrorEvent:. text=Error #2035: URL Not Found.

So maybe it wasn’t about the url location after all (in the first place) ??

I’m not sure how I should write out the FP.getXML() after I make a constant variable from the embedded file. Should I type something like

xmlLoader.load(new URLRequest(FP.getXML(level1varnamehere)));

(Jacob Albano) #10

The message says “URL Not Found” – it’s definitely about the file location.

public static function getXML(file:Class):XML
Loads the file as an XML object.

var xml:XML = FP.getXML(LEVEL);

(John Andersson) #11

I meant in the beginning when I got the stream error.

Anyway, I have tried different combinations such as

		xmlLoader = new URLLoader();
		var xml:XML = FP.getXML(LEVEL1);
		xmlLoader.addEventListener(Event.COMPLETE, xmlLoadComplete);
		xmlLoader.load(new URLRequest(xml));

and other combinations, but then I realized you might be talking about the xmlLoadComplete function

so I changed that from

xml = new XML(e.target.data);

to

xml = FP.getXML(LEVEL);

But the level is black, does that mean it didn’t load yet or did I mess up (as usual)?


(Jacob Albano) #12

Dude, I’m talking about only using the getXML function. It creates an XML object from an embedded file, no URLLoader required.


(John Andersson) #14

Oh okay, sorry.

now I fixed it but it displays this error:

The connection to file:(tilesetnamehere) has been stopped - not allowed from (swf location here) Error #2044: Unhandled IOErrorEvent:. text=Error #2035: URL Not Found.

I double checked and the location of the tileset is correct


(Jacob Albano) #15

Okay.

Why are you using URLLoaders in the first place? Is it just because the tutorial says to? What’s stopping you from going through the code, looking at each element, determining what it does, and converting it to use the Flashpunk approach if necessary?

This is my last answer of the day. I’m taking a break from the forums for a while.


(John Andersson) #16

Because

A) It’s a tutorial.

B) I am not good enough to magically realize the correct code, hence all of my questions.

C) I have tried converting it to the FP way, but it’s hard.

Anyway, thanks for your help. Take care! :slight_smile: I will enjoy harassing you more when you come back.

Does anyone else know how to fix this hellish problem? :stuck_out_tongue:


(billy2000) #17

Have u tried embedding it rather then using URLLoader?


(John Andersson) #18

Yes. Now I have this code:

package levels.level1
{
import blocks.*;
import enemies.*;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Shape;
import flash.events.*;
import flash.events.Event;
import flash.geom.*;
import flash.geom.Point;
import flash.net.URLLoader;
import flash.net.URLRequest;
import hero.*;
import net.flashpunk.Entity;
import net.flashpunk.FP;
import net.flashpunk.Graphic;
import net.flashpunk.graphics.Image;

public class Level1 extends Entity
{
	private var xmlLoader:URLLoader; // for reading the tmx file
	private var xml:XML; // for storing the tmx data as xml
	private var screenBitmap:Bitmap; // for drawing the map
	public var screenBitmapTopLayer:Bitmap; // data of an image, for drawing the map that the character will move under
	
	public var mapWidth:uint;
	public var mapHeight:uint;
	private var tileWidth:uint;
	private var tileHeight:uint;
	
	private var tileSets:Array = new Array();
	
	private var totalTileSets:uint = 0;
	private var tileSetsLoaded:uint = 0;
	
	public var collisionTiles:Array = new Array();
	private var eventLoaders:Array = new Array();
	
	[Embed(source = "level_1.tmx", mimeType = "application/octet-stream")] public const LEVEL1:Class;
	
	public function Level1()
	{			
		type = "ground";

		xml = FP.getXML(LEVEL1);
		xmlLoadComplete();
	}
	
	private function xmlLoadComplete():void
	{
		mapWidth = xml.attribute("width");
		mapHeight = xml.attribute("height");
		tileWidth = xml.attribute("tilewidth");
		tileHeight = xml.attribute("tileheight");
		
		var xmlCounter:uint = 0;
		for each (var tileset:XML in xml.tileset)
		{
			var imageWidth:uint = xml.tileset.image.attribute("width")[xmlCounter];
			var imageHeight:uint = xml.tileset.image.attribute("height")[xmlCounter];
			var firstGid:uint = xml.tileset.attribute("firstgid")[xmlCounter];
			var tilesetName:String = xml.tileset.attribute("name")[xmlCounter];
			var tilesetTileWidth:uint = xml.tileset.attribute("tilewidth")[xmlCounter];
			var tilesetTileHeight:uint = xml.tileset.attribute("tileheight")[xmlCounter];
			var tilesetImagePath:String = xml.tileset.image.attribute("source")[xmlCounter];
			tileSets.push(new TileSet(firstGid, tilesetName, tilesetTileWidth, tilesetTileHeight, tilesetImagePath, imageWidth, imageHeight));
			xmlCounter++;
			trace("HI")
		}
		totalTileSets = xmlCounter;
		
		// load images for tileset
		for (var i:int = 0; i < totalTileSets; i++)
		{
			trace("load tileset at " + tileSets[i].source);
			var loader:TileCodeEventLoader = new TileCodeEventLoader();
			loader.contentLoaderInfo.addEventListener(Event.COMPLETE, tilesLoadComplete);
			loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, progressHandler);
			loader.tileSet = tileSets[i];
			loader.load(new URLRequest("../assets/" + tileSets[i].source));
			eventLoaders.push(loader);
		}
		screenBitmap = new Bitmap(new BitmapData(mapWidth * tileWidth, mapHeight * tileHeight, false, 0x22ffff));
		screenBitmapTopLayer = new Bitmap(new BitmapData(mapWidth * tileWidth, mapHeight * tileHeight, true, 0));
	}
	
	private function progressHandler(event:ProgressEvent):void
	{
		trace("progressHandler: bytesLoaded=" + event.bytesLoaded + " bytesTotal=" + event.bytesTotal);
	}
	
	private function tilesLoadComplete(e:Event):void
	{
		var currentTileset:TileSet = e.target.loader.tileSet;
		currentTileset.bitmapData = Bitmap(e.target.content).bitmapData;
		tileSetsLoaded++;
		// wait until all the tileset images are loaded before we combine them layer by layer into one bitmap
		if (tileSetsLoaded == totalTileSets)
		{
			addTileBitmapData();
		}
	}
	
	private function addTileBitmapData():void
	{
		// load each layer
		for each (var layer:XML in xml.layer)
		{
			var tiles:Array = new Array();
			var tileLength:uint = 0;
			// assign the gid to each location in the layer
			for each (var tile:XML in layer.data.tile)
			{
				var gid:Number = tile.attribute("gid");
				// if gid > 0
				if (gid > 0)
				{
					tiles[tileLength] = gid;
				}
				tileLength++;
			}
			
			var useBitmap:BitmapData;
			
			var layerName:String = layer.attribute("name")[0];
			
			// decide where we're going to put the layer
			var layerMap:int = 0;
			switch (layerName)
			{
				case "Top": 
					layerMap = 1;
					break;
				default: 
				//trace("using base layer");
			}
			
			// store the gid into a 2d array
			var tileCoordinates:Array = new Array();
			for (var tileX:int = 0; tileX < mapWidth; tileX++)
			{
				tileCoordinates[tileX] = new Array();
				for (var tileY:int = 0; tileY < mapHeight; tileY++)
				{
					tileCoordinates[tileX][tileY] = tiles[(tileX + (tileY * mapWidth))];
				}
			}
			
			for (var spriteForX:int = 0; spriteForX < mapWidth; spriteForX++)
			{
				for (var spriteForY:int = 0; spriteForY < mapHeight; spriteForY++)
				{
					var tileGid:int = int(tileCoordinates[spriteForX][spriteForY]);
					var currentTileset:TileSet;
					// only use tiles from this tileset (we get the source image from here)
					for each (var tileset1:TileSet in tileSets)
					{
						if (tileGid >= tileset1.firstgid - 1 && tileGid <= tileset1.lastgid)
						{
							// we found the right tileset for this gid!
							currentTileset = tileset1;
							break;
						}
					}
					var destY:int = spriteForY * tileWidth;
					var destX:int = spriteForX * tileWidth;
					// basic math to find out where the tile is coming from on the source image
					tileGid -= currentTileset.firstgid - 1;
					var sourceY:int = Math.ceil(tileGid / currentTileset.tileAmountWidth) - 1;
					var sourceX:int = tileGid - (currentTileset.tileAmountWidth * sourceY) - 1;
					// copy the tile from the tileset onto our bitmap
					if (layerMap == 0)
					{
						screenBitmap.bitmapData.copyPixels(currentTileset.bitmapData, new Rectangle(sourceX * currentTileset.tileWidth, sourceY * currentTileset.tileWidth, currentTileset.tileWidth, currentTileset.tileHeight), new Point(destX, destY), null, null, true);
					}
					else if (layerMap == 1)
					{
						screenBitmapTopLayer.bitmapData.copyPixels(currentTileset.bitmapData, new Rectangle(sourceX * currentTileset.tileWidth, sourceY * currentTileset.tileWidth, currentTileset.tileWidth, currentTileset.tileHeight), new Point(destX, destY), null, null, true);
					}
				}
			}
		}
		
		/*for each (var objectgroup:XML in xml.objectgroup)
		{
			var objectGroup:String = objectgroup.attribute("name");
			switch (objectGroup)
			{
				case "Collision": 
					for each (var object:XML in objectgroup.object)
					{
						var rectangle:Shape = new Shape();
						rectangle.graphics.beginFill(0x0099CC, 1);
						rectangle.graphics.drawRect(0, 0, object.attribute("width"), object.attribute("height"));
						rectangle.graphics.endFill();
						rectangle.x = object.attribute("x");
						rectangle.y = object.attribute("y");
						collisionTiles.push(rectangle);
						FP.world.add(rectangle);
					}
					break;
				default: 
					trace("unrecognized object type:", objectgroup.attribute("name"));
			}
		}*/
		
		// load background layer
		var screenbmap:Entity = new Entity();
		screenbmap.graphic = new Image(screenBitmap);
		FP.world.add(screenbmap);
		
		// load player, enemies, powerups, etc
		
		// rectangle just to demonstrate how something would look in-between layers
		/*var playerExample:Shape = new Shape();
		playerExample.graphics.beginFill(0x0099CC, 1);
		playerExample.graphics.lineStyle(2); // outline rectangle
		playerExample.graphics.drawRect(0, 0, 100, 100);
		playerExample.graphics.endFill();
		playerExample.x = 420;
		playerExample.y = 260;
		collisionTiles.push(playerExample);
		FP.world.add(playerExample);*/
		
		// load top layer
		
		var screenbmaptoplayer:Entity = new Entity();
		screenbmaptoplayer.graphic = new Image(screenBitmapTopLayer);
		FP.world.add(screenbmaptoplayer);
	}

}

}


(billy2000) #19

Uhm and…does it works?


(Zachary Lewis) #20

I’d take ten steps back and figure out how to use the net.flashpunk.graphics.Tilemap class. You can do it all in code. Once you get that drawing, maybe you’ll get an idea of how it could work with Tiled. If not, maybe you won’t even need Tiled, or maybe you’ll start looking into how to parse XML using E4X.

As a protip, you can probably Google any of these things and get exactly what you need on the first page of results.


(John Andersson) #21

No, it doesn’t work. I get this error

The connection to file:(tilesetnamehere) has been stopped - not allowed from (swf location here) Error #2044: Unhandled IOErrorEvent:. text=Error #2035: URL Not Found.

I think it’s a safety issue, the game can’t seem to access the png file!


(John Andersson) #22

While that does sound nice, I’d much prefer it if I could just fix this last error so I can use Tiled forever and be happy :stuck_out_tongue:


(John Andersson) #23

Ok so the full error I get is actually this

*** Security Sandbox Violation ***

Connection to file:///C|/platformer/assets/../../assets/Tileset_Dungeon.png halted - not permitted from file:///C|/platformer/bin/platformer.swf

Error #2044: Unhandled IOErrorEvent:. text=Error #2035: URL Not Found.

I googled and found a possible solution, adding the game directory to a cfg file (flash player’s trusted files) but it didn’t help

EDIT: It seems like this line

loader.load(new URLRequest("../assets/" + tileSets[i].source));

is the villain here. I tried changing it into

loader.load(new URLRequest(tileSets[i].source));

but it doesn’t work