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


(billy2000) #24

in my current project i use ogmo ,but after all its a xml file that should be exported from ogmo also …so here is how i do it …maybe u can see similarities and adapt it :smile:

        protected var map:Entity;
	protected var miniMap:Entity;
	protected var bubbles_:Bubble = new Bubble(FP.camera.x + 10, FP.camera.y + 20);
	
	protected var mapGrid:Grid;
	protected var mapImage:Graphic;
	protected var mapMiniGrid:Grid;
	protected var mapMiniImage:Graphic;
	protected var mapMiniWall:Entity;
	protected var mapData:Class;
	protected var mapWall:Entity;
	protected var mapSpikes:Entity;
	
	public function Level(mapData:Class = null) 
	{
		//backdrops
		eCloseBackdrop = new Backdrops(0, 15,"close background");
		add(eCloseBackdrop);
		eFarBackdrop = new Backdrops(0, 15,"far background");
		add(eFarBackdrop);
		
		this.mapData = mapData;
		loadMap(mapData);
		
		//normal map
		mapImage = null; //new Image(mapGrid.data);
		map = new Entity(0, 0, mapImage, mapGrid);
		map.layer = 0;
		map.mask = mapGrid;
		map.type = "solid";
		//mini map
		mapMiniImage = null; //new Image(mapMiniGrid.data);
		miniMap = new Entity(0, 0, mapMiniImage, mapMiniGrid);
		miniMap.layer = 0;
		miniMap.mask = mapMiniGrid;
		miniMap.type = "solid";
		
	}
	protected function loadMap(mapData:Class):void
	{
		var mapXML:XML = FP.getXML(mapData);
		mapWidth = mapXML.@width;
		mapHeight = mapXML.@height;
		whereTele = mapXML.@whereTele;
		
		
		//tilemap and grids
		tileMapAndGrids(mapXML);
		
	}

         private function tileMapAndGrids(mapXML:XML):void
	{
		var node:XML;
		
		//normal grid
		mapGrid = new Grid(uint(mapXML.@width), uint(mapXML.@height), 36, 36, 0, 0);
		mapGrid.loadFromString(String(mapXML.Grid), "", "\n");
		
		//normal tilesmap
		if (String(mapXML.TileSet).length > 0)
		{
			var wallW1:Tilemap = new Tilemap(E.WORLD1IMG, mapGrid.width, mapGrid.height, 36, 36);
			wallW1.loadFromString(mapXML.TileSet, ",", "\n");
			mapWall = new Entity(0, 0, wallW1, mapGrid);
			
			add(mapWall);
		}
		
		//mini grids
		mapMiniGrid = new Grid(uint(mapXML.@width), uint(mapXML.@height), 24, 18, 0, 0);
		mapMiniGrid.loadFromString(String(mapXML.black), "", "\n");
		
		//mini tilemap
		if (String(mapXML.SmlTile).length > 0)
		{
			var wallMiniW1:Tilemap = new Tilemap(E.WORLD1LILIMG, mapMiniGrid.width, mapMiniGrid.height, 24, 18);
			wallMiniW1.loadFromString(mapXML.SmlTile, ",", "\n");
			mapMiniWall = new Entity(0, 0, wallMiniW1, mapMiniGrid);
			
			add(mapMiniWall);
		}
		
		//spikes tilemap
		if (String(mapXML.spikeset).length > 0)
		{
			var spikes_:Tilemap = new Tilemap(E.SPIKESMAP, mapGrid.width, mapGrid.height, 36, 36);
			spikes_.loadFromString(mapXML.spikeset, ",", "\n");
			mapSpikes = new Entity(0, 0, spikes_, mapGrid);
			
			add(mapSpikes);
			
		}
	}

U wont see embeds cause i embed the xml file and images in a class called E so everywhere u will see E.BlahBlah that means it was embed and i call it from E :smile:


(Zachary Lewis) #25

That’s like having a car and not even knowing how to change the oil and saying, “I really need to rebuild these pistons because someone said it would fix stuff and then I can drive it.”

Flash movies made with FlashPunk should work just fine with URLLoaders, but they’re not really a part of FlashPunk. They’re a bit more advanced. That said, here’s some basic reading for working with dynamically loaded content in ActionScript 3 that will answer most of the questions you’re having.

Just a heads up, this pool only gets deeper.


(John Andersson) #26

No, what I said is like using a computer and not knowing how the computer actually works under the hood. :stuck_out_tongue:

Thanks for the links, I’ll check them out.

Now if anyone could give me a straight answer so I could move on that would be great.

PS: Billy, I have used OGMO before and it worked fine. The only thing about OGMO is that it is too slow, so I use Tiled instead. It is like OGMO but 10x better. The only thing lacking is different XML modes and the fact that it has a very weird way to use entities. So I guess it’s only 9x better

