Support for Tiled Groups and Infinite Map Fixes

- Added support for Tiled group layers (issue #4099)
- Fixed some layer offset bugs for infinite maps
This commit is contained in:
Seth Berrier 2019-10-23 12:35:25 -05:00
parent c25331cf30
commit a9e897370a
4 changed files with 221 additions and 38 deletions

View file

@ -0,0 +1,55 @@
/**
* @author Seth Berrier <berriers@uwstout.edu>
* @copyright 2019 Photon Storm Ltd.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
var GetFastValue = require('../../../utils/object/GetFastValue');
/**
* Parse a Tiled group layer and create a state object for inheriting.
*
* @function Phaser.Tilemaps.Parsers.Tiled.CreateGroupLayer
* @since 3.21.0
*
* @param {object} json - The Tiled JSON object.
* @param {object} [currentl] - The current group layer from the Tiled JSON file.
* @param {object} [parentstate] - The state of the parent group (if any).
*
* @return {object} A group state object with proper values for updating children layers.
*/
var CreateGroupLayer = function (json, groupl, parentstate)
{
if (!groupl)
{
// Return a default group state object
return {
i: 0, // Current layer array iterator
layers: json.layers, // Current array of layers
// Values inherited from parent group
name: '',
opacity: 1,
visible: true,
x: 0,
y: 0
};
}
// Compute group layer x, y
var layerX = groupl.x + GetFastValue(groupl, 'startx', 0) * json.tilewidth + GetFastValue(groupl, 'offsetx', 0);
var layerY = groupl.y + GetFastValue(groupl, 'starty', 0) * json.tileheight + GetFastValue(groupl, 'offsety', 0);
// Compute next state inherited from group
return {
i: 0,
layers: groupl.layers,
name: parentstate.name + groupl.name + '/',
opacity: parentstate.opacity * groupl.opacity,
visible: parentstate.visible && groupl.visible,
x: parentstate.x + layerX,
y: parentstate.y + layerY
};
};
module.exports = CreateGroupLayer;

View file

@ -5,37 +5,73 @@
*/
var GetFastValue = require('../../../utils/object/GetFastValue');
var CreateGroupLayer = require('./CreateGroupLayer');
/**
* [description]
* Parses a Tiled JSON object into an array of objects with details about the image layers.
*
* @function Phaser.Tilemaps.Parsers.Tiled.ParseImageLayers
* @since 3.0.0
*
* @param {object} json - [description]
* @param {object} json - The Tiled JSON object.
*
* @return {array} [description]
* @return {array} Array of objects that include critical info about the map's image layers
*/
var ParseImageLayers = function (json)
{
var images = [];
for (var i = 0; i < json.layers.length; i++)
// State inherited from a parent group
var groupStack = [];
var curGroupState = CreateGroupLayer(json);
while (curGroupState.i < curGroupState.layers.length || groupStack.length > 0)
{
if (json.layers[i].type !== 'imagelayer')
if (curGroupState.i >= curGroupState.layers.length)
{
// Ensure recursion stack is not empty first
if (groupStack.length < 1)
{
console.warn(
'TilemapParser.parseTiledJSON - Invalid layer group hierarchy'
);
break;
}
// Return to previous recursive state
curGroupState = groupStack.pop();
continue;
}
var curi = json.layers[i];
// Get current layer and advance iterator
var curi = curGroupState.layers[curGroupState.i];
curGroupState.i++;
if (curi.type !== 'imagelayer')
{
if (curi.type === 'group')
{
// Compute next state inherited from group
var nextGroupState = CreateGroupLayer(json, curi, curGroupState);
// Preserve current state before recursing
groupStack.push(curGroupState);
curGroupState = nextGroupState;
}
// Skip this layer OR 'recurse' (iterative style) into the group
continue;
}
var layerOffsetX = GetFastValue(curi, 'offsetx', 0) + GetFastValue(curi, 'startx', 0);
var layerOffsetY = GetFastValue(curi, 'offsety', 0) + GetFastValue(curi, 'starty', 0);
images.push({
name: curi.name,
name: (curGroupState.name + curi.name),
image: curi.image,
x: GetFastValue(curi, 'offsetx', 0) + curi.x,
y: GetFastValue(curi, 'offsety', 0) + curi.y,
alpha: curi.opacity,
visible: curi.visible,
x: (curGroupState.x + layerOffsetX + curi.x),
y: (curGroupState.y + layerOffsetY + curi.y),
alpha: (curGroupState.opacity * curi.opacity),
visible: (curGroupState.visible && curi.visible),
properties: GetFastValue(curi, 'properties', {})
});
}

View file

@ -7,6 +7,7 @@
var GetFastValue = require('../../../utils/object/GetFastValue');
var ParseObject = require('./ParseObject');
var ObjectLayer = require('../../mapdata/ObjectLayer');
var CreateGroupLayer = require('./CreateGroupLayer');
/**
* Parses a Tiled JSON object into an array of ObjectLayer objects.
@ -22,18 +23,57 @@ var ParseObjectLayers = function (json)
{
var objectLayers = [];
for (var i = 0; i < json.layers.length; i++)
// State inherited from a parent group
var groupStack = [];
var curGroupState = CreateGroupLayer(json);
while (curGroupState.i < curGroupState.layers.length || groupStack.length > 0)
{
if (json.layers[i].type !== 'objectgroup')
if (curGroupState.i >= curGroupState.layers.length)
{
// Ensure recursion stack is not empty first
if (groupStack.length < 1)
{
console.warn(
'TilemapParser.parseTiledJSON - Invalid layer group hierarchy'
);
break;
}
// Return to previous recursive state
curGroupState = groupStack.pop();
continue;
}
var curo = json.layers[i];
var offsetX = GetFastValue(curo, 'offsetx', 0);
var offsetY = GetFastValue(curo, 'offsety', 0);
var objects = [];
// Get current layer and advance iterator
var curo = curGroupState.layers[curGroupState.i];
curGroupState.i++;
// Modify inherited properties
curo.opacity *= curGroupState.opacity;
curo.visible = curGroupState.visible && curo.visible;
if (curo.type !== 'objectgroup')
{
if (curo.type === 'group')
{
// Compute next state inherited from group
var nextGroupState = CreateGroupLayer(json, curo, curGroupState);
// Preserve current state before recursing
groupStack.push(curGroupState);
curGroupState = nextGroupState;
}
// Skip this layer OR 'recurse' (iterative style) into the group
continue;
}
curo.name = curGroupState.name + curo.name;
var offsetX = curGroupState.x + GetFastValue(curo, 'startx', 0) + GetFastValue(curo, 'offsetx', 0);
var offsetY = curGroupState.y + GetFastValue(curo, 'starty', 0) + GetFastValue(curo, 'offsety', 0);
var objects = [];
for (var j = 0; j < curo.objects.length; j++)
{
var parsedObject = ParseObject(curo.objects[j], offsetX, offsetY);

View file

@ -9,31 +9,66 @@ var GetFastValue = require('../../../utils/object/GetFastValue');
var LayerData = require('../../mapdata/LayerData');
var ParseGID = require('./ParseGID');
var Tile = require('../../Tile');
var CreateGroupLayer = require('./CreateGroupLayer');
/**
* [description]
* Parses all tilemap layers in a Tiled JSON object into new LayerData objects.
*
* @function Phaser.Tilemaps.Parsers.Tiled.ParseTileLayers
* @since 3.0.0
*
* @param {object} json - [description]
* @param {boolean} insertNull - [description]
* @param {object} json - The Tiled JSON object.
* @param {boolean} insertNull - Controls how empty tiles, tiles with an index of -1, in the map
* data are handled (see {@link Phaser.Tilemaps.Parsers.Tiled.ParseJSONTiled}).
*
* @return {array} [description]
* @return {Phaser.Tilemaps.LayerData[]} - An array of LayerData objects, one for each entry in
* json.layers with the type 'tilelayer'.
*/
var ParseTileLayers = function (json, insertNull)
{
var infiniteMap = GetFastValue(json, 'infinite', false);
var tileLayers = [];
for (var i = 0; i < json.layers.length; i++)
// State inherited from a parent group
var groupStack = [];
var curGroupState = CreateGroupLayer(json);
while (curGroupState.i < curGroupState.layers.length || groupStack.length > 0)
{
if (json.layers[i].type !== 'tilelayer')
if (curGroupState.i >= curGroupState.layers.length)
{
// Ensure recursion stack is not empty first
if (groupStack.length < 1)
{
console.warn(
'TilemapParser.parseTiledJSON - Invalid layer group hierarchy'
);
break;
}
// Return to previous recursive state
curGroupState = groupStack.pop();
continue;
}
var curl = json.layers[i];
var curl = curGroupState.layers[curGroupState.i];
curGroupState.i++;
if (curl.type !== 'tilelayer')
{
if (curl.type === 'group')
{
// Compute next state inherited from group
var nextGroupState = CreateGroupLayer(json, curl, curGroupState);
// Preserve current state before recursing
groupStack.push(curGroupState);
curGroupState = nextGroupState;
}
// Skip this layer OR 'recurse' (iterative style) into the group
continue;
}
// Base64 decode data if necessary. NOTE: uncompressed base64 only.
if (curl.compression)
@ -46,7 +81,21 @@ var ParseTileLayers = function (json, insertNull)
}
else if (curl.encoding && curl.encoding === 'base64')
{
curl.data = Base64Decode(curl.data);
// Chunks for an infinite map
if (curl.chunks)
{
for (var i = 0; i < curl.chunks.length; i++)
{
curl.chunks[i].data = Base64Decode(curl.chunks[i].data);
}
}
// Non-infinite map data
if (curl.data)
{
curl.data = Base64Decode(curl.data);
}
delete curl.encoding; // Allow the same map to be parsed multiple times
}
@ -66,18 +115,18 @@ var ParseTileLayers = function (json, insertNull)
if (infiniteMap)
{
var layerOffsetX = GetFastValue(curl, 'startx', 0) + curl.x;
var layerOffsetY = GetFastValue(curl, 'starty', 0) + curl.y;
var layerOffsetX = (GetFastValue(curl, 'startx', 0) + curl.x);
var layerOffsetY = (GetFastValue(curl, 'starty', 0) + curl.y);
layerData = new LayerData({
name: curl.name,
x: layerOffsetX,
y: layerOffsetY,
name: (curGroupState.name + curl.name),
x: (curGroupState.x + GetFastValue(curl, 'offsetx', 0) + layerOffsetX * json.tilewidth),
y: (curGroupState.y + GetFastValue(curl, 'offsety', 0) + layerOffsetY * json.tileheight),
width: curl.width,
height: curl.height,
tileWidth: json.tilewidth,
tileHeight: json.tileheight,
alpha: curl.opacity,
visible: curl.visible,
alpha: (curGroupState.opacity * curl.opacity),
visible: (curGroupState.visible && curl.visible),
properties: GetFastValue(curl, 'properties', {})
});
@ -142,15 +191,15 @@ var ParseTileLayers = function (json, insertNull)
else
{
layerData = new LayerData({
name: curl.name,
x: GetFastValue(curl, 'offsetx', 0) + curl.x,
y: GetFastValue(curl, 'offsety', 0) + curl.y,
name: (curGroupState.name + curl.name),
x: (curGroupState.x + GetFastValue(curl, 'offsetx', 0) + curl.x),
y: (curGroupState.y + GetFastValue(curl, 'offsety', 0) + curl.y),
width: curl.width,
height: curl.height,
tileWidth: json.tilewidth,
tileHeight: json.tileheight,
alpha: curl.opacity,
visible: curl.visible,
alpha: (curGroupState.opacity * curl.opacity),
visible: (curGroupState.visible && curl.visible),
properties: GetFastValue(curl, 'properties', {})
});
@ -194,10 +243,13 @@ var ParseTileLayers = function (json, insertNull)
}
layerData.data = output;
tileLayers.push(layerData);
}
tileLayers.forEach(function (curLayer)
{
console.warn(`${curLayer.name} - (${curLayer.x}, ${curLayer.y}) [${curLayer.alpha} / ${curLayer.visible}]`);
});
return tileLayers;
};