first cut: TMX map support
by William Hilke · in Torque 2D Beginner · 02/22/2013 (11:53 pm) · 37 replies
I've started working on adding support for T2D to load/render tile maps based on the TMX format:
www.mapeditor.org/
So far I've introduced a new asset type for tmx map files, which allows tmx maps to flow into the standard asset pipeline.
I've also added a new SceneObject for rendering a tile map, based on a tmx map asset It uses a CompositeSprite for each tile and object layer, with some basic properties set. I'll probably expose most of that so you can have more control over sorting, etc.
ISO layers are working right now, and I'll be adding RECT layers next. After that I'm planning to add in some other helpful features like allowing script access to custom properties for a given tile location.
Feel free to review the repo and let me know if there is any feature that should be added. Once I'm done I'll put in a pull request to let GG decide if they want it or not.
github.com/whilke/Torque2D/tree/feature/tmx_maps
www.mapeditor.org/
So far I've introduced a new asset type for tmx map files, which allows tmx maps to flow into the standard asset pipeline.
I've also added a new SceneObject for rendering a tile map, based on a tmx map asset It uses a CompositeSprite for each tile and object layer, with some basic properties set. I'll probably expose most of that so you can have more control over sorting, etc.
ISO layers are working right now, and I'll be adding RECT layers next. After that I'm planning to add in some other helpful features like allowing script access to custom properties for a given tile location.
Feel free to review the repo and let me know if there is any feature that should be added. Once I'm done I'll put in a pull request to let GG decide if they want it or not.
github.com/whilke/Torque2D/tree/feature/tmx_maps
#2
1.) It only supports embedded tile sets right now. Need to add support for external tile sets.
2.) Create an ImageAsset that has the same name as the art asset for the tile set, or add a property to your tile set with the name "AssetName" and the value of the full assetId you want to use.
3.) All layers will go to scene layer 0 unless you add a property to the layer with the name "LayerId", and a value of the T2D you want it to use.
It also uses an open source tmx parser, which is included as another project in the VS2010 solution. Don't know if I should scrap that, and just write one embedded to the Torque2D project or not.
Let me know if you run into any issues. I've only been testing it with my small test cases so far.
02/23/2013 (7:40 am)
Michael, couple of things to remember when building a map:1.) It only supports embedded tile sets right now. Need to add support for external tile sets.
2.) Create an ImageAsset that has the same name as the art asset for the tile set, or add a property to your tile set with the name "AssetName" and the value of the full assetId you want to use.
3.) All layers will go to scene layer 0 unless you add a property to the layer with the name "LayerId", and a value of the T2D you want it to use.
It also uses an open source tmx parser, which is included as another project in the VS2010 solution. Don't know if I should scrap that, and just write one embedded to the Torque2D project or not.
Let me know if you run into any issues. I've only been testing it with my small test cases so far.
#3
02/23/2013 (7:45 am)
@William - I haven't cloned yet, so I don't know the folder structure. Did you create a toy to show it off?
#4
It's simple to add in a map with:
with test_map defined with this taml:
02/23/2013 (7:48 am)
Not yet. Wanted to get rectangle layout support added before I put in a toy that shows everything off.It's simple to add in a map with:
%mapSprite = new TmxMapSprite()
{
Map = "ToyAssets:test_map";
};
SandboxScene.add( %mapSprite );with test_map defined with this taml:
<TmxMapAsset
AssetName="test_map"
MapFile="map.tmx"
/>
#5
02/23/2013 (7:50 am)
For your own sake and others who want to test, I highly recommend you whip up a toy real quick. Add some custom controls, like the CompositeSprite toy. It will dramatically speed up your testing.
#6
02/23/2013 (8:12 am)
There will be a full toy added shortly :). I just wanted to put it out so anyone could get a first peek at it.
#7
02/23/2013 (8:14 am)
Right on. Let me us know as soon as its ready.
#8
I've also added a simple toy that renders a four layer ortho tmx map.
I'm thinking of rethinking the TmxAsset, and adding support there for defining tmx layer->T2D layer, and tmx tileset image->ImageAssetId. Instead of defining it with custom properties in the map itself.
You should be able to clone the repo, and switch over to the TmxMapToy (custom type).
02/23/2013 (11:31 am)
I've added support for orth maps, but only tile layers. Will add in object layers later.I've also added a simple toy that renders a four layer ortho tmx map.
I'm thinking of rethinking the TmxAsset, and adding support there for defining tmx layer->T2D layer, and tmx tileset image->ImageAssetId. Instead of defining it with custom properties in the map itself.
You should be able to clone the repo, and switch over to the TmxMapToy (custom type).
#10
You can now define the render layer for each TMX map layer in the asset.
I'm going to add support for defining properties on specific tileset tiles (collisions, sort point, depth, blending, etc) next.
That should give you all the power you need to setup any type of map, and integrate into any of your render layers. Moving objects can then be placed within the scene on the correct layer, with the right sorting params and it should render properly.
02/24/2013 (8:07 pm)
repo has been updated with my new approach to the TmxMapAsset<TmxMapAsset
AssetName="testtown_map"
MapFile="testtown.tmx">
<TmxMapAsset.Layers>
<Layer Name="Floor" Layer="15"></Layer>
<Layer Name="Fringe" Layer="14"></Layer>
<Layer Name="Over" Layer="13"></Layer>
<Layer Name="Hip roof" Layer="12"></Layer>
</TmxMapAsset.Layers>
</TmxMapAsset>You can now define the render layer for each TMX map layer in the asset.
I'm going to add support for defining properties on specific tileset tiles (collisions, sort point, depth, blending, etc) next.
That should give you all the power you need to setup any type of map, and integrate into any of your render layers. Moving objects can then be placed within the scene on the correct layer, with the right sorting params and it should render properly.
#12
One thing I wanted to give you a heads-up on is custom serialization which is extremely weak currently in T2D.
I have spent a good chunk of the weekend sorting this out. The change is to allow an unlimited tree of nodes to be generated for custom serialization as well as supporting any of those nodes being SimObjects.
This is far better than the fixed set-up we currently have. I wanted to get this work in before release but several things conspired against me.
This changes how you form custom serialization but doesn't break existing XML set-up. It does bump-up the binary version Id however.
The reason I'm mentioning this here is because you seem to be using that given your examples above so this will affect you but I think the change is necessary and will serve everyone well for the future.
Being able to have an unlimited tree of nodes with each node having its own fields essentially allows you to form your own schema inside your custom "xxxx.Layers" and any of those nodes can be a SimObject and T2D will build that for you as well as restore it.
It's a WIP, I'm working on it here in its dev branch.
02/25/2013 (2:48 am)
Good work William.One thing I wanted to give you a heads-up on is custom serialization which is extremely weak currently in T2D.
I have spent a good chunk of the weekend sorting this out. The change is to allow an unlimited tree of nodes to be generated for custom serialization as well as supporting any of those nodes being SimObjects.
This is far better than the fixed set-up we currently have. I wanted to get this work in before release but several things conspired against me.
This changes how you form custom serialization but doesn't break existing XML set-up. It does bump-up the binary version Id however.
The reason I'm mentioning this here is because you seem to be using that given your examples above so this will affect you but I think the change is necessary and will serve everyone well for the future.
Being able to have an unlimited tree of nodes with each node having its own fields essentially allows you to form your own schema inside your custom "xxxx.Layers" and any of those nodes can be a SimObject and T2D will build that for you as well as restore it.
It's a WIP, I'm working on it here in its dev branch.
#13
02/25/2013 (7:36 am)
Sounds good Melv. I'll pull it in once it looks complete and switch over to it. I'm about to add a lot more to the serialization data set.
#14
The major update here is the support for attached SceneObjects on tiles. With this, you can now define collision bodies, or any other TAML seriaizable SceneObject that you'd like.
In Tiled you can add a property to any specific tile on any tileset. If you create a property named "Tag", and give it a value that you define in the asset, then it will create any SceneObjects defined for that tag whenever the tile is rendered (Assuming that layer has useObjects=true).
Even if the layer is set to not render, if useObjects is set it will still create the objects at that tile location. This is important, as it allows you to define collision only layers in Tiled that don't actually render.