The thing is, I am so close to making it work. I just need to know why it tells me there is a safety violation!


(Zachary Lewis) #27

Bro, I assure you that the answer to your question is in the documentation I linked. There’s a big difference between asking for help doing something and asking someone to do it for you. Take some time to read through the Actionscript documentation to try to figure out what’s going wrong. You might figure it out yourself — you might even learn something you didn’t know that will help in the future! :+1:


(John Andersson) #28

Fine, I’ll go ahead and read it. Although it would be nice if someone wrote it for me, hehe.

I’ll update the thread after I’ve tried everything I could


(Ultima2876) #29

URLRequests to external files are not a good way to handle this stuff. Security Sandbox violations (caused by files on the local filesystem being inaccessible to flash content that has its sandbox set to network, which is the default) are just the tip of the iceberg here. If you do manage to fix the issues you’re having, the solution is likely to break your game once you put it online (hint: I’m pretty sure the “fix” involves switching your sandbox to local mode, which will break your game once you put it online - hardly a solution). Once you’ve fixed that, you’ll have problems distributing your game because you’ll need to provide the XML file separately to the swf for it to work… sponsors and web hosts won’t like this. Your game will spread about as fast as an anvil.

A better way would be to embed your XML file directly as you would with other assets and use that for the source of your xml = new XML(e.target.data); call.

[Embed(source='../Level_1/level_1.tmx',
        mimeType="application/octet-stream")]
    public static const Level1:Class;

//constructor
public function Level1(xml:Class)
	{			
		type = "ground";

		xml = new XML(new Level1);
	}

(John Andersson) #30

Thank you so much for this incredibly informative comment :slight_smile: I’m gonna try to implement it now, THANK YOU ultima. Now I think I understand


(John Andersson) #31

Ok. It seems like I didn’t get it after all :stuck_out_tongue:

I now have this code:

package levels.level1
{
imports here;

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(xml:Class)
{			
	type = "ground";

	xmlLoadComplete();
}

private function xmlLoadComplete():void
{
	xml = new XML(new Level1);
	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
	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);
}

}

}

and this in the game world:

var levelstart:Level1 = new Level1();

I get this error now:

Incorrect number of arguments.  Expected 1.

I don’t understand how I should fix that, since I already tell the Level1.as to load the xml file, should I do it twice? Once in the gameworld and once in Level1.as? :sweat_smile:


(billy2000) #32

Can you also type the line of code where it gets you this error? :smile:


(Jonathan Stoler) #33

Your Level1 constructor takes xml:Class as a parameter. Either include this in your World or change the way Level1() works in order to remove the parameter.


(John Andersson) #34

The line that receives the error is “var levelstart:Level1 = new Level1();”

As jonathanstoler said, how should I change how it works??


(Ultima2876) #35
public function Level1(xml:Class)
{			
	type = "ground";

	xmlLoadComplete();
}

Change this to:

public function Level1() //no argument needed
{			
	type = "ground";

	xmlLoadComplete();
}

Also, consider renaming your XML embed. Level1 is both your embedded asset name and your class name; you had this problem before and it caused some headaches :slight_smile: Maybe call your XML Level1_XML or something?

[Embed(source="level_1.tmx", mimeType="application/octet-stream")] public static const Level1_XML:Class;

(John Andersson) #36

Oh, haha. I read your embed and it said that, so I thought it was some super advanced trick where you name an embed the same thing as the AS file :stuck_out_tongue: Thank you so much man. Do you have a paypal I can donate to? As a thanks for all your help.

bromance


(John Andersson) #37

Okay so it seems that this

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

line causes an error

Error #2007: Parameter url must be non-null.

I know you said URLRequest isn’t that good, but I checked the Adobe links that Zachary posted and they seem to point towards using URLRequest anyway!? How am I supposed to load something in “loader” without URLRequest?

Loader is a “TileCodeEventLoader”

This is in that .as

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

(Jonathan Stoler) #38

Your tileSets[i].source isn’t set properly and is therefore null. URLRequest expects a String, so it doesn’t work.


(John Andersson) #39

Ahh… But this is in the XML

<image source="../../assets/tilesets/Dungeon1.png" trans="ffffff" width="3500" height="2536"/>

How come it isn’t considered a string?


(billy2000) #40
loader.load(new URLRequest(String(tileSets[i].source)));

it may not work but …try


(John Andersson) #41

It didn’t work :frowning: Damn


(Jonathan Stoler) #42

It’s definitely a string in your XML file, but when you are parsing the file, you are either not setting it at all, so it defaults null, or you’re setting it to null in the first place (if your XML parsing doesn’t read the XML file properly).

Calling String(null) will not fix the problem, because you’ll end up with "null", which isn’t a valid URL anyway.


(John Andersson) #43

If I type trace(xml) then it traces out the entire XML file, so I think it’s reading it properly

Any ideas how to make it load he string properly?