Any way to batch add layer properties across many tmx files?

Hey! I’ve got a set of maps that I’m trying to use in a unity port of a project. There are probably 25 or so maps, and each map has over 20 layers. I now need to add custom layer properties to each of those layers on all of the maps…

Has anyone used any kind of batch processing solution for back-modifying maps? I’m using SuperTiled2Unity for importing, so I’d like the end result to be a .tmx file – otherwise I’d just batch process the json output.

You could do this as a Tiled script. The Tiled API makes it easy to iterate open maps (or maps in a Project), iterate their layers, and add properties to them (whether all the same ones, or with dynamically determined values). Then, you can Save All to save your changes.

1 Like

Interesting - I’ll look into it! Thanks for the quick response.

One thing to keep in mind is that TileMap.layers only contains the top-level layers. If you use Groups, you will need to iterate those yourself.

This example script iterates all the top-level layers in all the currently open maps and adds an “example” boolean property set to whether the layer is a tile layer:

let addExampleProperty = tiled.registerAction("AddExampleProperty", function(action) {
   for(asset of tiled.openAssets) {
      if(!asset.isTileMap) continue;
      for(layer of asset.layers) {
         layer.setProperty("example", layer.isTileLayer);
      }
   }
});
addExampleProperty.text = "Add Example Property";

But if you want to correctly work with group layers, you’d have to modify it. I usually use recursive functions, e.g:

let addExampleProperty = tiled.registerAction("AddExampleProperty", function(action) {
   function addProperty(layerOrMap) {
      if(layerOrMap.isGroupLayer || layerOrMap.isTileMap) {
         for(layer of layerOrMap.layers) {
            addProperty(layer);
         }
      }
      if(!layerOrMap.isTileMap) {
         layerOrMap.setProperty("example", layerOrMap.isTileLayer);
      }
   }

   for(asset of tiled.openAssets) {
      if(asset.isTileMap)
         addProperty(asset);
   }
});
addExampleProperty.text = "Add Example Property";
1 Like

Thanks! This is exactly what I need, and I haven’t grouped my layers so it should be fairly simple. One question - can you think of any reason why asset.layers would return undefined here? I’ve just got one file open to test, and I’ve confirmed that it’s being recognized and is a tilemap through console output.

I didn’t test these scripts and it’s possible I made a typo or used the wrong property name, but I think it should be alright…
Is it consistently undefined, or just occasionally? In some versions of Tiled, there are some garbage collection issues, causing things to be null or undefined when they shouldn’t be, at random.

In my own scripts, I don’t usually use for(layer of asset.layers), but instead use a regular for loop that avoids storing layers locally where they can be garbage collected more easily, maybe it’ll work more consistently?

for(let i = 0; i < layerOrMap.layerCount; ++i) {
   //do work on layerOrMap.layerAt(i);
}

It seems like it was a Tiled version issue, I updated to the latest release and seems to have resolved. Thanks for your help!

1 Like