ASGAlex / flame_tiled_utils

A set of useful utils to extend tiled functionality
MIT License
8 stars 3 forks source link

ImageBatchCompiler.compileMapLayer({tileMap, layerNames}) only works with layers in root #8

Open nickgirga opened 3 months ago

nickgirga commented 3 months ago

I was trying to loop through a group of TileLayers in order to create individual, scripted, PositionComponents for each TileLayer that is a child of this layer group (Group) I created to organize them. For the longest time I couldn't figure out why my layers weren't rendering. So, I Ctrl+Clicked compileMapLayer(...) in VSCode, and poked around the source code a bit.

One thing stood out to me: a function that compileMapLayer(...) makes use of (_unlistedLayers(...)), only loops through the topmost layers:

static List<Layer> _unlistedLayers(
      RenderableTiledMap tileMap, List<String> layerNames) {
    final unlisted = <Layer>[];
    for (final layer in tileMap.map.layers) { // <-- HERE! Just the top layers
      if (!layerNames.contains(layer.name)) {
        unlisted.add(layer);
      }
    }
    return unlisted;
  }

So, I moved my layers to the root layer and redirected my references to them. Worked first try. So, I'm pretty sure my assumption was accurate.

It seems that this should be achieved with a recursive function. For example, one that can accept a "rootLayer" (of type Layer) or a RenderableTiledMap. This way, we can call the function inside of itself for every (populated) Group we find until we either run out of layers to check or we reach the recursion limit we set (e.g. 99).

A pseudo example interface (and some implementation to show what I mean):

static List<Layer> _unlistedLayers(
      List<String> layerNames, {RenderableTiledMap? tileMap, Layer? rootLayer, int recursionLevel = 0, int recursionLimit = 99}) {
   // check for needed data
   if (tileMap == null && rootLayer == null) {
      // log error; missing required starting point (tileMap/rootLayer)
      return [];
   }

   final unlisted = <Layer>[];
   List<Layer> layers = (rootLayer == null) ? tileMap.map.layers : rootLayer.layers; // determine which starting point
   for (final layer in layers) {
      // ...
      // do checks
      // ...
      if (layer is Group) { // check if group
         if (layer.layers.isNotEmpty && recursionLevel <= recursionLimit) { // check empty and prevent infinite recursion
            List<Layer> childUnlistedLayers = _unlistedLayers(layerNames, rootLayer: layer, recursionLevel: recursionLevel + 1);
            unlisted.addAll(childUnlistedLayers); // add data from recursive calls to return data
         }
      }
   }
   return unlisted; // return data and everything added from recursive layers
}

Calling the function would be nearly identical. It could be entirely identical if you kept "tileMap" a nullable positional parameter (I just prefer named parameters for nullable values). But its recursive design will allow it to make use of its new "rootLayer" parameter to search even the children of theoretically up to 99 layers deep (in our case with our recursion limit). This allows you to compile layers that are children of other layers.

I haven't had the time to implement and test this fully, and I'll probably just leave my TileLayers I want to compile in the root layer of my project in the meantime, but this would be a very helpful improvement. Especially for organizing. I shouldn't have to worry about the structure of my project in Tiled.