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


(John Andersson) #64

I’m gonna donate some money as soon as I get home :slight_smile: Thank you so much.

I’m gonna try it out now and see if I can make it work!! Thanks!!!


(John Andersson) #66

Sweeeet! I can actually see my map! WOO! Thank you zach. Excellent :smiley: Just one thing, every displayed tile seems to be the one to the left of the original tileset?

Changing

_map.setTile(tileX, tileY, uint(tile.@gid - 1));

to

_map.setTile(tileX, tileY, uint(tile.@gid));

fixed it, but I hope that won’t create future problems

Just a little question, if I want to spawn my hero from that TMX file (and also make every tile in a specific layer collidable as “ground”), how should I do that? Can tilemaps work with that type of information? And any special precautions I should take if I have several tilesets in one map?


(John Andersson) #67

Okay, so I’m using 3 different spritesheets (I will use 5 in the future), and they are simply the same but with different brightness, for some 3D effect. I will make them parallax differently.

Anyway, I have this code for the TMX now:

<map version="1.0" orientation="orthogonal" width="32" height="9" tilewidth="120" tileheight="120">
 <tileset firstgid="1" name="Dungeon1" tilewidth="120" tileheight="120">
  <image source="Dungeon1.png" trans="ffffff" width="3600" height="3600"/>
 </tileset>
 <tileset firstgid="901" name="Dungeon1_BG1" tilewidth="120" tileheight="120">
  <image source="Dungeon1_BG1.png" trans="ffffff" width="3600" height="3600"/>
 </tileset>
 <tileset firstgid="1801" name="Dungeon1_FG1" tilewidth="120" tileheight="120">
  <image source="Dungeon1_FG1.png" trans="ffffff" width="3600" height="3600"/>
 </tileset>
 <layer name="BG1" width="32" height="9">
  <data>
     //TILE GIDS HERE
  </data>
 </layer>
 <layer name="BG2" width="32" height="9">
  <data>
    //TILE GIDS HERE
  </data>
 </layer>
 <layer name="FG1" width="32" height="9">
  <data>
   //TILE GIDS HERE
  </data>
 </layer>