A TMX map asset is then defined like so:
As you can see, there is a tagged tile called "TileCollision". That defines one SceneObject with a simple square collision body(fills up the whole tile). Anytime a tile with that tag is added to the tile layer, it will also create a SceneObject at the same location based on that TAML information.
You can get as basic or complex here as you want. If you're trying to squeeze as much performance as you can, you'd probably want to use a hidden collision layer that defines as few sceneobjects as needed to setup your collision areas. Remember that a collision shape can be larger then the object (tile size), so you can lay down a collision tile in the middle of a large world object (that covers several tiles), and define a collision body(s) that overlaps whatever you want.
In the repo it uses the simple approach of defining a SceneObject for each non-walkable tile with a simple square collision body to cover the entire tile. This leads to over 600 static sceneobjects getting created, so it's really not the best approach to take (still renders at 500fps).

02/26/2013 (7:36 pm)
Few updates:- Repo has been updated to use the new TAML serialization.
- Layer overrides can now specify a render/useObjects flag.
- If render is off, then a CompositeSprite is not created for that tile layer, and no tile sprites will get created.
- If useObjects is off then any attached ScreneObjets will not be created anytime a tile is rendered in that layer (if any are defined).
- The TMX asset now allows you to define a tile tag, and for that tag a list of SceneObjects that will be created wherever that tile is used.
The major update here is the support for attached SceneObjects on tiles. With this, you can now define collision bodies, or any other TAML seriaizable SceneObject that you'd like.
In Tiled you can add a property to any specific tile on any tileset. If you create a property named "Tag", and give it a value that you define in the asset, then it will create any SceneObjects defined for that tag whenever the tile is rendered (Assuming that layer has useObjects=true).
Even if the layer is set to not render, if useObjects is set it will still create the objects at that tile location. This is important, as it allows you to define collision only layers in Tiled that don't actually render.

