Adding Image Object support to TMX Importer V2

Although I’ve requested this from Rex, and still waiting on a possible reply, I’ve gone ahead and tried to hack the plugin in order to learn something from it.

The main thing I want to do is get the Tile ID used by the Image Object. Tiled has the capability of using Tile images in Objects. I’ve recently realised that Tiles may not work for me in aspects where I am placing Tiles/images larger than the given map’s tile size. In isometric tiles, the the lower-left hand side is aligned with the lower-left limits of the ‘diamond’ tile. The taller Tile I use, the higher the offset will be when I try to place the Tile onto the grid. The wider the tile, the farther to the right the bottom tip of the ‘diamond’ gets from the grid tile’s anchor point.

In other words, alignment is the reason why I don’t want to use Tiles when there are tile sprites larger than the grid size.


I realised that I was trying to do this before because I saw a comment I put in the code near where I added something to get the ‘gid’ of an object. Apparently, I had forgotten that I had come to the same need before.


TMX Importer V2

First step was to find out how to get the ‘gid’, which is an xml attribute. I looked for other properties, like how Objects’ names are retrieved and follow the same procedure.

In runtime.js, I find:

instanceProto._read_obj = function (obj)
{
 if (!obj)
 return false;
 
 this.exp_ObjectName = obj.name;
 this.exp_ObjectType = obj.type;
 this.exp_ObjectPWidth = obj.width;
...
}

this.exp_ObjectName is populated by the obj.name. So I immediately just put in:

this.exp_ObjectGID = obj.gid;

Then, of course, I’ve got to be able to add the member .gid somewhere. So I look for obj by looking at who called _read_obj().

I see _retrieve_objects():

 instanceProto._retrieve_objects = function()
 {
 var obj_groups = this._tmx_obj.objectgroups;
 var i, group, group_cnt=obj_groups.length;
 var j, obj, objs, obj_cnt;
 var x,y;
 for (i=0; i<group_cnt; i++)
 {
 group = obj_groups[i];
 this.exp_ObjGroupName = group.name;
 this.exp_ObjGroupWidth = group.width;
 this.exp_ObjGroupHeight = group.height; 
 objs = group.objects;
 obj_cnt = objs.length;
 for (j=0; j<obj_cnt; j++)
 {
 this._read_obj(objs[j]);
 this.runtime.trigger(cr.plugins_.Rex_tmx_importer_v2.prototype.cnds.OnEachObject, this); 
 }
 }
 };

I’ve emphasised the key bits. Ultimately, objs[] is populated by groups which taken from the _tmx_obj. So I look for that.

instanceProto.ImportTMX = function(tmx_obj)
{ 
 this._tmx_obj = tmx_obj;
...
}

This imports the tmx and populates the _tmx_obj. It takes a tmx_obj as an arg, so I look at all calls to ImportTMX().

There are two instances. This is one of them:

instanceProto.import_tmxObj = function (source, parser)
 {
 var tmx_obj = parser.TMXObjGet(source); 
 this.ImportTMX(tmx_obj);
 
 this.tmx_source = source;
 this.parser_uid = parser.uid;
 };

I know that this is something to do with the TMX Parsers (XML/JSON), so I go over to those plugins and open up their runtime.js. The idea here, so far, is that the Parsers get XML info. So what I need, specifically, is to read that ‘gid’ attribute from the XML.

Go to XML Parser

I look for the code that gets properties from Objects (vs Tiles). I find it here:

var _get_object = function(xml_obj, xml_object)
 { 
 var object = {};
 object.name = xml_obj.get_string_value("@name", xml_object);
 object.type = xml_obj.get_string_value("@type", xml_object); 
 object.x = xml_obj.get_number_value("@x", xml_object);
 object.y = xml_obj.get_number_value("@y", xml_object); 
 object.width = xml_obj.get_number_value("@width", xml_object);
 object.height = xml_obj.get_number_value("@height", xml_object);
 var xml_properties = xml_obj.get_nodes("./properties/property", xml_object);
 object.properties = _get_properties(xml_obj, xml_properties);
 return object;
 };

From here I reckon that the syntax of the ampersand (@) gets the attribute name. So I add ‘gid’.

...
// lernie
 object.gid = xml_obj.get_number_value("@gid", xml_object);
...

Back to TMX Importer V2

Then I look over other member names to make sure I’ve init the variables like everyone else.

instanceProto.onCreate = function()
 {
...
this.exp_ObjectGID = 0;
...
}

I also see another reference (using ‘ObjectName’ as my search term)

Exps.prototype.ObjectName = function (ret)
 { 
 ret.set_string(this.exp_ObjectName);
 };

So I add that for ‘ObjectGID’

Exps.prototype.ObjectGID = function (ret)
 { 
 ret.set_int(this.exp_ObjectGID);
 };

I’m deducing that this connects the expression with the actual value of the member during runtime.

I know that there is still an edittime.js I need to attend to, so I go over there.

Go to edittime.js

I look for the same references (eg ‘ObjectName’) so I can add new stuff in.

I see:

// Expressions
...
AddExpression(55, ef_return_number, 
 "Get logical Y index of object", "Object: Object", "ObjectY", "Get logical Y index of object.");
...

So I add one in

AddExpression(59, ef_return_number, "Get GID of Object", "Object: Object", "ObjectGID", "Get GID of Object.");

I increment the first int, use ef_return_number which is likely a constant for the return value. The 4th arg is a unclear at the moment. The 5th arg “ObjectGID” is the actual expression keyword. This seems to tie in with runtime.js’s ‘expression prototypes’:

Exps.prototype.ObjectGID = function (ret)
 { 
 ret.set_int(this.exp_ObjectGID);
 };