</map>```

And this code in the level loader, so I can differentiate between the different layers.

    package game_handling
    {
	//IMPORTS HERE

	public class GameWorld extends World
	{
		[Embed(source="../assets/circle_gradient2.png")] public static const SPR_LIGHT_CIRCLE_GRADIENT:Class;
		
		private var stats:HeroStats = new HeroStats();
		
		private var xpbar:XpBarGraphic = new XpBarGraphic(470, 980);
		private var xpbar_xp:XpGraphic = new XpGraphic(470, 980);
		
		private var hpglobe:HpGlobe = new HpGlobe(30, 30);
		private var hpglobe_hp:HpGlobe_HP = new HpGlobe_HP(30, 30);
		
		private var staminabar:StaminaBarGraphic = new StaminaBarGraphic(250, 30);
		private var staminabar_stamina:StaminaGraphic = new StaminaGraphic(250, 30);
		
		private var gui:HUD = new HUD();
		private var sounds:Sounds = new Sounds();
		
		public var lighting:Lighting;
		public var mouseLight:Light;
		
		private var _map_FG2:Tilemap;
		private var _map_FG1:Tilemap;
		private var _map_Ground:Tilemap;
		private var _map_BG2:Tilemap;
		private var _map_BG1:Tilemap;
			
		//Level1
		[Embed(source = "../../levels/level_1.tmx", mimeType = "application/octet-stream")] private const LEVEL1:Class;

		[Embed(source="../../levels/Dungeon1_FG1.png")]private const DUNGEON_FG:Class;
		[Embed(source="../../levels/Dungeon1.png")]private const DUNGEON_GROUND:Class;
		[Embed(source="../../levels/Dungeon1_BG1.png")]private const DUNGEON_BG:Class;

		public function GameWorld()
		{
		
		}
		
		override public function begin():void
		{
			super.begin();
			
			//var level:Level1 = Level1(add(new Level1(LEVEL_1)));
			//var levelstart:Level1 = new Level1();
			
			//Hero stats
			add(stats);
			
			//Hp and xp graphics
			add(hpglobe);
			add(hpglobe_hp);
			
			add(xpbar);
			add(xpbar_xp);
			
			add(staminabar);
			add(staminabar_stamina);
			//HUD
			add(gui);
			
			add(sounds);
		
			//Lighting
				// create new lighting
				add(lighting = new Lighting(FP.screen.width, FP.screen.height));
				
				// add lights
				/*for (var i:uint = 0; i < 50; i++)
				{
					var image:Image = new Image(FP.choose(SPR_LIGHT_SQUARE, SPR_LIGHT_CIRCLE, SPR_LIGHT_CIRCLE_GRADIENT));
					image.centerOO();
					lighting.add(new Light(FP.rand(FP.screen.width), FP.rand(FP.screen.height), image, Math.random() * 1.5 + 0.5, Math.random(), Math.random() * 100 * FP.elapsed));
				}*/
				
			//add light that follows mouse
			var image:Image = new Image(SPR_LIGHT_CIRCLE_GRADIENT);
			image.centerOO();
			mouseLight = new Light(0, 0, image, 4);
			lighting.add(mouseLight);
			
			loadLevel1();
		}
	
		override public function update():void 
		{
			super.update();
			
			mouseLight.x = mouseX;
			mouseLight.y = mouseY;
		}
		
		public function loadLevel1():void
		{
			var mapXML:XML = FP.getXML(LEVEL1);
			var mapWidth:uint = uint(mapXML.layer.@width);
			var mapHeight:uint = uint(mapXML.layer.@height);
			var tileX:uint = 0;
			var tileY:uint = 0;

			// Create a tilemap to show the level.
			// Tile size is hardcoded, but could be pulled from the XML.
			
			_map_FG2 = new Tilemap(DUNGEON_FG, mapWidth * 120, mapHeight * 120, 120, 120);
			
			_map_FG1 = new Tilemap(DUNGEON_FG, mapWidth * 120, mapHeight * 120, 120, 120);
			
			_map_Ground = new Tilemap(DUNGEON_GROUND, mapWidth * 120, mapHeight * 120, 120, 120);
			
			_map_BG2 = new Tilemap(DUNGEON_BG, mapWidth * 120, mapHeight * 120, 120, 120);
			
			_map_BG1 = new Tilemap(DUNGEON_BG, mapWidth * 120, mapHeight * 120, 120, 120);

			trace(mapXML.layer)
			// Iterate through tiles, adding them to the tilemap.
			for each (var tile:XML in mapXML.layer.data.tile)
			{
				// Once the end of the map is reached, loop back to the start.
				if (tileX >= mapWidth)
				{
					tileX = 0;
					tileY++;
				}

				// Ignore empty tiles.
				if (tile.@gid != 0)
				{
					_map_Ground.setTile(tileX, tileY, uint(tile.@gid));
				}

				// Move to the next tile.
				tileX++;
			}

			// Add the map to the world.
			addGraphic(_map);
		}
		
		//End
	}
    }


But I get this weird error:

    Error: Java heap space


I googled and found a possible reason; that there is too little allocated memory. But I have changed that in the config file for the flash player.

(Ultima2876) #68

Have you tried restarting your computer? :slight_smile:


(Zachary Lewis) #69

If your hero is a tile, you can check for his specific gid and spawn him there when you find it.

Tilemap is a graphic. It doesn’t have any collision data. I’d suggest you create an Entity with a Tilemap as its graphic and a Grid as its mask. That will link the graphic and collision in “the standard FlashPunk way.”

With multiple tile sets, I’d just make sure I’m properly tracking my gids, since Tiled uses offset values to handle multiple spritesheets. Other than that, just look through each <layer>, creating a separate Tilemap for each layer, then parallax away.

For your heap space problem, there are already a few answers to that problem on these very forums. Use the search tool at the top of the page to see if your question is answered on here before asking again and you won’t have to wait for answers.


(John Andersson) #70

It seems that the error is related to the tilemap (actually the image used for the tileset).

If I remove

addGraphic(_map_Ground);

then I get no java heap space error.

However, I still get an “invalid bitmapdata” error. It’s probably because my tileset image is 3600 x 3600. Can as3 really not handle bigger images? If it was 5150000 x 515000 then I understand, but 3600 x 3600 is nothing… Sometimes it feels like actionscript is a god damn joke

And since I’m going all rage here, why do almost all programming languages almost always have such cryptic error messages? Java heap space error? What does that even mean? Why not something like “Too big image, stupid”?

And don’t get me started on C


(John Andersson) #71

Oh by the way, since there is a limit on the image size, I have made several smaller tilesets of the original one. They all have the same brick but in a different color. So every layer in the map will use some parts of each tileset here and there.

How do I make a tilemap work with multiple tilesets? The documentation says to provide one tileset… Because it seems stupid to have several tilemaps with one tileset each instead of having one bigass tilemap with all of the tilesets. This brings me to a problem, what if the level doesn’t use a specific tileset? How do I make it only load the necessary ones? Is all of this memory efficient?

And how would I add all of this combined data in the collision entity?

I’m a bit confused on how to solve the logical problems


(John Andersson) #72

Okay, so I’ve tried working with only one tilemap (which means only one tileset and layer) to make it work before I move on to more tilesets.

This is the code for the GameWorld so far:

package game_handling
{
imports here

public class GameWorld extends World
{
	[Embed(source="../assets/circle_gradient2.png")] public static const SPR_LIGHT_CIRCLE_GRADIENT:Class;
	
	random vars here

	**public var collisionData:Grid;**
		
	//Level1
	[Embed(source = "../../levels/level_1.tmx", mimeType = "application/octet-stream")] private const LEVEL1:Class;

	[Embed(source="../../levels/Bricks1_DarkGray.png")]private const BRICKS1_DARKGRAY:Class;
	[Embed(source="../../levels/Bricks1_Gold.png")]private const BRICKS1_GOLD:Class;
	[Embed(source="../../levels/Bricks1_Ice.png")]private const BRICKS1_ICE:Class;
	[Embed(source="../../levels/Bricks1_Purple.png")]private const BRICKS1_PURPLE:Class;
	[Embed(source="../../levels/Bricks1_White.png")]private const BRICKS1_WHITE:Class;

	public function GameWorld()
	{
	
	}
	
	override public function begin():void
	{
		super.begin();
		
		//var level:Level1 = Level1(add(new Level1(LEVEL_1)));
		//var levelstart:Level1 = new Level1();
		
		//Hero stats
		add(stats);
		
		//Hp and xp graphics
		add(hpglobe);
		add(hpglobe_hp);
		
		add(xpbar);
		add(xpbar_xp);
		
		add(staminabar);
		add(staminabar_stamina);
		//HUD
		add(gui);
		
		add(sounds);
	
		//Lighting
			// create new lighting
			add(lighting = new Lighting(FP.screen.width, FP.screen.height));
			
			// add lights
			/*for (var i:uint = 0; i < 50; i++)
			{
				var image:Image = new Image(FP.choose(SPR_LIGHT_SQUARE, SPR_LIGHT_CIRCLE, SPR_LIGHT_CIRCLE_GRADIENT));
				image.centerOO();
				lighting.add(new Light(FP.rand(FP.screen.width), FP.rand(FP.screen.height), image, Math.random() * 1.5 + 0.5, Math.random(), Math.random() * 100 * FP.elapsed));
			}*/
			
		//add light that follows mouse
		var image:Image = new Image(SPR_LIGHT_CIRCLE_GRADIENT);
		image.centerOO();
		mouseLight = new Light(0, 0, image, 4);
		lighting.add(mouseLight);
		
		loadLevel1();
	}

	override public function update():void 
	{
		super.update();
		
		mouseLight.x = mouseX;
		mouseLight.y = mouseY;
	}
	
	public function loadLevel1():void
	{
		var mapXML:XML = FP.getXML(LEVEL1);
		var mapWidth:uint = uint(mapXML.layer.@width);
		var mapHeight:uint = uint(mapXML.layer.@height);
		var tileX:uint = 0;
		var tileY:uint = 0;
		collisionData = new Grid(mapWidth * 120, mapHeight * 120, 120, 120);

		// Create a tilemap to show the level.
		// Tile size is hardcoded, but could be pulled from the XML.
		_map_Ground = new Tilemap(BRICKS1_DARKGRAY, mapWidth * 120, mapHeight * 120, 120, 120);

		// Iterate through tiles, adding them to the tilemap.
		for each (var tile:XML in mapXML.layer.data.tile)
		{
			// Once the end of the map is reached, loop back to the start.
			if (tileX >= mapWidth)
			{
				tileX = 0;
				tileY++;
			}

			// Ignore empty tiles.
			if (tile.@gid != 0)
			{
				_map_Ground.setTile(tileX, tileY, uint(tile.@gid - 1));
			}
			
			var groundTiles:String = mapXML.layer.attribute("name");
			switch (groundTiles)
			{
				case "Ground":
					collisionData.setTile(tileX, tileY, true);
					
					break;
				default:
					trace("NOTHING")
			}
			
			// Move to the next tile.
			tileX++;
		}
		
		for each (var objectgroup:XML in mapXML.objectgroup) 
		{
			var objectGroup:String = objectgroup.attribute("name");
			switch(objectGroup) 
			{
				
			case "Spawn":
				for each (var object:XML in objectgroup.object) 
				{
					var objectVar:String = objectgroup.object.attribute("name");
					switch(objectVar)
					{
					case "Hero":
						FP.world.add(new Hero(new Point(int(object.@x), int(object.@y))));
						break;
						
					default:
						trace("no spawns")
					}
				}
				break;
				
			default:
				trace("unrecognized object type:", objectgroup.attribute("name"));
			}
		}

		// Add the map to the world.
		addGraphic(_map_Ground);
		
		updateCollision();
	}
	
	public function updateCollision():void
	{
		var collision_:Collision = FP.world.getInstance("collision");
		if (collision_ == null)
		{
			trace("no collision")
			FP.world.add(new Collision(0, 0, _map_Ground, collisionData))
			
		}else {
		
			trace("updating collision")
		}
	}
	
	//End
}
}

As you can see, I tried making a grid which is the same thing as the tiles from the “ground” layer (from the XML).

I also have another class that handles the collision, Collision.as:

package game_handling 
{
import net.flashpunk.Entity;
import net.flashpunk.Graphic;
import net.flashpunk.Mask;

public class Collision extends Entity 
{
	public function Collision(x:Number=0, y:Number=0, graphic:Graphic=null, mask:Mask=null) 
	{
		name = "Collision"
		type = "ground";
		super(x, y, graphic, mask);
	}
	
	override public function update():void
	{
		trace("collision is active")
	}
}
}

I’m guessing it is all working except for one thing, the collision. The hero is simply stuck in the air, as if the grid isn’t aligned to the stage. How can I make the grid visible?

Btw, the FPS is really low now… I’m gonna try and see what is causing this


(Ultima2876) #73

The maximum size of a BitmapData in Flash is 2048x2048 pixels. Why do you need a tilemap that is so huge? o_O Most other languages do not support extremely large bitmaps natively either; usually because of hardware constraints (a lot of low-end graphics cards have limits on texture size such as 1024x1024 or 2048x2048 pixels).

The java heap space error is not a compiler error. It’s a java error and it means that java ran out of memory while trying to compile; this is often a problem with your environment - try increasing java’s heap allocation size in the settings. It sometimes does this when you are working with a huge amount of assets - another fix if you have the Flash IDE is to embed your assets in an swf and access them that way (import into Flash, add to library and check ‘export for actionscript’, give the resource and name then export -> flash movie). These assets will be precompiled and shouldn’t take up memory at compile time, thus avoiding the java heap space error.

However, the heap space problem shouldn’t occur unless you’re using hundreds of megabytes of assets… at which point a bigger concern is the final compiled size of your game and distributivity. Ideally your game should be less than 5mb when complete; you never really want it to be more than 20mb.


(John Andersson) #74

Well, the tiles are 120x120…


(Ultima2876) #75

A Canvas might be a better option than a Tilemap for tiles so large. Then each tile can be its own separate Image (BitmapData).


(John Andersson) #76

Any examples of the canvas so I know how to incorporate it?


(Ultima2876) #77

It’s pretty much a regular Graphic type, except you can draw to it like a BitmapData and it supports very large sizes. So load up your tiles as Images or BitmapData, then use the Canvas’s drawGraphic or copyPixels method to draw to it!