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
#22
04/15/2013 (7:46 am)
My question was, does TMX have a way to add data to the tile map information? Or would that be better done in a custom tool? Mainly for creating tiles that cause damage instead of being simply impassable, for instance. And placement of "doors" or other event triggers, and spawnpoints, etc. Of course, I suppose we could always use a set of dummy transparent images and use them to do a processing pass on the map at load to place these where the dummies tell us to (that sounds kind of funny to me, but you get the picture).
#23
04/15/2013 (7:54 am)
You can set tile and map properties. In Corona + Corona with Lime, I use JSON to fetch collision properties for RPG maps based on the tile. It didn't help with specific locations on the map (though I didn't look into map properties for that) triggering specific events, but it helped on a tile level.
#24
@Richard - Tiled supports NamedValued property pairs for several levels of maps. The map itself, layers, and individuals tiles in your pallet can all have NamedValue property defined.
My implementation supports querying for properties on tiles, and I should add in layer & map properties to that query. You just pass in a tile location and name, and it will return a value if one was defined. I'll probably expand the query interface to be more powerful.
I also support a custom data layer system in the TmxMapAsset. You define a specific Named property, and give it a custom value. TmxMapAsset will search for that value in the TmxMapAsset TAML for a serialized SceneObject that will get created/positioned/sized to the tile location. You can use this to define collision bodies/triggers/etc. Look at my blog post where I use that to define collision bodies in my sample map.
04/15/2013 (8:35 am)
@Mich - Do you think I should rewrite the TMX reader to be embeded into T2D, or keep it as a 3rd party library? I'd need to give it a once over to see if anything needs to be cleaned up for a pull request.@Richard - Tiled supports NamedValued property pairs for several levels of maps. The map itself, layers, and individuals tiles in your pallet can all have NamedValue property defined.
My implementation supports querying for properties on tiles, and I should add in layer & map properties to that query. You just pass in a tile location and name, and it will return a value if one was defined. I'll probably expand the query interface to be more powerful.
I also support a custom data layer system in the TmxMapAsset. You define a specific Named property, and give it a custom value. TmxMapAsset will search for that value in the TmxMapAsset TAML for a serialized SceneObject that will get created/positioned/sized to the tile location. You can use this to define collision bodies/triggers/etc. Look at my blog post where I use that to define collision bodies in my sample map.
#25
Does that work?
04/15/2013 (8:39 am)
@Dylan - That is a good idea. What I will do is if a tile has a Tag defined, and linked to a SceneObject and there are other properties defined for that tile as well, I will then set those other properties as dynamic fields for that SceneObject. Then your dynamic properties would get passed down. Does that work?
#26
Are Object(TiledTMX object layers) properties supported, under objectgroup->object->properties?
That is what I would need, I think, as every door might be tied to the same tile so I couldn't just tie those parameters to the single tile.
So, I would need for a way for each instance of that door to have separate properties, which I think can only be done with object layers.
Edit: However, similar to your example with how you define different size collidable areas, that would work too for defining NPCs and objects on a map. Just have a different tile for each object on the invisible tile layer. So if you did pass through other tile properties as dynamic properties, that would be adequate to make it possible.
04/15/2013 (3:36 pm)
I've been thinking about it. So, with your implementation, TMX XML wise, the property element is supported under tile->properties.Are Object(TiledTMX object layers) properties supported, under objectgroup->object->properties?
That is what I would need, I think, as every door might be tied to the same tile so I couldn't just tie those parameters to the single tile.
So, I would need for a way for each instance of that door to have separate properties, which I think can only be done with object layers.
Edit: However, similar to your example with how you define different size collidable areas, that would work too for defining NPCs and objects on a map. Just have a different tile for each object on the invisible tile layer. So if you did pass through other tile properties as dynamic properties, that would be adequate to make it possible.
#27
I will also be adding the same Tag system to object layer objects to work the same way, but if you have the same visual tile/object but different properties you'll want to use a hidden data layer to give those different tiles different properties.
04/16/2013 (10:56 am)
@Dylan, look at my blog post. You create a non-rendering tile layer as a data layer. On that layer you use a custom tile set to tie to different objects/properties. I will also be adding the same Tag system to object layer objects to work the same way, but if you have the same visual tile/object but different properties you'll want to use a hidden data layer to give those different tiles different properties.
#28
04/16/2013 (11:11 am)
@William - I'll review your code tonight or tomorrow and let you know what I think.
#29
04/16/2013 (11:12 am)
@Mich - Sure mich, I'm going to be adding some more functionality, but the basic code base isn't changing unless I implement my own TMX map reader.
#30
I've built your branch and it runs fine, but a) the toy you mentioned earlier is nowhere to be seen, and b) it's based on an earlier master, I think, and we don't have that lovely taml schema dump command in this version. I'm not familiar enough with it to fork and merge it myself, else I would.
05/03/2013 (1:30 pm)
Any progress on this? TMX support would make T2D MIT a definite yes for my summer project. :)I've built your branch and it runs fine, but a) the toy you mentioned earlier is nowhere to be seen, and b) it's based on an earlier master, I think, and we don't have that lovely taml schema dump command in this version. I'm not familiar enough with it to fork and merge it myself, else I would.
#31
05/04/2013 (12:31 pm)
Yeah, sorry. Real life creeps up sometimes :). It's high on my list, and I'll be hopefully getting it cleaned up here very soon.
#32
https://github.com/elementc/Torque2D/tree/feature/tmx_maps
05/10/2013 (9:26 am)
Not a problem! In the interest of my own experience with torque, I've isolated your TMX support code and brought it over to the current shipping version of T2D MIT. I'm not sure what the git etiquette is for that kind of thing, but I've moved it to my own fork on github for the time being. Also added VS2012 support since I don't have 2010. https://github.com/elementc/Torque2D/tree/feature/tmx_maps
#33
05/10/2013 (9:28 am)
If you want to go a step further you can submit a pull request to my branch and I can merge in your changes :).
#34
It seems to me that collision bodies, at the least, are a fundamental part of what we'd want to support at the level of the TmxMapSprite. This isn't to say the tag matching objects from taml scheme you've come up with isn't great, just that collision bodies needs special consideration.
I've changed your code to make TmxMapSprites at the object level static as opposed to dynamic objects. I've also finished your implementation of the ScriptBinding header.
I've also change things so that TMX objects without a tileset that are a) of type "collision" or b) on a layer named "collision" will be created as static collision bodies.
There are a few idiosyncrasies to consider:
1) Tiled lets you define arbitrary polygon shapes as objects. Box only lets us have convex shapes with eight (hard coded and can be upped) or fewer vertices. For now I just make note of that in an engine guide document and share it with my artists. We might be able to better handle these cases by splitting the input polygons. I'm not familiar with an algorithm to do this (well, Source engine uses BSP but I'm not sure how that would apply here), but I can probably talk to my old algorithms professor and see what he thinks.
2) Tiled lets us define arbitrary ellipses as objects. Box2D only has circles. There are ways of defining a pseudo ellipse using two circles and a ten-point polygon (see box2d.org/forum/viewtopic.php?f=3&t=8911) but we'd need to up the point count in-engine to do that and I've not yet written code to generate collision bodies as defined in the linked post. For now, I just use circles, and print a warning when the height:width ratio is too far outside a reasonable bound.
3) I don't have an iso map to test this on. Assuming your TileToCoord (and its inverse) functions are correct, it should work. Still, tested extensively with ortho maps just fine.
4) The code you put up on github does not seem to support the advanced taml features you mentioned and demonstrated in your blog post (layer definitions and options, tag a tile to spawn an arbitrary object there). Is there a commit that I missed? Because I'm sure that's going to be a fun thing to merge with my changes.
All my changes are still up on github.com/elementc/Torque2D/tree/feature/tmx_maps. Take some time to review them and let me know if you still want a pull request. My changes are relatively small, and I'd rather merge them myself after pulling from whatever's missing as described in (4) than have you be caught in hell of merging ahead and behind at the same time.
:)
PS: I have a Toy I've been using that supports basically live Taml on a single scene.taml file. I've been using that to iterate quickly on my physics generation code. It's at sdrv.ms/138xmLB for anyone who wants it.
05/11/2013 (1:31 pm)
I would, but I've kind of changed how your code worked in a fundamental way. It seems to me that collision bodies, at the least, are a fundamental part of what we'd want to support at the level of the TmxMapSprite. This isn't to say the tag matching objects from taml scheme you've come up with isn't great, just that collision bodies needs special consideration.
I've changed your code to make TmxMapSprites at the object level static as opposed to dynamic objects. I've also finished your implementation of the ScriptBinding header.
I've also change things so that TMX objects without a tileset that are a) of type "collision" or b) on a layer named "collision" will be created as static collision bodies.
There are a few idiosyncrasies to consider:
1) Tiled lets you define arbitrary polygon shapes as objects. Box only lets us have convex shapes with eight (hard coded and can be upped) or fewer vertices. For now I just make note of that in an engine guide document and share it with my artists. We might be able to better handle these cases by splitting the input polygons. I'm not familiar with an algorithm to do this (well, Source engine uses BSP but I'm not sure how that would apply here), but I can probably talk to my old algorithms professor and see what he thinks.
2) Tiled lets us define arbitrary ellipses as objects. Box2D only has circles. There are ways of defining a pseudo ellipse using two circles and a ten-point polygon (see box2d.org/forum/viewtopic.php?f=3&t=8911) but we'd need to up the point count in-engine to do that and I've not yet written code to generate collision bodies as defined in the linked post. For now, I just use circles, and print a warning when the height:width ratio is too far outside a reasonable bound.
3) I don't have an iso map to test this on. Assuming your TileToCoord (and its inverse) functions are correct, it should work. Still, tested extensively with ortho maps just fine.
4) The code you put up on github does not seem to support the advanced taml features you mentioned and demonstrated in your blog post (layer definitions and options, tag a tile to spawn an arbitrary object there). Is there a commit that I missed? Because I'm sure that's going to be a fun thing to merge with my changes.
All my changes are still up on github.com/elementc/Torque2D/tree/feature/tmx_maps. Take some time to review them and let me know if you still want a pull request. My changes are relatively small, and I'd rather merge them myself after pulling from whatever's missing as described in (4) than have you be caught in hell of merging ahead and behind at the same time.
:)
PS: I have a Toy I've been using that supports basically live Taml on a single scene.taml file. I've been using that to iterate quickly on my physics generation code. It's at sdrv.ms/138xmLB for anyone who wants it.
#35
06/02/2013 (11:20 pm)
Tried this quickly and i have a few observation/suggestion to do. First do not connect your layer composite directly to the scene graph, connect all the layer from each tmx loaded to a single root scene object that you can then use to access and transform the whole thing in one go, second revert the change making then static or (even better) provide an option to make a tmx object either dynamic or static depending on needs when building one. Cool stuff otherwise.
#36
Body type defaults to static for now since I haven't looked up what the default type is in a SceneObject, but you can certainly cahnge types on the fly.
I'm not familiar enough with Torque's architecture to know how exactly to do the rest yet. Would I have to override some methods and make sure to forward their calls to the individual compositesprites? Or is there another more accepted way to do it?
I'll toy around with it today, but more specific guidance would be appreciated.
06/05/2013 (12:42 pm)
@Jonathan I have just committed a few changes that enable you to use the following script commands:someTmxMapSprite.setMap("someAssetID");
someTmxMapSprite.setBodyType("static|dynamic|kinematic"); Body type defaults to static for now since I haven't looked up what the default type is in a SceneObject, but you can certainly cahnge types on the fly.
I'm not familiar enough with Torque's architecture to know how exactly to do the rest yet. Would I have to override some methods and make sure to forward their calls to the individual compositesprites? Or is there another more accepted way to do it?
I'll toy around with it today, but more specific guidance would be appreciated.
#37
06/18/2013 (4:57 pm)
Nice, dunno where I got the impression that Torque scenegraph supported attaching object to scenenode probably from using Ogre3D for too long, one solution would be weld jointing all the layer together (dirty hack if you ask me to involve the physic system in this, we really need a SceneNode object with the ability to sprite batch all the SceneObject attached to it or at the least transform them as a whole), but this way you don't need to update all the layer in a loop when doing a transfo, just move one and the rest follow, doesn't really help with scaling the whole thing tho. Maybe another solution could be to use a single CompostiteSprite ensure all layer and sprites are built in order and rendersort them from newest/oldest to get everything in a single batch, but this way loose flexibility on the manipulation of the sprite (picking cant be filtered as easily) and will cause tons of other problems. Another more complicated approach would be to refactor the CompositeSprite object to hold their sprite in separated LayerObject which could be added or removed with their own layer id and properties and throw all of it to the parent SpriteBatch, which is the approach i am currently experimenting with for my prototype, work well, but the code is quite dirty. I have also experimented with linking collision information directly with tile type and use a dummy Composite where all the Shape are compounded, performance degrade quickly with bigger map and i will need to make a pass to *stitch* as many collision shape possible into bigger blocks, the deep of the recursion need to be limited in that case to support acceptable runtime modification of the graph. </rant>
Employee Michael Perry
ZombieShortbus
@William - He brought up a good point we should discuss. I have two questions.
1. Would you like your work to be part of the official repository?
2. Is your work close to being ready for an integration in the official T2D repo?