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


(Jonathan Stoler) #44

Whether you’re reading the XML file properly has almost nothing to do with whether you’re parsing it properly.

Go back and check your parsing code. You might want to create a simpler test XML (plus comment out unrelated parts of your parser) in order to help diagnose the problem.


(John Andersson) #45

I know that the problem lies in that little line of code.

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

If I remove that line, then the program works (but there is no visible map).


(John Andersson) #46

if I type

trace(tileSets[i].source)

then it traces out

../../assets/tilesets/Dungeon1.png
../../assets/tilesets/Dungeon1_front.png
../../assets/tilesets/Dungeon1_back.png

So why doesn’t loading it work?


(Jonathan Stoler) #47

I don’t know where you put this trace statement, but if it outputs valid strings, and your URLRequest is taking null as a parameter, then something must be changing between the trace() code and your URLRequest.


Side note: it’s actually okay if URLRequest takes null as a parameter (this is the default), but if you do this, you need to set the url property of your URLRequest elsewhere before using it. That’s what your error is complaining about. (The same applies for URLLoader and the request property)

However, since you’re simply passing a constructed URLRequest to your URLLoader without storing it as a variable, you won’t have a chance to set url, so you still need a non-null value here.


(John Andersson) #48

That’s the thing, the code looks like this:

trace(tileSets[i].source)

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

It’s right after it

I made this now:

var request:URLRequest = new URLRequest(tileSets[i].source); 
trace(request)

But I still get a null trace :l

EDIT: Now I tried it again without changing anything, and I get this

[object URLRequest]
[object URLRequest]
[object URLRequest]
[object URLRequest]

If I type

loader.load(request);

Then I still get the “Parameter url must be non-null”


(Jonathan Stoler) #49

You subclassed URLLoader, right?

I’m guessing the problem lies with your subclass and how it handles request.


(John Andersson) #50

Yes, the loader works with TileCodeEventLoader

public class TileCodeEventLoader extends Loader
{
	public var tileSet:TileSet;		
}

(John Andersson) #51

Any ideas on what’s wrong?


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

I’m no pro, but I think you should re-write this whole bad boy again. From start to the end, including fixes guys gave you along the way. That way, you yourself may find something that is wrong.


(Jonathan Stoler) #53

If you can replace your TileCodeEventLoader with a regular URLLoader and it works (by “works” I mean it compiles without errors, not that it necessary does what you want), then you’ll know for sure that your subclass is the source of the problem.

Also, @betkowski’s suggestion is spot-on. If you step back and clean up your code, it’ll be a lot easier to track down your problem. You can also try Rubber duck debugging.


(John Andersson) #54

The thing is that I don’t understand how the parser works. I’m too bad. So writing one myself would take ages of trial and error, and I’d probably open up more threads…

I’ve tried replacing it with a regular URLloader but everything is even weirder now,

How am I supposed to work with tileSet (the class), previously the TileCodeEventLoader had a public variable called tileset, but now I don’t know how to make it, because if I do, it suddenly asks me for all the parameters. I’m so confused. I just wish I could understand all of this


(John Andersson) #55

If anyone know of any tutorial for tiled and flashpunk, then I’d be glad to see it. I’ve tried finding one but I can’t


(Jonathan Stoler) #56

Not trying to be rude here, but…

If you don’t think you’re ready to write it yourself, why don’t you work on something that doesn’t require it? By the time you finish, your AS3 skills might be good enough that you can write it yourself. If not, you can keep working on other smaller, simpler projects until you improve enough to tackle this problem yourself.


(John Andersson) #57

Because I don’t want to have to wait until I become a master programmer to finish my current game I have already spent so much time on. The only thing standing in my way of finishing this game is loading the levels. I have made everything else (the core functions of the game) work.

Oh, and also. Why the hell do the tutorial files work but not mine? They don’t have any problems with loading the png.


(John Andersson) #58

Now I don’t even get a sandbox violation.

I just get this stupid error

load tileset at ../../assets/tilesets/Dungeon1.png
load tileset at ../../assets/tilesets/Dungeon1_front.png
load tileset at ../../assets/tilesets/Dungeon1_back.png
load tileset at ../../assets/tilesets/Dungeon1_background.png
Error #2044: Unhandled IOErrorEvent:. text=Error #2035: URL Not Found.
Error #2044: Unhandled IOErrorEvent:. text=Error #2035: URL Not Found.
Error #2044: Unhandled IOErrorEvent:. text=Error #2035: URL Not Found.
Error #2044: Unhandled IOErrorEvent:. text=Error #2035: URL Not Found.

Even though the path is correct and the files are in the trusted directories


(John Andersson) #59

You know what, I’ve had enough. Anyone who can just tell me exactly what to type gets a payment through paypal.

Here is all the code:

GameWorld:

(in the begin function)

var levelstart:Level1 = new Level1();

Level1.as:

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.graphics.Image;