A TMX map asset is then defined like so:
<TmxMapAsset
AssetName="testtown_map"
MapFile="testtown.tmx"
>
<TmxMapAsset.Layers>
<Layer Name="Floor" Layer="15" Render="true" useObjects="true" ></Layer>
<Layer Name="Fringe" Layer="14" Render="true" useObjects="true" ></Layer>
<Layer Name="Over" Layer="13" Render="true" useObjects="true"></Layer>
<Layer Name="Hip roof" Layer="12" Render="true" useObjects="true"></Layer>
</TmxMapAsset.Layers>
<TmxMapAsset.Tiles>
<Tile Tag="TileCollision" >
<SceneObject TamlId="1" BodyType="Static">
<SceneObject.CollisionShapes>
<Polygon>
<Point>-0.5 -0.5</Point>
<Point>0.5 -0.5</Point>
<Point>-0.5 0.5</Point>
<Point>0.5 0.5</Point>
</Polygon>
</SceneObject.CollisionShapes>
</SceneObject>
</Tile>
</TmxMapAsset.Tiles>
</TmxMapAsset>As you can see, there is a tagged tile called "TileCollision". That defines one SceneObject with a simple square collision body(fills up the whole tile). Anytime a tile with that tag is added to the tile layer, it will also create a SceneObject at the same location based on that TAML information.
You can get as basic or complex here as you want. If you're trying to squeeze as much performance as you can, you'd probably want to use a hidden collision layer that defines as few sceneobjects as needed to setup your collision areas. Remember that a collision shape can be larger then the object (tile size), so you can lay down a collision tile in the middle of a large world object (that covers several tiles), and define a collision body(s) that overlaps whatever you want.
In the repo it uses the simple approach of defining a SceneObject for each non-walkable tile with a simple square collision body to cover the entire tile. This leads to over 600 static sceneobjects getting created, so it's really not the best approach to take (still renders at 500fps).