Where ‘ObjectGID’ is like a run-time replacement (like LScript pragmas), in which the name of the expression keyword is tied with the internal member name of exp_ObjectGID.

When I test this, it works. So, now I’ve successfully retrieved the ‘gid’ of the Object being tested for.

Now the next step is to find out the TilesetName property (among other things) of the Image Object. I know that the ‘gid’ refers to the TileID. So I look at how Tile IDs are connected with a Tileset, and from there, get the Tileset’s name.

I search for the ‘TilesetName’, and the one of interest comes up:

instanceProto._read_tile_at_LXY = function(tmx_layer, x, y, is_raw_data)
{
...
var tileset_obj = this._tmx_obj.GetTileSet(this.exp_TileID);
 this.exp_TilesetName = tileset_obj.name;
 this.exp_ImageSource = tileset_obj.image.source;
 this.exp_ImageWidth = tileset_obj.image.width;
 this.exp_ImageHeight = tileset_obj.image.height;
 this.exp_TilesetProperties = tileset_obj.properties;
 var tile_obj = tileset_obj.tiles[this.exp_TileID];
 this.exp_Frame = this.exp_TileID - tileset_obj.firstgid;
 this.exp_TileProperties = (tile_obj != null)? tile_obj.properties: null;
...
}

I see the to assign exp_TilesetName you need the Tileset Object, and that’s accessed through

var tileset_obj = this._tmx_obj.GetTileSet(this.exp_TileID);

So I thought that if I simply use the ObjectGID, instead of the Tile ID as passed above, then I will be good to go. I just needed to find where to place it.

I go back to where I had assigned ObjectGID:

instanceProto._read_obj = function (obj)
{
...
this.exp_ObjectGID = obj.gid;
var tileset_obj = this._tmx_obj.GetTileSet(obj.gid);
if (tileset_obj != null)
{
this.exp_ObjectTilesetName = tileset_obj.name;
}
}

Code works. However, I originally got an error accessing the ‘name’ variable, so I had to test the validity of the tileset_obj, because it may return null, causing the error.

When this worked, I copied all the other members from the Tilesets properties and adapted them for Image Object:

if (tileset_obj != null)
 {
 // alert (tileset_obj.name);
 this.exp_ObjectTilesetName = tileset_obj.name;
 
 this.exp_ObjectImageSource = tileset_obj.image.source;
 this.exp_ObjectImageWidth = tileset_obj.image.width;
 this.exp_ObjectImageHeight = tileset_obj.image.height;
 this.exp_ObjectTilesetProperties = tileset_obj.properties;
 var tile_obj = tileset_obj.tiles[this.exp_ObjectGID];
 this.exp_ObjectFrame = this.exp_ObjectGID - tileset_obj.firstgid;
 this.exp_ObjectTileProperties = (tile_obj != null)? tile_obj.properties: null;
 }

Conclusion/Summary/Points/What I’ve Learned

  1. The TMX parser is the one that populates the TMX Object so any new data needed must be checked there to see whether it can be properly accessed. After the TMX Object is populated the runtime object (this) can get that info for its own members (eg self.exp_ObjectGID).
  2. Expressions are registered in the edittime.js. This is required because C2 won’t allow you to write any expression variable just like that. The expression is referenced back into the runtime from the Exps.prototype.ObjectGID = function(ret) {..} mentioned above. This function seems to return the self member, so it seems like the retriever of values from the runtime object into the game. I could be wrong, though. 🙂
  3. instanceProto is basically ‘this’, and it used to declare functions, which are then called through self.myFunc(). If you trace any function, it will end up as being declared by this.
  4. In the case of the Objects, they were being retrieved, and if traced back, it all started from the Action:Create tiles.

 

 

 

 

Advertisements

2 thoughts on “Adding Image Object support to TMX Importer V2”

  1. I’m running into an issue with the tiled/c2 workflow that perhaps you can help me with. I “think” your modified tweaks might help me but im not sure.

    Basically. When using a image object in tiled to build a map. Any custom properties you add to the tileset. While they show up in the object properties window, that data does NOT get written to the object section of the tmx file when its saved. However if you embed the tileset into the TMX file then save it, its still not there, however you’ll see all of the data for each tile located higher in the tmx file. But rex’s importer doesnt read that data, therefore C2 says it cant find any info.

    In the tiled forums bjorn explains that, that data is intentionally left out if the map creator doesnt change the defaults for a tile to cut down on the amount of data being saved into the tmx file. But without that data its effectively making the tileset custom properties useless. The only work around is to go and manually change the defaults of every tile which is also a huge waste of time and make custom properties useless again.

    So, does your modified version work for reading custom properties from an external tileset that’s been embedded into the tmx.?

    Or can you think of a way to be able to read that data?

    Object layer with a image tile with custom properties from an external tileset.

    Thanks!

    Like

    1. Hi Justin. After examining the code of Rex’s TMX Importer V2 and the XML Parser, I believe you’re right in saying that there is no functionality at the moment that will enable us to retrieve tileset information directly except through the construct of these tilesets being used as Tiles (not Objects).

      Now, during my test to understand the situation, I’ve modified my version so that it’s now able to retrieve an object’s tileset properties (both inherited and per-object properties), which is basically what you want. However, external tilesets are not supported because that’s out of scope for I’m doing. It’s not impossible, but I don’t have any plans to do that.

      I will send you a copy of the code. Rex suggested to me that I register the new plugin through another name, but since I haven’t actually shared the code with anyone else, I’ve failed to do this even up to now. I’ll write more about this and details of how to implement the change in C2 in the email I will send.

      best,
      lernie.

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s