public class Level1 extends Entity
{
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 static const LEVEL1:Class;

public function Level1()
{			
	type = "ground";

	loadXML();
}

private function loadXML():void
{
	xml = new XML(new LEVEL1);
	mapWidth = xml.attribute("width");
	mapHeight = xml.attribute("height");
	tileWidth = xml.attribute("tilewidth");
	tileHeight = xml.attribute("tileheight");

	var tilesetCounter:uint = 0;
	
	for each (var tileset:XML in xml.tileset)
	{
		var imageWidth:uint = xml.tileset.image.attribute("width")[tilesetCounter];
		var imageHeight:uint = xml.tileset.image.attribute("height")[tilesetCounter];
		var firstGid:uint = xml.tileset.attribute("firstgid")[tilesetCounter];
		var tilesetName:String = xml.tileset.attribute("name")[tilesetCounter];
		var tilesetTileWidth:uint = xml.tileset.attribute("tilewidth")[tilesetCounter];
		var tilesetTileHeight:uint = xml.tileset.attribute("tileheight")[tilesetCounter];
		var tilesetImagePath:String = xml.tileset.image.attribute("source")[tilesetCounter];
		tileSets.push(new TileSet(firstGid, tilesetName, tilesetTileWidth, tilesetTileHeight, tilesetImagePath, imageWidth, imageHeight));
	
		tilesetCounter++;
	}
	totalTileSets = tilesetCounter;

	// 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.tileSet = tileSets[i];
		
		var request:URLRequest = new URLRequest(tileSets[i].source); 
		loader.load(request);
		
		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 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 top layer
	var screenbmaptoplayer:Entity = new Entity();
	screenbmaptoplayer.graphic = new Image(screenBitmapTopLayer);
	FP.world.add(screenbmaptoplayer);
}

}

}

The TMX file (called level_1.tmx) I only excluded the actual data, like "< tile gid = “blabla” > and the objects layer

<map version="1.0" orientation="orthogonal" width="32" height="9" tilewidth="120" tileheight="120">
 <tileset firstgid="106" name="Dungeon1" tilewidth="120" tileheight="120">
  <image source="../../assets/tilesets/Dungeon1.png" trans="ffffff" width="3500" height="2536"/>
 </tileset>
 <tileset firstgid="715" name="Dungeon1_front" tilewidth="120" tileheight="120">
  <image source="../../assets/tilesets/Dungeon1_front.png" trans="ffffff" width="3500" height="2536"/>
 </tileset>
 <tileset firstgid="1324" name="Dungeon1_back" tilewidth="120" tileheight="120">
  <image source="../../assets/tilesets/Dungeon1_back.png" trans="ffffff" width="3500" height="2536"/>
 </tileset>
 <tileset firstgid="1933" name="Dungeon1_BG" tilewidth="120" tileheight="120">
  <image source="../../assets/tilesets/Dungeon1_background.png" trans="ffffff" width="3500" height="2536"/>
 </tileset>
 <layer name="BG" width="32" height="9">
  <data>
etc etc```


TileSet.as:

    package levels.level1
    {
	import flash.display.BitmapData;
	
	public class TileSet 
	{
		public var firstgid:uint;
		public var lastgid:uint;
		public var name:String;
		public var tileWidth:uint;
		public var source:String;
		public var tileHeight:uint;
		public var imageWidth:uint;
		public var imageHeight:uint;
		public var bitmapData:BitmapData;
		
		public var tileAmountWidth:uint;
		
		public function TileSet(firstgid:uint, name:String, tileWidth:uint, tileHeight:uint, source:String, imageWidth:uint, imageHeight:uint) 
		{
			this.firstgid = firstgid;
			this.name = name;
			this.tileWidth = tileWidth;
			this.tileHeight = tileHeight;
			this.source = source;
			this.imageWidth = imageWidth;
			this.imageHeight = imageHeight;
			tileAmountWidth = Math.floor(imageWidth / tileWidth);
			lastgid = tileAmountWidth * Math.floor(imageHeight / tileHeight) + firstgid - 1;
		}
	}

    }

And finally TileCodeEventLoader.as:

    package  levels.level1
    {
	import flash.display.Loader;
	
	public class TileCodeEventLoader extends Loader
	{
		public var tileSet:TileSet;		
	}
    }

(billy2000) #60

Pretty late on here but i made a search on google and i found this example of code. He is using tiled and flashpunk. Give it a try and take a look on it :stuck_out_tongue: http://pastebin.com/g1GVRjky


(John Andersson) #61

But what if I don’t want to use that in the World class? I want to use Gameworld as a “hub”. I have all of the stats (xp etc) there.


(David Williams) #62

Copy the functions into your Gameworld class, then call them from your world class and have it assigned to a variable that you can access? It doesn’t seem too difficult.


(Zachary Lewis) #63

@John_Andersson1 I wanted to give this problem a shot, and it is possible to load a .tmx file in as a Tilemap.

I put my source code up in the zachwlewis/tiled-flashpunk repository for your consumption.

Additionally, here’s the actual .swf I wound up with. You can pan across the map with and .

TiledFlashpunk.swf (185.0 KB)

Study this code and use it as a reference. Feel free to ask about anything you don’t understand in the source code. I tried to document it pretty well.

Finally, if you’re super eager to throw money around, here’s a pretty good place to do it.