#15
Everything in there was done in the Tiled editor, except for the bouncing knight. I tossed him into the scene on the correct layer, and threw him against one of the collision walls to get him moving.
The collisions are created by adding a new TMX layer, and placing special tiles to mark where collision bodies should be drawn. I then define those bodies in the map asset like this:
02/28/2013 (1:49 pm)
Here's a video of how I'm planning to use TMX support: Everything in there was done in the Tiled editor, except for the bouncing knight. I tossed him into the scene on the correct layer, and threw him against one of the collision walls to get him moving.
The collisions are created by adding a new TMX layer, and placing special tiles to mark where collision bodies should be drawn. I then define those bodies in the map asset like this:
<TmxMapAsset
AssetName="testmap"
MapFile="pd_tilemap.tmx"
>
<TmxMapAsset.Layers>
<Layer Name="Wall" Layer="14" Render="true" useObjects="true" ></Layer>
<Layer Name="Floor" Layer="15" Render="true" useObjects="true" ></Layer>
<Layer Name="Collisions" Layer="13" Render="false" useObjects="true"></Layer>
</TmxMapAsset.Layers>
<TmxMapAsset.Tiles>
<Tile Tag="3x3Collision" >
<SceneObject TamlId="1" BodyType="Static">
<SceneObject.CollisionShapes>
<Polygon>
<Point>-3.5 -0.5</Point>
<Point>3.5 -0.5</Point>
<Point>-3.5 0.5</Point>
<Point>3.5 0.5</Point>
</Polygon>
</SceneObject.CollisionShapes>
</SceneObject>
</Tile>
<Tile Tag="1x1Collision" >
<SceneObject TamlId="1" BodyType="Static">
<SceneObject.CollisionShapes>
<Polygon>
<Point>-1.5 -0.5</Point>
<Point>1.5 -0.5</Point>
<Point>-1.5 0.5</Point>
<Point>1.5 0.5</Point>
</Polygon>
</SceneObject.CollisionShapes>
</SceneObject>
</Tile>
<Tile Tag="1x1VertCollision" >
<SceneObject TamlId="1" BodyType="Static">
<SceneObject.CollisionShapes>
<Polygon>
<Point>-0.5 -1.5</Point>
<Point>0.5 -1.5</Point>
<Point>-0.5 1.5</Point>
<Point>0.5 1.5</Point>
</Polygon>
</SceneObject.CollisionShapes>
</SceneObject>
</Tile>
</TmxMapAsset.Tiles>
</TmxMapAsset>
#16
02/28/2013 (1:55 pm)
@William - Outstanding work!
#17
www.garagegames.com/community/blogs/view/22199
02/28/2013 (3:24 pm)
For anyone interested, I put up a blog post that walks through the creation of the above map for support in T2D.www.garagegames.com/community/blogs/view/22199
#18
03/02/2013 (12:32 pm)
@William - Looking at the blog now!
#19
Now you can add properties/flags to your tilesets, and then query for them at runtime.
I've also exposed a WorldPointToTileCoord function so that you can find a tile from a world coordinate.
Both of those together can be used to take a world object (script/player/object/enemy), find out what tile they are on or moving towards, and query for any properties on those tiles.
Maybe you want to flag all of your water tiles so that anything moving through them will have a reduced velocity, etc.
I use tile properties so that I can embed some hints into my maps to let the client know that a map change might be coming up and it can start pre-loading any needed resources to transition smoothly.
03/10/2013 (8:16 pm)
I've updated my feature repo with some warning cleanups and a script interface to be able to access tile properties by (layer, x,y, propname).Now you can add properties/flags to your tilesets, and then query for them at runtime.
I've also exposed a WorldPointToTileCoord function so that you can find a tile from a world coordinate.
Both of those together can be used to take a world object (script/player/object/enemy), find out what tile they are on or moving towards, and query for any properties on those tiles.
Maybe you want to flag all of your water tiles so that anything moving through them will have a reduced velocity, etc.
I use tile properties so that I can embed some hints into my maps to let the client know that a map change might be coming up and it can start pre-loading any needed resources to transition smoothly.
#20
I have a question for you -
I'm working on a sidescroller.
Say, I wanted to add NPCs to a map.
For example, say I want to build a spike area. Judging by what you have here, I could set a property on a spike tile called "Tag" and a value of my choice. In the TAML I can define what SceneObect I want to create at that location.
Is there any way to pass dynamic properties from the Tiled map editor, if I add other properties to a tile, to the object that will get created via TAML? Hopefully that makes sense.
For example, I had doors in my game. I had an object on the map, and the class was like GameDoor, and in the old torque editor I'd set properties for where the door linked. I'd set destx,desty, required_gameflag(locked doors), mapname. There was only one door class. That's how I did the doors in this.
[/center]
In this, if I wanted to do that, would I need a separate tag value for each door, ie door1, door2, door3, etc and define the properties in the TAML file?
I started writing my own TMX loader, except I was doing it in straight TorqueScript. I'm maybe half way done.
http://www.garagegames.com/community/forums/viewthread/133794
I might just switch and use your work. Hopefully it gets accepted upstream into MIT Torque2d.
Hopefully this makes sense. Thanks!
04/14/2013 (9:55 pm)
Hey, William, this looks pretty awesome.I have a question for you -
I'm working on a sidescroller.
Say, I wanted to add NPCs to a map.
For example, say I want to build a spike area. Judging by what you have here, I could set a property on a spike tile called "Tag" and a value of my choice. In the TAML I can define what SceneObect I want to create at that location.
Is there any way to pass dynamic properties from the Tiled map editor, if I add other properties to a tile, to the object that will get created via TAML? Hopefully that makes sense.
For example, I had doors in my game. I had an object on the map, and the class was like GameDoor, and in the old torque editor I'd set properties for where the door linked. I'd set destx,desty, required_gameflag(locked doors), mapname. There was only one door class. That's how I did the doors in this.
[/center]
In this, if I wanted to do that, would I need a separate tag value for each door, ie door1, door2, door3, etc and define the properties in the TAML file?
I started writing my own TMX loader, except I was doing it in straight TorqueScript. I'm maybe half way done.
http://www.garagegames.com/community/forums/viewthread/133794
I might just switch and use your work. Hopefully it gets accepted upstream into MIT Torque2d.
Hopefully this makes sense. Thanks!
Employee Michael Perry
ZombieShortbus