Phaser: [Tiled] add support for layer groups

Created on 6 Oct 2018  路  5Comments  路  Source: photonstorm/phaser

Problem

I've been trying to organize my layers into groups using Tiled 1.2.0

tiled_groups

However these groups are being ignored by the Tiled parser

What's happening

The parser looks for layers of type "tilelayer" and ignores the rest. My "Platform" element is of type "group". As a result, this.layers is populated only with the layer "Ground" in this example

Details

The JSON structure of the example above :

{ "height":30,
 "infinite":false,
 "layers":[
        {
         "data":[...],
         "height":30,
         "id":1,
         "name":"Ground",
         "opacity":1,
         "type":"tilelayer",
         "visible":true,
         "width":40,
         "x":0,
         "y":0
        }, 
        {
         "id":2,
         "layers":[
                {
                 "data":[...],
                 "height":30,
                 "id":5,
                 "name":"Platform",
                 "opacity":1,
                 "type":"tilelayer",
                 "visible":true,
                 "width":40,
                 "x":0,
                 "y":0
                }, 
                {
                 "data":[...],
                 "height":30,
                 "id":6,
                 "name":"Platform-grass",
                 "opacity":1,
                 "type":"tilelayer",
                 "visible":true,
                 "width":40,
                 "x":0,
                 "y":0
                }],
         "name":"Platform",
         "opacity":1,
         "type":"group",
         "visible":true,
         "x":0,
         "y":0
        }],
 "nextlayerid":7,
 "nextobjectid":1,
 "orientation":"orthogonal",
 "renderorder":"right-down",
 "tiledversion":"1.2.0",
 "tileheight":16,
 "tilesets":[],
 "tilewidth":16,
 "type":"map",
 "version":1.2,
 "width":40
}

The relevant line : l30 in /src/tilemaps/parsers/tiled/ParseTileLayers.js :

        if (json.layers[i].type !== 'tilelayer')
        {
            continue;
        }

Feature proposal

I think that the group properties have to be passed on to the nested layers in the following way :

  • child_opacity *= parent_opacity
  • child_visible = child_visible && parent_visible
  • child_x += parent_x
  • child_y += parent_y

Did I forget any property ?

The new code should probably use a recursive loop to go through the different levels of nesting (groups inside groups)

I can work on a PR if you want


Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

馃挅 Feature Request 馃檹 Help Wanted

Most helpful comment

Sometimes it's nice to have different groups that contain layers with the same name. For example you might have two groups, "overworld" and "underground", that each contain a "background" layer. I propose prepending group names to layer names. So in this example you'd end up with one LayerData with the name "overworld/background" and another with the name "underground/background". That way you can do things like tilemap.createStaticLayer( group + '/' + layer ).

All 5 comments

Sometimes it's nice to have different groups that contain layers with the same name. For example you might have two groups, "overworld" and "underground", that each contain a "background" layer. I propose prepending group names to layer names. So in this example you'd end up with one LayerData with the name "overworld/background" and another with the name "underground/background". That way you can do things like tilemap.createStaticLayer( group + '/' + layer ).

UPDATE: well there are LOTS of tilemaps in the phaser3 examples with their TMX files and those have served nicely. I don't believe any of them use groups (I didn't look exhaustively) but it was easy enough to just add some groups to an existing one.


Does anyone have some example Tiled maps that have groups? Is there just a good set of general example Tiled maps online? I'm working on this and need some test data.

I'm going to look around the Phaser examples for a few good tiled tilemaps but I'm assuming they won't have groups going on and that the original TMX file will probably not be there (just the JSON).

I have been working on this and have a reasonable implementation that accounts for groups rather than skipping them. Here are some subtleties:

Layer Hierarchy

A Tilemap game object has no sense of hierarchy for its stored layers (they are in a flat array) and changing this is out of scope. Instead, I think it is best to flatten that hierarchy and discard the groups as they are parsed.

Inherited Properties

Applying the inheritance of opacity and visibility as suggested by Babeetlebum works well and causes the layers to mirror their behavior in the Tiled editor.

  • child_opacity *= parent_opacity
  • child_visible = child_visible && parent_visible

I also had to be careful to look for offsetx and offsety (and presumably startx and starty for infinite maps) and account for these in computing the layer x and y. Note, I haven't tested with an infinite map to confirm the change in property names for offset, I'm just assuming based on how the tile layer parser already handles infinite maps.

  • child_x += parent_x + parent_offsetx + parent_startx
  • child_y += parent_y + parent_offsety + parent_starty

Renaming Layers With Group Names

Renaming layers as suggested by jbpuryear makes sense because layer names can act as unique IDs in phaser and flattening the hierarchy would create collisions. In order to help programmers that don't expect this renaming, I modified the Tilemap object to output a list of valid layer names when one is requested and not found. This way, they will see the group names included with the layer name on the console and have a prayer of figuring out what is wrong.

  • child_name = parent_name + '/' + child_name

In order to avoid breaking maps that don't use groups, I do NOT append a '/' for the implied root group.

Object and Image Layers

Tiled lets any layers exist inside of groups (object and image layers included) so I modified the parsers for all three layer types to account for this. All behave as above, inheriting the properties as suggested and re-naming to include group names. This was trickier with object layers because the X and Y values are baked into the objects rather than stored in the layer but I think I have it correct.


I duplicated the features test map and added a bunch of group layers to it to test inheriting all these properties and having all three different kinds of layers in groups. So far, all seems to work as expected and none of the previous tilemap examples have broken.

I'm going to keep testing a little bit more with the phaser3-examples as this was a pretty significant change in the end, but once I've kicked the tires and all seems okay I will make a pull request for this issue. When updating documentation, I have assumed this will be a part of 3.21.0 (just for the sake of putting something in there).

When I tried this out with an infinite map, things got complicated! Near as I can tell, infinite maps were not quite correctly implemented even for maps without group layers. There are two key properties related to position for a tile layer:

  • offsetx / offsety: an offset in the map vs other layers, in pixel units
  • startx / starty: Where layer content starts (top-left I assume) in an infinite map, in TILE units

_startx/y_ are documented in the Tiled 'latest' 1.3 beta manual but they appear in JSON output even with earlier versions of tiled (I am using 'stable' 1.2.4 and they show up). They were being accounted for already in ParseTiledLayers if the map was infinite.

_offsetx/y_ have been around for a long time and can be controlled right in the Tiled interface. I think they are not frequently used but they do need to be accounted for when importing. ParseTiledLayers does so for NON-infinite maps but it seems to ignore them for infinite maps which is NOT correct. Those values can be there regardless of weather the map is infinite or not.

Once I realized that and fixed this (and after a lot of monkeying to get things in the correct units) I got the group layer code to work with both bounded and infinite maps. At this point I think the code is ready. I will clean things up and put out a pull request soon!

On another note, I'm wondering if I should also offer a pull request for my new tests? I built them on top of the phaser3-examples repo but they really aren't 'examples' but are just tests to make sure the changes still work. I just duplicated the features-test map a couple of times and added groups for one and toggled infinite map for the other.

Thank you for submitting this feature request. We have implemented this and the feature has been pushed to the master branch. It will be part of the next release. If you get time to build and test it for yourself we would appreciate that.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

halilcakar picture halilcakar  路  4Comments

cncolder picture cncolder  路  4Comments

BigZaphod picture BigZaphod  路  4Comments

lilijreey picture lilijreey  路  4Comments

Colbydude picture Colbydude  路  4Comments