phaser
Version:
A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers from the team at Phaser Studio Inc.
1,231 lines (1,086 loc) • 113 kB
JavaScript
/**
* @author Richard Davey <rich@phaser.io>
* @copyright 2013-2025 Phaser Studio Inc.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
var BuildTilesetIndex = require('./parsers/tiled/BuildTilesetIndex');
var Class = require('../utils/Class');
var DegToRad = require('../math/DegToRad');
var Formats = require('./Formats');
var GetFastValue = require('../utils/object/GetFastValue');
var LayerData = require('./mapdata/LayerData');
var ObjectHelper = require('./ObjectHelper');
var ORIENTATION = require('./const/ORIENTATION_CONST');
var Rotate = require('../math/Rotate');
var SpliceOne = require('../utils/array/SpliceOne');
var Sprite = require('../gameobjects/sprite/Sprite');
var Tile = require('./Tile');
var TilemapComponents = require('./components');
var TilemapLayer = require('./TilemapLayer');
var Tileset = require('./Tileset');
/**
* A predicate, to test each element of the array.
*
* @callback TilemapFilterCallback
*
* @param {Phaser.GameObjects.GameObject} value - An object found in the filtered area.
* @param {number} index - The index of the object within the array.
* @param {Phaser.GameObjects.GameObject[]} array - An array of all the objects found.
*
* @return {boolean} A value that coerces to `true` to keep the element, or to `false` otherwise.
*/
/**
* @callback TilemapFindCallback
*
* @param {Phaser.GameObjects.GameObject} value - An object found.
* @param {number} index - The index of the object within the array.
* @param {Phaser.GameObjects.GameObject[]} array - An array of all the objects found.
*
* @return {boolean} `true` if the callback should be invoked, otherwise `false`.
*/
/**
* @classdesc
* A Tilemap is a container for Tilemap data. This isn't a display object, rather, it holds data
* about the map and allows you to add tilesets and tilemap layers to it. A map can have one or
* more tilemap layers, which are the display objects that actually render the tiles.
*
* The Tilemap data can be parsed from a Tiled JSON file, a CSV file or a 2D array. Tiled is a free
* software package specifically for creating tile maps, and is available from:
* http://www.mapeditor.org
*
* As of Phaser 3.50.0 the Tilemap API now supports the following types of map:
*
* 1) Orthogonal
* 2) Isometric
* 3) Hexagonal
* 4) Staggered
*
* Prior to this release, only orthogonal maps were supported.
*
* Another large change in 3.50 was the consolidation of Tilemap Layers. Previously, you created
* either a Static or Dynamic Tilemap Layer. However, as of 3.50 the features of both have been
* merged and the API simplified, so now there is just the single `TilemapLayer` class.
*
* A Tilemap has handy methods for getting and manipulating the tiles within a layer, allowing
* you to build or modify the tilemap data at runtime.
*
* Note that all Tilemaps use a base tile size to calculate dimensions from, but that a
* TilemapLayer may have its own unique tile size that overrides this.
*
* As of Phaser 3.21.0, if your tilemap includes layer groups (a feature of Tiled 1.2.0+) these
* will be traversed and the following properties will impact children:
*
* - Opacity (blended with parent) and visibility (parent overrides child)
* - Vertical and horizontal offset
*
* The grouping hierarchy is not preserved and all layers will be flattened into a single array.
*
* Group layers are parsed during Tilemap construction but are discarded after parsing so dynamic
* layers will NOT continue to be affected by a parent.
*
* To avoid duplicate layer names, a layer that is a child of a group layer will have its parent
* group name prepended with a '/'. For example, consider a group called 'ParentGroup' with a
* child called 'Layer 1'. In the Tilemap object, 'Layer 1' will have the name
* 'ParentGroup/Layer 1'.
*
* The Phaser Tiled Parser does **not** support the 'Collection of Images' feature for a Tileset.
* You must ensure all of your tiles are contained in a single tileset image file (per layer)
* and have this 'embedded' in the exported Tiled JSON map data.
*
* @class Tilemap
* @memberof Phaser.Tilemaps
* @constructor
* @since 3.0.0
*
* @param {Phaser.Scene} scene - The Scene to which this Tilemap belongs.
* @param {Phaser.Tilemaps.MapData} mapData - A MapData instance containing Tilemap data.
*/
var Tilemap = new Class({
initialize:
function Tilemap (scene, mapData)
{
/**
* @name Phaser.Tilemaps.Tilemap#scene
* @type {Phaser.Scene}
* @since 3.0.0
*/
this.scene = scene;
/**
* The base width of a tile in pixels. Note that individual layers may have a different tile
* width.
*
* @name Phaser.Tilemaps.Tilemap#tileWidth
* @type {number}
* @since 3.0.0
*/
this.tileWidth = mapData.tileWidth;
/**
* The base height of a tile in pixels. Note that individual layers may have a different
* tile height.
*
* @name Phaser.Tilemaps.Tilemap#tileHeight
* @type {number}
* @since 3.0.0
*/
this.tileHeight = mapData.tileHeight;
/**
* The width of the map (in tiles).
*
* @name Phaser.Tilemaps.Tilemap#width
* @type {number}
* @since 3.0.0
*/
this.width = mapData.width;
/**
* The height of the map (in tiles).
*
* @name Phaser.Tilemaps.Tilemap#height
* @type {number}
* @since 3.0.0
*/
this.height = mapData.height;
/**
* The orientation of the map data (as specified in Tiled), usually 'orthogonal'.
*
* @name Phaser.Tilemaps.Tilemap#orientation
* @type {string}
* @since 3.0.0
*/
this.orientation = mapData.orientation;
/**
* The render (draw) order of the map data (as specified in Tiled), usually 'right-down'.
*
* The draw orders are:
*
* right-down
* left-down
* right-up
* left-up
*
* This can be changed via the `setRenderOrder` method.
*
* @name Phaser.Tilemaps.Tilemap#renderOrder
* @type {string}
* @since 3.12.0
*/
this.renderOrder = mapData.renderOrder;
/**
* The format of the map data.
*
* @name Phaser.Tilemaps.Tilemap#format
* @type {number}
* @since 3.0.0
*/
this.format = mapData.format;
/**
* The version of the map data (as specified in Tiled, usually 1).
*
* @name Phaser.Tilemaps.Tilemap#version
* @type {number}
* @since 3.0.0
*/
this.version = mapData.version;
/**
* Map specific properties as specified in Tiled.
*
* Depending on the version of Tiled and the JSON export used, this will be either
* an object or an array of objects. For Tiled 1.2.0+ maps, it will be an array.
*
* @name Phaser.Tilemaps.Tilemap#properties
* @type {object|object[]}
* @since 3.0.0
*/
this.properties = mapData.properties;
/**
* The width of the map in pixels based on width * tileWidth.
*
* @name Phaser.Tilemaps.Tilemap#widthInPixels
* @type {number}
* @since 3.0.0
*/
this.widthInPixels = mapData.widthInPixels;
/**
* The height of the map in pixels based on height * tileHeight.
*
* @name Phaser.Tilemaps.Tilemap#heightInPixels
* @type {number}
* @since 3.0.0
*/
this.heightInPixels = mapData.heightInPixels;
/**
* A collection of Images, as parsed from Tiled map data.
*
* @name Phaser.Tilemaps.Tilemap#imageCollections
* @type {Phaser.Tilemaps.ImageCollection[]}
* @since 3.0.0
*/
this.imageCollections = mapData.imageCollections;
/**
* An array of Tiled Image Layers.
*
* @name Phaser.Tilemaps.Tilemap#images
* @type {array}
* @since 3.0.0
*/
this.images = mapData.images;
/**
* An array of Tilemap layer data.
*
* @name Phaser.Tilemaps.Tilemap#layers
* @type {Phaser.Tilemaps.LayerData[]}
* @since 3.0.0
*/
this.layers = mapData.layers;
/**
* Master list of tiles -> x, y, index in tileset.
*
* @name Phaser.Tilemaps.Tilemap#tiles
* @type {array}
* @since 3.60.0
* @see Phaser.Tilemaps.Parsers.Tiled.BuildTilesetIndex
*/
this.tiles = mapData.tiles;
/**
* An array of Tilesets used in the map.
*
* @name Phaser.Tilemaps.Tilemap#tilesets
* @type {Phaser.Tilemaps.Tileset[]}
* @since 3.0.0
*/
this.tilesets = mapData.tilesets;
/**
* An array of ObjectLayer instances parsed from Tiled object layers.
*
* @name Phaser.Tilemaps.Tilemap#objects
* @type {Phaser.Tilemaps.ObjectLayer[]}
* @since 3.0.0
*/
this.objects = mapData.objects;
/**
* The index of the currently selected LayerData object.
*
* @name Phaser.Tilemaps.Tilemap#currentLayerIndex
* @type {number}
* @since 3.0.0
*/
this.currentLayerIndex = 0;
/**
* The length of the horizontal sides of the hexagon.
* Only used for hexagonal orientation Tilemaps.
*
* @name Phaser.Tilemaps.Tilemap#hexSideLength
* @type {number}
* @since 3.50.0
*/
this.hexSideLength = mapData.hexSideLength;
var orientation = this.orientation;
/**
* Functions used to handle world to tile, and tile to world, conversion.
* Cached here for internal use by public methods such as `worldToTileXY`, etc.
*
* @name Phaser.Tilemaps.Tilemap#_convert
* @private
* @type {object}
* @since 3.50.0
*/
this._convert = {
WorldToTileXY: TilemapComponents.GetWorldToTileXYFunction(orientation),
WorldToTileX: TilemapComponents.GetWorldToTileXFunction(orientation),
WorldToTileY: TilemapComponents.GetWorldToTileYFunction(orientation),
TileToWorldXY: TilemapComponents.GetTileToWorldXYFunction(orientation),
TileToWorldX: TilemapComponents.GetTileToWorldXFunction(orientation),
TileToWorldY: TilemapComponents.GetTileToWorldYFunction(orientation),
GetTileCorners: TilemapComponents.GetTileCornersFunction(orientation)
};
},
/**
* Sets the rendering (draw) order of the tiles in this map.
*
* The default is 'right-down', meaning it will order the tiles starting from the top-left,
* drawing to the right and then moving down to the next row.
*
* The draw orders are:
*
* 0 = right-down
* 1 = left-down
* 2 = right-up
* 3 = left-up
*
* Setting the render order does not change the tiles or how they are stored in the layer,
* it purely impacts the order in which they are rendered.
*
* You can provide either an integer (0 to 3), or the string version of the order.
*
* Calling this method _after_ creating Tilemap Layers will **not** automatically
* update them to use the new render order. If you call this method after creating layers, use their
* own `setRenderOrder` methods to change them as needed.
*
* @method Phaser.Tilemaps.Tilemap#setRenderOrder
* @since 3.12.0
*
* @param {(number|string)} renderOrder - The render (draw) order value. Either an integer between 0 and 3, or a string: 'right-down', 'left-down', 'right-up' or 'left-up'.
*
* @return {this} This Tilemap object.
*/
setRenderOrder: function (renderOrder)
{
var orders = [ 'right-down', 'left-down', 'right-up', 'left-up' ];
if (typeof renderOrder === 'number')
{
renderOrder = orders[ renderOrder ];
}
if (orders.indexOf(renderOrder) > -1)
{
this.renderOrder = renderOrder;
}
return this;
},
/**
* Adds an image to the map to be used as a tileset. A single map may use multiple tilesets.
* Note that the tileset name can be found in the JSON file exported from Tiled, or in the Tiled
* editor.
*
* @method Phaser.Tilemaps.Tilemap#addTilesetImage
* @since 3.0.0
*
* @param {string} tilesetName - The name of the tileset as specified in the map data.
* @param {string} [key] - The key of the Phaser.Cache image used for this tileset. If
* `undefined` or `null` it will look for an image with a key matching the tilesetName parameter.
* @param {number} [tileWidth] - The width of the tile (in pixels) in the Tileset Image. If not
* given it will default to the map's tileWidth value, or the tileWidth specified in the Tiled
* JSON file.
* @param {number} [tileHeight] - The height of the tiles (in pixels) in the Tileset Image. If
* not given it will default to the map's tileHeight value, or the tileHeight specified in the
* Tiled JSON file.
* @param {number} [tileMargin] - The margin around the tiles in the sheet (in pixels). If not
* specified, it will default to 0 or the value specified in the Tiled JSON file.
* @param {number} [tileSpacing] - The spacing between each the tile in the sheet (in pixels).
* If not specified, it will default to 0 or the value specified in the Tiled JSON file.
* @param {number} [gid=0] - If adding multiple tilesets to a blank map, specify the starting
* GID this set will use here.
* @param {object} [tileOffset={x: 0, y: 0}] - Tile texture drawing offset.
* If not specified, it will default to {0, 0}
*
* @return {?Phaser.Tilemaps.Tileset} Returns the Tileset object that was created or updated, or null if it
* failed.
*/
addTilesetImage: function (tilesetName, key, tileWidth, tileHeight, tileMargin, tileSpacing, gid, tileOffset)
{
if (tilesetName === undefined) { return null; }
if (key === undefined || key === null) { key = tilesetName; }
var textureManager = this.scene.sys.textures;
if (!textureManager.exists(key))
{
console.warn('Texture key "%s" not found', key);
return null;
}
var texture = textureManager.get(key);
var index = this.getTilesetIndex(tilesetName);
if (index === null && this.format === Formats.TILED_JSON)
{
console.warn('Tilemap has no tileset "%s". Its tilesets are %o', tilesetName, this.tilesets);
return null;
}
var tileset = this.tilesets[ index ];
if (tileset)
{
if (tileWidth || tileHeight)
{
tileset.setTileSize(tileWidth, tileHeight);
}
if (tileMargin || tileSpacing)
{
tileset.setSpacing(tileMargin, tileSpacing);
}
tileset.setImage(texture);
return tileset;
}
if (tileWidth === undefined) { tileWidth = this.tileWidth; }
if (tileHeight === undefined) { tileHeight = this.tileHeight; }
if (tileMargin === undefined) { tileMargin = 0; }
if (tileSpacing === undefined) { tileSpacing = 0; }
if (gid === undefined) { gid = 0; }
if (tileOffset === undefined) { tileOffset = { x: 0, y: 0 }; }
tileset = new Tileset(tilesetName, gid, tileWidth, tileHeight, tileMargin, tileSpacing, undefined, undefined, tileOffset);
tileset.setImage(texture);
this.tilesets.push(tileset);
this.tiles = BuildTilesetIndex(this);
return tileset;
},
/**
* Copies the tiles in the source rectangular area to a new destination (all specified in tile
* coordinates) within the layer. This copies all tile properties & recalculates collision
* information in the destination region.
*
* If no layer specified, the map's current layer is used. This cannot be applied to StaticTilemapLayers.
*
* @method Phaser.Tilemaps.Tilemap#copy
* @since 3.0.0
*
* @param {number} srcTileX - The x coordinate of the area to copy from, in tiles, not pixels.
* @param {number} srcTileY - The y coordinate of the area to copy from, in tiles, not pixels.
* @param {number} width - The width of the area to copy, in tiles, not pixels.
* @param {number} height - The height of the area to copy, in tiles, not pixels.
* @param {number} destTileX - The x coordinate of the area to copy to, in tiles, not pixels.
* @param {number} destTileY - The y coordinate of the area to copy to, in tiles, not pixels.
* @param {boolean} [recalculateFaces=true] - `true` if the faces data should be recalculated.
* @param {(string|number|Phaser.Tilemaps.TilemapLayer)} [layer] - The tile layer to use. If not given the current layer is used.
*
* @return {?Phaser.Tilemaps.Tilemap} Returns this, or null if the layer given was invalid.
*/
copy: function (srcTileX, srcTileY, width, height, destTileX, destTileY, recalculateFaces, layer)
{
layer = this.getLayer(layer);
if (layer !== null)
{
TilemapComponents.Copy(
srcTileX, srcTileY,
width, height,
destTileX, destTileY,
recalculateFaces, layer
);
return this;
}
else
{
return null;
}
},
/**
* Creates a new and empty Tilemap Layer. The currently selected layer in the map is set to this new layer.
*
* Prior to v3.50.0 this method was called `createBlankDynamicLayer`.
*
* @method Phaser.Tilemaps.Tilemap#createBlankLayer
* @since 3.0.0
*
* @param {string} name - The name of this layer. Must be unique within the map.
* @param {(string|string[]|Phaser.Tilemaps.Tileset|Phaser.Tilemaps.Tileset[])} tileset - The tileset, or an array of tilesets, used to render this layer. Can be a string or a Tileset object.
* @param {number} [x=0] - The world x position where the top left of this layer will be placed.
* @param {number} [y=0] - The world y position where the top left of this layer will be placed.
* @param {number} [width] - The width of the layer in tiles. If not specified, it will default to the map's width.
* @param {number} [height] - The height of the layer in tiles. If not specified, it will default to the map's height.
* @param {number} [tileWidth] - The width of the tiles the layer uses for calculations. If not specified, it will default to the map's tileWidth.
* @param {number} [tileHeight] - The height of the tiles the layer uses for calculations. If not specified, it will default to the map's tileHeight.
*
* @return {?Phaser.Tilemaps.TilemapLayer} Returns the new layer that was created, or `null` if it failed.
*/
createBlankLayer: function (name, tileset, x, y, width, height, tileWidth, tileHeight)
{
if (x === undefined) { x = 0; }
if (y === undefined) { y = 0; }
if (width === undefined) { width = this.width; }
if (height === undefined) { height = this.height; }
if (tileWidth === undefined) { tileWidth = this.tileWidth; }
if (tileHeight === undefined) { tileHeight = this.tileHeight; }
var index = this.getLayerIndex(name);
if (index !== null)
{
console.warn('Invalid Tilemap Layer ID: ' + name);
return null;
}
var layerData = new LayerData({
name: name,
tileWidth: tileWidth,
tileHeight: tileHeight,
width: width,
height: height,
orientation: this.orientation,
hexSideLength: this.hexSideLength
});
var row;
for (var tileY = 0; tileY < height; tileY++)
{
row = [];
for (var tileX = 0; tileX < width; tileX++)
{
row.push(new Tile(layerData, -1, tileX, tileY, tileWidth, tileHeight, this.tileWidth, this.tileHeight));
}
layerData.data.push(row);
}
this.layers.push(layerData);
this.currentLayerIndex = this.layers.length - 1;
var layer = new TilemapLayer(this.scene, this, this.currentLayerIndex, tileset, x, y);
layer.setRenderOrder(this.renderOrder);
this.scene.sys.displayList.add(layer);
return layer;
},
/**
* Creates a new Tilemap Layer that renders the LayerData associated with the given
* `layerID`. The currently selected layer in the map is set to this new layer.
*
* The `layerID` is important. If you've created your map in Tiled then you can get this by
* looking in Tiled and looking at the layer name. Or you can open the JSON file it exports and
* look at the layers[].name value. Either way it must match.
*
* Prior to v3.50.0 this method was called `createDynamicLayer`.
*
* @method Phaser.Tilemaps.Tilemap#createLayer
* @since 3.0.0
*
* @param {(number|string)} layerID - The layer array index value, or if a string is given, the layer name from Tiled.
* @param {(string|string[]|Phaser.Tilemaps.Tileset|Phaser.Tilemaps.Tileset[])} tileset - The tileset, or an array of tilesets, used to render this layer. Can be a string or a Tileset object.
* @param {number} [x=0] - The x position to place the layer in the world. If not specified, it will default to the layer offset from Tiled or 0.
* @param {number} [y=0] - The y position to place the layer in the world. If not specified, it will default to the layer offset from Tiled or 0.
*
* @return {?Phaser.Tilemaps.TilemapLayer} Returns the new layer was created, or null if it failed.
*/
createLayer: function (layerID, tileset, x, y)
{
var index = this.getLayerIndex(layerID);
if (index === null)
{
console.warn('Invalid Tilemap Layer ID: ' + layerID);
if (typeof layerID === 'string')
{
console.warn('Valid tilelayer names: %o', this.getTileLayerNames());
}
return null;
}
var layerData = this.layers[ index ];
// Check for an associated tilemap layer
if (layerData.tilemapLayer)
{
console.warn('Tilemap Layer ID already exists:' + layerID);
return null;
}
this.currentLayerIndex = index;
// Default the x/y position to match Tiled layer offset, if it exists.
if (x === undefined)
{
x = layerData.x;
}
if (y === undefined)
{
y = layerData.y;
}
var layer = new TilemapLayer(this.scene, this, index, tileset, x, y);
layer.setRenderOrder(this.renderOrder);
this.scene.sys.displayList.add(layer);
return layer;
},
/**
* This method will iterate through all of the objects defined in a Tiled Object Layer and then
* convert the matching results into Phaser Game Objects (by default, Sprites)
*
* Objects are matched on one of 4 criteria: The Object ID, the Object GID, the Object Name, or the Object Type.
*
* Within Tiled, Object IDs are unique per Object. Object GIDs, however, are shared by all objects
* using the same image. Finally, Object Names and Types are strings and the same name can be used on multiple
* Objects in Tiled, they do not have to be unique; Names are specific to Objects while Types can be inherited
* from Object GIDs using the same image.
*
* You set the configuration parameter accordingly, based on which type of criteria you wish
* to match against. For example, to convert all items on an Object Layer with a `gid` of 26:
*
* ```javascript
* createFromObjects(layerName, {
* gid: 26
* });
* ```
*
* Or, to convert objects with the name 'bonus':
*
* ```javascript
* createFromObjects(layerName, {
* name: 'bonus'
* });
* ```
*
* Or, to convert an object with a specific id:
*
* ```javascript
* createFromObjects(layerName, {
* id: 9
* });
* ```
*
* You should only specify either `id`, `gid`, `name`, `type`, or none of them. Do not add more than
* one criteria to your config. If you do not specify any criteria, then _all_ objects in the
* Object Layer will be converted.
*
* By default this method will convert Objects into {@link Phaser.GameObjects.Sprite} instances, but you can override
* this by providing your own class type:
*
* ```javascript
* createFromObjects(layerName, {
* gid: 26,
* classType: Coin
* });
* ```
*
* This will convert all Objects with a gid of 26 into your custom `Coin` class. You can pass
* any class type here, but it _must_ extend {@link Phaser.GameObjects.GameObject} as its base class.
* Your class will always be passed 1 parameter: `scene`, which is a reference to either the Scene
* specified in the config object or, if not given, the Scene to which this Tilemap belongs. The
* class must have {@link Phaser.GameObjects.Components.Transform#setPosition setPosition} and
* {@link Phaser.GameObjects.Components.Texture#setTexture setTexture} methods.
*
* This method will set the following Tiled Object properties on the new Game Object:
*
* - `flippedHorizontal` as `flipX`
* - `flippedVertical` as `flipY`
* - `height` as `displayHeight`
* - `name`
* - `rotation`
* - `visible`
* - `width` as `displayWidth`
* - `x`, adjusted for origin
* - `y`, adjusted for origin
*
* Additionally, this method will set Tiled Object custom properties
*
* - on the Game Object, if it has the same property name and a value that isn't `undefined`; or
* - on the Game Object's {@link Phaser.GameObjects.GameObject#data data store} otherwise.
*
* For example, a Tiled Object with custom properties `{ alpha: 0.5, gold: 1 }` will be created as a Game
* Object with an `alpha` value of 0.5 and a `data.values.gold` value of 1.
*
* When `useTileset` is `true` (the default), Tile Objects will inherit the texture and any tile properties
* from the tileset, and the local tile ID will be used as the texture frame. For the frame selection to work
* you need to load the tileset texture as a spritesheet so its frame names match the local tile IDs.
*
* For instance, a tileset tile
*
* ```
* { id: 3, type: 'treadmill', speed: 4 }
* ```
*
* with gid 19 and an object
*
* ```
* { id: 7, gid: 19, speed: 5, rotation: 90 }
* ```
*
* will be interpreted as
*
* ```
* { id: 7, gid: 19, speed: 5, rotation: 90, type: 'treadmill', texture: '[the tileset texture]', frame: 3 }
* ```
*
* You can suppress this behavior by setting the boolean `ignoreTileset` for each `config` that should ignore
* object gid tilesets.
*
* You can set a `container` property in the config. If given, the new Game Object will be added to
* the Container or Layer instance instead of the Scene.
*
* You can set named texture-`key` and texture-`frame` properties, which will be set on the new Game Object.
*
* Finally, you can provide an array of config objects, to convert multiple types of object in
* a single call:
*
* ```javascript
* createFromObjects(layerName, [
* {
* gid: 26,
* classType: Coin
* },
* {
* id: 9,
* classType: BossMonster
* },
* {
* name: 'lava',
* classType: LavaTile
* },
* {
* type: 'endzone',
* classType: Phaser.GameObjects.Zone
* }
* ]);
* ```
*
* The signature of this method changed significantly in v3.60.0. Prior to this, it did not take config objects.
*
* @method Phaser.Tilemaps.Tilemap#createFromObjects
* @since 3.0.0
*
* @param {string} objectLayerName - The name of the Tiled object layer to create the Game Objects from.
* @param {Phaser.Types.Tilemaps.CreateFromObjectLayerConfig|Phaser.Types.Tilemaps.CreateFromObjectLayerConfig[]} config - A CreateFromObjects configuration object, or an array of them.
* @param {boolean} [useTileset=true] - True if objects that set gids should also search the underlying tile for properties and data.
*
* @return {Phaser.GameObjects.GameObject[]} An array containing the Game Objects that were created. Empty if invalid object layer, or no matching id/gid/name was found.
*/
createFromObjects: function (objectLayerName, config, useTileset)
{
if (useTileset === undefined) { useTileset = true; }
var results = [];
var objectLayer = this.getObjectLayer(objectLayerName);
if (!objectLayer)
{
console.warn('createFromObjects: Invalid objectLayerName given: ' + objectLayerName);
return results;
}
var objectHelper = new ObjectHelper(useTileset ? this.tilesets : undefined);
if (!Array.isArray(config))
{
config = [ config ];
}
var objects = objectLayer.objects;
for (var c = 0; c < config.length; c++)
{
var singleConfig = config[ c ];
var id = GetFastValue(singleConfig, 'id', null);
var gid = GetFastValue(singleConfig, 'gid', null);
var name = GetFastValue(singleConfig, 'name', null);
var type = GetFastValue(singleConfig, 'type', null);
objectHelper.enabled = !GetFastValue(singleConfig, 'ignoreTileset', null);
var obj;
var toConvert = [];
// Sweep to get all the objects we want to convert in this pass
for (var s = 0; s < objects.length; s++)
{
obj = objects[ s ];
if (
(id === null && gid === null && name === null && type === null) ||
(id !== null && obj.id === id) ||
(gid !== null && obj.gid === gid) ||
(name !== null && obj.name === name) ||
(type !== null && objectHelper.getTypeIncludingTile(obj) === type)
)
{
toConvert.push(obj);
}
}
// Now let's convert them ...
var classType = GetFastValue(singleConfig, 'classType', Sprite);
var scene = GetFastValue(singleConfig, 'scene', this.scene);
var container = GetFastValue(singleConfig, 'container', null);
var texture = GetFastValue(singleConfig, 'key', null);
var frame = GetFastValue(singleConfig, 'frame', null);
for (var i = 0; i < toConvert.length; i++)
{
obj = toConvert[ i ];
var sprite = new classType(scene);
sprite.setName(obj.name);
sprite.setPosition(obj.x, obj.y);
objectHelper.setTextureAndFrame(sprite, texture, frame, obj);
if (obj.width)
{
sprite.displayWidth = obj.width;
}
if (obj.height)
{
sprite.displayHeight = obj.height;
}
if (this.orientation === ORIENTATION.ISOMETRIC)
{
var isometricRatio = this.tileWidth / this.tileHeight;
var isometricPosition = {
x: sprite.x - sprite.y,
y: (sprite.x + sprite.y) / isometricRatio
};
sprite.x = isometricPosition.x;
sprite.y = isometricPosition.y;
}
// Origin is (0, 1) for tile objects or (0, 0) for other objects in Tiled, so find the offset that matches the Sprites origin.
// Do not offset objects with zero dimensions (e.g. points).
var offset = {
x: sprite.originX * obj.width,
y: (sprite.originY - (obj.gid ? 1 : 0)) * obj.height
};
// If the object is rotated, then the origin offset also needs to be rotated.
if (obj.rotation)
{
var angle = DegToRad(obj.rotation);
Rotate(offset, angle);
sprite.rotation = angle;
}
sprite.x += offset.x;
sprite.y += offset.y;
if (obj.flippedHorizontal !== undefined || obj.flippedVertical !== undefined)
{
sprite.setFlip(obj.flippedHorizontal, obj.flippedVertical);
}
if (!obj.visible)
{
sprite.visible = false;
}
objectHelper.setPropertiesFromTiledObject(sprite, obj);
if (container)
{
container.add(sprite);
}
else
{
scene.add.existing(sprite);
}
results.push(sprite);
}
}
return results;
},
/**
* Creates a Sprite for every tile matching the given tile indexes in the layer. You can
* optionally specify if each tile will be replaced with a new tile after the Sprite has been
* created. Set this value to -1 if you want to just remove the tile after conversion.
*
* This is useful if you want to lay down special tiles in a level that are converted to
* Sprites, but want to replace the tile itself with a floor tile or similar once converted.
*
* The following features were added in Phaser v3.80:
*
* By default, Phaser Sprites have their origin set to 0.5 x 0.5. If you don't specify a new
* origin in the spriteConfig, then it will adjust the sprite positions by half the tile size,
* to position them accurately on the map.
*
* When the Sprite is created it will copy the following properties from the tile:
*
* 'rotation', 'flipX', 'flipY', 'alpha', 'visible' and 'tint'.
*
* The spriteConfig also has a special property called `useSpriteSheet`. If this is set to
* `true` and you have loaded the tileset as a sprite sheet (not an image), then it will
* set the Sprite key and frame to match the sprite texture and tile index.
*
* @method Phaser.Tilemaps.Tilemap#createFromTiles
* @since 3.0.0
*
* @param {(number|array)} indexes - The tile index, or array of indexes, to create Sprites from.
* @param {?(number|array)} replacements - The tile index, or array of indexes, to change a converted
* tile to. Set to `null` to leave the tiles unchanged. If an array is given, it is assumed to be a
* one-to-one mapping with the indexes array.
* @param {Phaser.Types.GameObjects.Sprite.SpriteConfig} [spriteConfig] - The config object to pass into the Sprite creator (i.e. scene.make.sprite).
* @param {Phaser.Scene} [scene] - The Scene to create the Sprites within.
* @param {Phaser.Cameras.Scene2D.Camera} [camera] - The Camera to use when calculating the tile index from the world values.
* @param {(string|number|Phaser.Tilemaps.TilemapLayer)} [layer] - The tile layer to use. If not given the current layer is used.
*
* @return {?Phaser.GameObjects.Sprite[]} Returns an array of Tiles, or null if the layer given was invalid.
*/
createFromTiles: function (indexes, replacements, spriteConfig, scene, camera, layer)
{
layer = this.getLayer(layer);
if (layer === null) { return null; }
return TilemapComponents.CreateFromTiles(indexes, replacements, spriteConfig, scene, camera, layer);
},
/**
* Sets the tiles in the given rectangular area (in tile coordinates) of the layer with the
* specified index. Tiles will be set to collide if the given index is a colliding index.
* Collision information in the region will be recalculated.
*
* If no layer specified, the map's current layer is used.
* This cannot be applied to StaticTilemapLayers.
*
* @method Phaser.Tilemaps.Tilemap#fill
* @since 3.0.0
*
* @param {number} index - The tile index to fill the area with.
* @param {number} [tileX] - The left most tile index (in tile coordinates) to use as the origin of the area.
* @param {number} [tileY] - The top most tile index (in tile coordinates) to use as the origin of the area.
* @param {number} [width] - How many tiles wide from the `tileX` index the area will be.
* @param {number} [height] - How many tiles tall from the `tileY` index the area will be.
* @param {boolean} [recalculateFaces=true] - `true` if the faces data should be recalculated.
* @param {(string|number|Phaser.Tilemaps.TilemapLayer)} [layer] - The tile layer to use. If not given the current layer is used.
*
* @return {?Phaser.Tilemaps.Tilemap} Returns this, or null if the layer given was invalid.
*/
fill: function (index, tileX, tileY, width, height, recalculateFaces, layer)
{
if (recalculateFaces === undefined) { recalculateFaces = true; }
layer = this.getLayer(layer);
if (layer === null) { return null; }
TilemapComponents.Fill(index, tileX, tileY, width, height, recalculateFaces, layer);
return this;
},
/**
* For each object in the given object layer, run the given filter callback function. Any
* objects that pass the filter test (i.e. where the callback returns true) will be returned in a
* new array. Similar to Array.prototype.Filter in vanilla JS.
*
* @method Phaser.Tilemaps.Tilemap#filterObjects
* @since 3.0.0
*
* @param {(Phaser.Tilemaps.ObjectLayer|string)} objectLayer - The name of an object layer (from Tiled) or an ObjectLayer instance.
* @param {TilemapFilterCallback} callback - The callback. Each object in the given area will be passed to this callback as the first and only parameter.
* @param {object} [context] - The context under which the callback should be run.
*
* @return {?Phaser.Types.Tilemaps.TiledObject[]} An array of object that match the search, or null if the objectLayer given was invalid.
*/
filterObjects: function (objectLayer, callback, context)
{
if (typeof objectLayer === 'string')
{
var name = objectLayer;
objectLayer = this.getObjectLayer(objectLayer);
if (!objectLayer)
{
console.warn('No object layer found with the name: ' + name);
return null;
}
}
return objectLayer.objects.filter(callback, context);
},
/**
* For each tile in the given rectangular area (in tile coordinates) of the layer, run the given
* filter callback function. Any tiles that pass the filter test (i.e. where the callback returns
* true) will returned as a new array. Similar to Array.prototype.Filter in vanilla JS.
* If no layer specified, the map's current layer is used.
*
* @method Phaser.Tilemaps.Tilemap#filterTiles
* @since 3.0.0
*
* @param {function} callback - The callback. Each tile in the given area will be passed to this
* callback as the first and only parameter. The callback should return true for tiles that pass the
* filter.
* @param {object} [context] - The context under which the callback should be run.
* @param {number} [tileX] - The left most tile index (in tile coordinates) to use as the origin of the area to filter.
* @param {number} [tileY] - The top most tile index (in tile coordinates) to use as the origin of the area to filter.
* @param {number} [width] - How many tiles wide from the `tileX` index the area will be.
* @param {number} [height] - How many tiles tall from the `tileY` index the area will be.
* @param {Phaser.Types.Tilemaps.FilteringOptions} [filteringOptions] - Optional filters to apply when getting the tiles.
* @param {(string|number|Phaser.Tilemaps.TilemapLayer)} [layer] - The tile layer to use. If not given the current layer is used.
*
* @return {?Phaser.Tilemaps.Tile[]} Returns an array of Tiles, or null if the layer given was invalid.
*/
filterTiles: function (callback, context, tileX, tileY, width, height, filteringOptions, layer)
{
layer = this.getLayer(layer);
if (layer === null) { return null; }
return TilemapComponents.FilterTiles(callback, context, tileX, tileY, width, height, filteringOptions, layer);
},
/**
* Searches the entire map layer for the first tile matching the given index, then returns that Tile
* object. If no match is found, it returns null. The search starts from the top-left tile and
* continues horizontally until it hits the end of the row, then it drops down to the next column.
* If the reverse boolean is true, it scans starting from the bottom-right corner traveling up to
* the top-left.
* If no layer specified, the map's current layer is used.
*
* @method Phaser.Tilemaps.Tilemap#findByIndex
* @since 3.0.0
*
* @param {number} index - The tile index value to search for.
* @param {number} [skip=0] - The number of times to skip a matching tile before returning.
* @param {boolean} [reverse=false] - If true it will scan the layer in reverse, starting at the bottom-right. Otherwise it scans from the top-left.
* @param {(string|number|Phaser.Tilemaps.TilemapLayer)} [layer] - The tile layer to use. If not given the current layer is used.
*
* @return {?Phaser.Tilemaps.Tile} Returns a Tiles, or null if the layer given was invalid.
*/
findByIndex: function (findIndex, skip, reverse, layer)
{
layer = this.getLayer(layer);
if (layer === null) { return null; }
return TilemapComponents.FindByIndex(findIndex, skip, reverse, layer);
},
/**
* Find the first object in the given object layer that satisfies the provided testing function.
* I.e. finds the first object for which `callback` returns true. Similar to
* Array.prototype.find in vanilla JS.
*
* @method Phaser.Tilemaps.Tilemap#findObject
* @since 3.0.0
*
* @param {(Phaser.Tilemaps.ObjectLayer|string)} objectLayer - The name of an object layer (from Tiled) or an ObjectLayer instance.
* @param {TilemapFindCallback} callback - The callback. Each object in the given area will be passed to this callback as the first and only parameter.
* @param {object} [context] - The context under which the callback should be run.
*
* @return {?Phaser.Types.Tilemaps.TiledObject} An object that matches the search, or null if no object found.
*/
findObject: function (objectLayer, callback, context)
{
if (typeof objectLayer === 'string')
{
var name = objectLayer;
objectLayer = this.getObjectLayer(objectLayer);
if (!objectLayer)
{
console.warn('No object layer found with the name: ' + name);
return null;
}
}
return objectLayer.objects.find(callback, context) || null;
},
/**
* Find the first tile in the given rectangular area (in tile coordinates) of the layer that
* satisfies the provided testing function. I.e. finds the first tile for which `callback` returns
* true. Similar to Array.prototype.find in vanilla JS.
* If no layer specified, the maps current layer is used.
*
* @method Phaser.Tilemaps.Tilemap#findTile
* @since 3.0.0
*
* @param {FindTileCallback} callback - The callback. Each tile in the given area will be passed to this callback as the first and only parameter.
* @param {object} [context] - The context under which the callback should be run.
* @param {number} [tileX] - The left most tile index (in tile coordinates) to use as the origin of the area to search.
* @param {number} [tileY] - The top most tile index (in tile coordinates) to use as the origin of the area to search.
* @param {number} [width] - How many tiles wide from the `tileX` index the area will be.
* @param {number} [height] - How many tiles tall from the `tileY` index the area will be.
* @param {Phaser.Types.Tilemaps.FilteringOptions} [filteringOptions] - Optional filters to apply when getting the tiles.
* @param {(string|number|Phaser.Tilemaps.TilemapLayer)} [layer] - The Tile layer to run the search on. If not provided will use the current layer.
*
* @return {?Phaser.Tilemaps.Tile} Returns a Tiles, or null if the layer given was invalid.
*/
findTile: function (callback, context, tileX, tileY, width, height, filteringOptions, layer)
{
layer = this.getLayer(layer);
if (layer === null) { return null; }
return TilemapComponents.FindTile(callback, context, tileX, tileY, width, height, filteringOptions, layer);
},
/**
* For each tile in the given rectangular area (in tile coordinates) of the layer, run the given
* callback. Similar to Array.prototype.forEach in vanilla JS.
*
* If no layer specified, the map's current layer is used.
*
* @method Phaser.Tilemaps.Tilemap#forEachTile
* @since 3.0.0
*
* @param {EachTileCallback} callback - The callback. Each tile in the given area will be passed to this callback as the first and only parameter.
* @param {object} [context] - The context under which the callback should be run.
* @param {number} [tileX] - The left most tile index (in tile coordinates) to use as the origin of the area to search.
* @param {number} [tileY] - The top most tile index (in tile coordinates) to use as the origin of the area to search.
* @param {number} [width] - How many tiles wide from the `tileX` index the area will be.
* @param {number} [height] - How many tiles tall from the `tileY` index the area will be.
* @param {Phaser.Types.Tilemaps.FilteringOptions} [filteringOptions] - Optional filters to apply when getting the tiles.
* @param {(string|number|Phaser.Tilemaps.TilemapLayer)} [layer] - The Tile layer to run the search on. If not provided will use the current layer.
*
* @return {?Phaser.Tilemaps.Tilemap} Returns this, or null if the layer given was invalid.
*/
forEachTile: function (callback, context, tileX, tileY, width, height, filteringOptions, layer)
{
layer = this.getLayer(layer);
if (layer === null) { return null; }
TilemapComponents.ForEachTile(callback, context, tileX, tileY, width, height, filteringOptions, layer);
return this;
},
/**
* Gets the image layer index based on its name.
*
* @method Phaser.Tilemaps.Tilemap#getImageIndex
* @since 3.0.0
*
* @param {string} name - The name of the image to get.
*
* @return {number} The index of the image in this tilemap, or null if not found.
*/
getImageIndex: function (name)
{
return this.getIndex(this.images, name);
},
/**
* Return a list of all valid imagelayer names loaded in this Tilemap.
*
* @method Phaser.Tilemaps.Tilemap#getImageLayerNames
* @since 3.21.0
*
* @return {string[]} Array of valid imagelayer names / IDs loaded into this Tilemap.
*/
getImageLayerNames: function ()
{
if (!this.images || !Array.isArray(this.images))
{
return [];
}
return this.images.map(function (image)
{
return image.name;
});
},
/**
* Internally used. Returns the index of the object in one of the Tilemaps arrays whose name
* property matches the given `name`.
*
* @method Phaser.Tilemaps.Tilemap#getIndex
* @since 3.0.0
*
* @param {array} location - The Tilemap array to search.
* @param {string} name - The