This commit is contained in:
Richard Davey 2017-11-29 22:24:02 +00:00
commit a45554d630
45 changed files with 889 additions and 254 deletions

View file

@ -3,7 +3,28 @@ var Parse = require('./parsers/Parse');
var MapData = require('./mapdata/MapData');
var Tilemap = require('./Tilemap');
// Parse from key or data and create a Tilemap. If neither is given, make an empty Tilemap.
/**
* Create a Tilemap from the given key or data. If neither is given, make a blank Tilemap. When
* loading from CSV or a 2D array, you should specify the tileWidth & tileHeight. When parsing from
* a map from Tiled, the tileWidth, tileHeight, width & height will be pulled from the map data. For
* an empty map, you should specify tileWidth, tileHeight, width & height.
*
* @param {Scene} scene - [description]
* @param {string} [key] - The key in the Phaser cache that corresponds to the loaded tilemap data.
* @param {integer} [tileWidth=32] - The width of a tile in pixels.
* @param {integer} [tileHeight=32] - The height of a tile in pixels.
* @param {integer} [width=10] - The width of the map in tiles.
* @param {integer} [height=10] - The height of the map in tiles.
* @param {integer[][]} [data] - Instead of loading from the cache, you can also load directly from
* a 2D array of tile indexes.
* @param {boolean} [insertNull=false] - Controls how empty tiles, tiles with an index of -1, in the
* map data are handled. If `true`, empty locations will get a value of `null`. If `false`, empty
* location will get a Tile object with an index of -1. If you've a large sparsely populated map and
* the tile data doesn't need to change then setting this value to `true` will help with memory
* consumption. However if your map is small or you need to update the tiles dynamically, then leave
* the default value set.
* @returns {Tilemap}
*/
var ParseToTilemap = function (scene, key, tileWidth, tileHeight, width, height, data, insertNull)
{
if (tileWidth === undefined) { tileWidth = 32; }

View file

@ -3,8 +3,6 @@ var Components = require('../components');
var Tile = new Class({
// TODO: custom rotation or use transform component
// TODO: Add in bounds mixin, or custom replacement
Mixins: [
Components.Alpha,
Components.Flip,
@ -13,35 +11,175 @@ var Tile = new Class({
initialize:
/**
* A Tile is a representation of a single tile within the Tilemap. This is a lightweight data
* representation, so it's position information is stored without factoring in scroll, layer
* scale or layer position.
*
* @class Tile
* @constructor
*
* @param {LayerData} layer - The LayerData object in the Tilemap that this tile belongs to.
* @param {integer} index - The unique index of this tile within the map.
* @param {integer} x - The x coordinate of this tile in tile coordinates.
* @param {integer} y - The y coordinate of this tile in tile coordinates.
* @param {integer} width - Width of the tile in pixels.
* @param {integer} height - Height of the tile in pixels.
*/
function Tile (layer, index, x, y, width, height)
{
/**
* The LayerData in the Tilemap data that this tile belongs to.
* @property {LayerData} layer
*/
this.layer = layer;
this.index = index;
this.x = x;
this.y = y;
this.worldX = x * width;
this.worldY = y * height;
this.width = width;
this.height = height;
this.properties = {};
this.rotation = 0;
this.collideLeft = false;
this.collideRight = false;
this.collideUp = false;
this.collideDown = false;
this.faceLeft = false;
this.faceRight = false;
this.faceTop = false;
this.faceBottom = false;
this.collisionCallback = null;
this.collisionCallbackContext = this;
this.scanned = false;
// TODO: update renders to allow for using Components.Tint
this.tint = 0xFFFFFF;
/**
* The index of this tile within the map data corresponding to the tileset, or -1 if this
* represents a blank tile.
* @property {integer} index
*/
this.index = index;
/**
* The x map coordinate of this tile in tile units.
* @property {integer} x
*/
this.x = x;
/**
* The y map coordinate of this tile in tile units.
* @property {integer} y
*/
this.y = y;
/**
* The world x coordinate of this tile in pixels. This does not factor in camera scroll,
* layer scale or layer position.
* @property {number} x
*/
this.worldX = x * width;
/**
* The world y coordinate of this tile in pixels. This does not factor in camera scroll,
* layer scale or layer position.
* @property {number} y
*/
this.worldY = y * height;
/**
* The width of the tile in pixels.
* @property {integer} width
*/
this.width = width;
/**
* The height of the tile in pixels.
* @property {integer} height
*/
this.height = height;
/**
* Tile specific properties. These usually come from Tiled.
* @property {object} properties
*/
this.properties = {};
/**
* The rotation angle of this tile.
* @property {number} rotation
*/
this.rotation = 0;
/**
* Whether the tile should collide with any object on the left side.
* @property {boolean} collideLeft
*/
this.collideLeft = false;
/**
* Whether the tile should collide with any object on the right side.
* @property {boolean} collideRight
*/
this.collideRight = false;
/**
* Whether the tile should collide with any object on the top side.
* @property {boolean} collideUp
*/
this.collideUp = false;
/**
* Whether the tile should collide with any object on the bottom side.
* @property {boolean} collideDown
*/
this.collideDown = false;
/**
* Whether the tile's left edge is interesting for collisions.
* @property {boolean} faceLeft
*/
this.faceLeft = false;
/**
* Whether the tile's right edge is interesting for collisions.
* @property {boolean} faceRight
*/
this.faceRight = false;
/**
* Whether the tile's top edge is interesting for collisions.
* @property {boolean} faceTop
*/
this.faceTop = false;
/**
* Whether the tile's bottom edge is interesting for collisions.
* @property {boolean} faceBottom
*/
this.faceBottom = false;
/**
* Tile collision callback.
* @property {function} collisionCallback
*/
this.collisionCallback = null;
/**
* The context in which the collision callback will be called.
* @property {object} collisionCallbackContext
*/
this.collisionCallbackContext = this;
/**
* The tint to apply to this tile. Note: tint is currently a single color value instead of
* the 4 corner tint component on other GameObjects.
* @property {number} Tint
* @default
*/
this.tint = 0xffffff;
},
// Copy everything except position & interesting faces
/**
* Check if the given x and y world coordinates are within this Tile. This does not factor in
* camera scroll, layer scale or layer position.
*
* @param {number} x - The x coordinate to test.
* @param {number} y - The y coordinate to test.
* @return {boolean} True if the coordinates are within this Tile, otherwise false.
*/
containsPoint: function (x, y)
{
return !(x < this.worldX || y < this.worldY || x > this.right || y > this.bottom);
},
/**
* Copies the tile data & properties from the given tile to this tile. This copies everything
* except for position and interesting faces.
*
* @param {Tile} tile - The tile to copy from.
* @returns {this}
*/
copy: function (tile)
{
this.index = tile.index;
@ -61,12 +199,9 @@ var Tile = new Class({
return this;
},
// Does not factor in scroll offset or tilemap layer position
containsPoint: function (x, y)
{
return !(x < this.worldX || y < this.worldY || x > this.right || y > this.bottom);
},
/**
* Clean up memory.
*/
destroy: function ()
{
this.collisionCallback = undefined;
@ -74,12 +209,33 @@ var Tile = new Class({
this.properties = undefined;
},
// Does not factor in scroll offset or tilemap layer position
/**
* Check for intersection with this tile. This does not factor in camera scroll, layer scale or
* layer position.
*
* @param {number} x - The x axis in pixels.
* @param {number} y - The y axis in pixels.
* @param {number} right - The right point.
* @param {number} bottom - The bottom point.
* @return {boolean}
*/
intersects: function (x, y, right, bottom)
{
return !(right <= this.worldX || bottom <= this.worldY || x >= this.right || y >= this.bottom);
return !(
right <= this.worldX || bottom <= this.worldY ||
x >= this.right || y >= this.bottom
);
},
/**
* Checks if the tile is interesting.
*
* @param {boolean} collides - If true, will consider the tile interesting if it collides on any
* side.
* @param {boolean} faces - If true, will consider the tile interesting if it has an interesting
* face.
* @returns {boolean} True if the Tile is interesting, otherwise false.
*/
isInteresting: function (collides, faces)
{
if (collides && faces) { return (this.canCollide || this.hasInterestingFace); }
@ -88,6 +244,11 @@ var Tile = new Class({
return false;
},
/**
* Reset collision status flags.
*
* @returns {this}
*/
resetCollision: function ()
{
this.collideLeft = false;
@ -99,8 +260,19 @@ var Tile = new Class({
this.faceBottom = false;
this.faceLeft = false;
this.faceRight = false;
return this;
},
/**
* Sets the collision flags for each side of this tile and updates the interesting faces list.
*
* @param {boolean} left - Indicating collide with any object on the left.
* @param {boolean} right - Indicating collide with any object on the right.
* @param {boolean} up - Indicating collide with any object on the top.
* @param {boolean} down - Indicating collide with any object on the bottom.
* @returns {this}
*/
setCollision: function (left, right, up, down)
{
if (right === undefined) { right = left; }
@ -116,11 +288,22 @@ var Tile = new Class({
this.faceRight = right;
this.faceTop = up;
this.faceBottom = down;
return this;
},
/**
* Set a callback to be called when this tile is hit by an object. The callback must true for
* collision processing to take place.
*
* @param {function} callback - Callback function.
* @param {object} context - Callback will be called within this context.
* @returns {this}
*/
setCollisionCallback: function (callback, context)
{
if (callback === null) {
if (callback === null)
{
this.collisionCallback = undefined;
this.collisionCallbackContext = undefined;
}
@ -129,17 +312,32 @@ var Tile = new Class({
this.collisionCallback = callback;
this.collisionCallbackContext = context;
}
return this;
},
/**
* Sets the size of the tile and updates its worldX and worldY.
*
* @param {integer} tileWidth - The width of the tile in pixels.
* @param {integer} tileHeight - The height of the tile in pixels.
* @returns {this}
*/
setSize: function (tileWidth, tileHeight)
{
this.worldX = this.x * tileWidth;
this.worldY = this.y * tileHeight;
this.width = tileWidth;
this.height = tileHeight;
return this;
},
// True if this tile can collide on any of its faces or has a collision callback set.
/**
* True if this tile can collide on any of its faces or has a collision callback set.
* @property {boolean} canCollide
* @readonly
*/
canCollide: {
get: function ()
{
@ -147,7 +345,11 @@ var Tile = new Class({
}
},
// True if this tile can collide on any of its faces.
/**
* True if this tile can collide on any of its faces.
* @property {boolean} canCollide
* @readonly
*/
collides: {
get: function ()
{
@ -155,7 +357,11 @@ var Tile = new Class({
}
},
// True if this tile has any interesting faces
/**
* True if this tile has any interesting faces.
* @property {boolean} canCollide
* @readonly
*/
hasInterestingFace: {
get: function ()
{
@ -163,6 +369,12 @@ var Tile = new Class({
}
},
/**
* The world position of the left side of the tile. This does not factor in camera scroll, layer
* scale or layer position.
* @property {integer} left
* @readonly
*/
left: {
get: function ()
{
@ -170,6 +382,12 @@ var Tile = new Class({
}
},
/**
* The world position of the right side of the tile. This does not factor in camera scroll,
* layer scale or layer position.
* @property {integer} right
* @readonly
*/
right: {
get: function ()
{
@ -177,6 +395,12 @@ var Tile = new Class({
}
},
/**
* The world position of the top side of the tile. This does not factor in camera scroll,
* layer scale or layer position.
* @property {integer} top
* @readonly
*/
top: {
get: function ()
{
@ -184,6 +408,12 @@ var Tile = new Class({
}
},
/**
* The world position of the bottom side of the tile. This does not factor in camera scroll,
* layer scale or layer position.
* @property {integer} bottom
* @readonly
*/
bottom: {
get: function ()
{
@ -191,6 +421,12 @@ var Tile = new Class({
}
},
/**
* The x world position of the center of the tile. This does not factor in camera scroll, layer
* scale or layer position.
* @property {integer} centerX
* @readonly
*/
centerX: {
get: function ()
{
@ -198,6 +434,12 @@ var Tile = new Class({
}
},
/**
* The y world position of the center of the tile. This does not factor in camera scroll, layer
* scale or layer position.
* @property {integer} centerY
* @readonly
*/
centerY: {
get: function ()
{

View file

@ -38,16 +38,12 @@ var Tilemap = new Class({
this.tiles = mapData.tiles;
this.objects = mapData.objects;
this.currentLayerIndex = 0;
// TODO: debugging methods
},
addTilesetImage: function (tilesetName, key, tileWidth, tileHeight, tileMargin, tileSpacing, gid)
{
if (tilesetName === undefined) { return null; }
if (key === undefined || key === null) { key = tilesetName; }
if (tileWidth === undefined) { tileWidth = this.tileWidth; }
if (tileHeight === undefined) { tileHeight = this.tileHeight; }
if (!this.scene.sys.textures.exists(key))
{
@ -57,8 +53,6 @@ var Tilemap = new Class({
var texture = this.scene.sys.textures.get(key);
// TODO: potentially add in v2 support for bitmap data
var index = this.getTilesetIndex(tilesetName);
if (index === null && this.format === Formats.TILEMAP_TILED_JSON)
@ -75,6 +69,8 @@ var Tilemap = new Class({
return this.tilesets[index];
}
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; }
@ -83,15 +79,13 @@ var Tilemap = new Class({
tileset.setImage(texture);
this.tilesets.push(tileset);
return tileset;
// TODO: add in GID & master list of tiles
},
// Creates & selects
createBlankDynamicLayer: function (name, tileset, x, y, width, height, tileWidth, tileHeight)
{
if (tileWidth === undefined) { tileWidth = this.tileWidth; }
if (tileHeight === undefined) { tileHeight = this.tileHeight; }
if (tileWidth === undefined) { tileWidth = tileset.tileWidth; }
if (tileHeight === undefined) { tileHeight = tileset.tileHeight; }
if (width === undefined) { width = this.width; }
if (height === undefined) { height = this.height; }
if (x === undefined) { x = 0; }
@ -127,8 +121,6 @@ var Tilemap = new Class({
this.layers.push(layerData);
this.currentLayerIndex = this.layers.length - 1;
// TODO: decide about v2 trimming to game width/height
var dynamicLayer = new DynamicTilemapLayer(this.scene, this, this.currentLayerIndex, tileset, x, y);
this.scene.sys.displayList.add(dynamicLayer);
@ -142,13 +134,13 @@ var Tilemap = new Class({
* property of alpha: 0.5 in the map editor will duplicate that when the Sprite is created.
*
* @param {string} name - The name of the object layer (from Tiled) to create Sprites from.
* @param {number} id - Either the id (object), gid (tile object) or name (object or tile
* object) from Tiled. Ids are unique in Tiled, but a gid is shared by all tile objects with the
* same graphic. The same name can be used on multiple objects.
* @param {integer|string} id - Either the id (object), gid (tile object) or name (object or
* tile object) from Tiled. Ids are unique in Tiled, but a gid is shared by all tile objects
* with the same graphic. The same name can be used on multiple objects.
* @param {object} spriteConfig - The config object to pass into the Sprite creator (i.e.
* scene.make.sprite).
* @param {Scene} [scene=the scene the map is within] - The Scene to create the Sprites within.
* @return {array} An array of the Sprites that were created.
* @return {Sprite[]} An array of the Sprites that were created.
*/
createFromObjects: function (name, id, spriteConfig, scene)
{
@ -220,7 +212,7 @@ var Tilemap = new Class({
return sprites;
},
// Creates & selects
// Creates & selects, uses layer offset if x,y undefined
createStaticLayer: function (layerID, tileset, x, y)
{
var index = this.getLayerIndex(layerID);
@ -231,24 +223,36 @@ var Tilemap = new Class({
return null;
}
var layerData = this.layers[index];
// Check for an associated static or dynamic tilemap layer
if (this.layers[index].tilemapLayer)
if (layerData.tilemapLayer)
{
console.warn('Cannot create static tilemap layer since a static or dynamic tilemap layer exists for layer ID:' + layerID);
return null;
}
// TODO: new feature, allow multiple CSV layers
// TODO: display dimension
this.currentLayerIndex = index;
// Make sure that all the LayerData & the tiles have the correct tile size. They usually
// are, but wouldn't match if you try to load a 2x or 4x res tileset when the map was made
// with a 1x res tileset.
if (layerData.tileWidth !== tileset.tileWidth || layerData.tileHeight !== tileset.tileHeight)
{
this.setLayerTileSize(tileset.tileWidth, tileset.tileHeight, index);
}
// Default the x/y position to match Tiled layer offset, if it exists.
if (x === undefined && this.layers[index].x) { x = this.layers[index].x; }
if (y === undefined && this.layers[index].y) { y = this.layers[index].y; }
var layer = new StaticTilemapLayer(this.scene, this, index, tileset, x, y);
this.scene.sys.displayList.add(layer);
return layer;
},
// Creates & selects
// Creates & selects, uses layer offset if x,y undefined
createDynamicLayer: function (layerID, tileset, x, y)
{
var index = this.getLayerIndex(layerID);
@ -259,23 +263,56 @@ var Tilemap = new Class({
return null;
}
var layerData = this.layers[index];
// Check for an associated static or dynamic tilemap layer
if (this.layers[index].tilemapLayer)
if (layerData.tilemapLayer)
{
console.warn('Cannot create dynamic tilemap layer since a static or dynamic tilemap layer exists for layer ID:' + layerID);
return null;
}
// TODO: new feature, allow multiple CSV layers
// TODO: display dimension
this.currentLayerIndex = index;
// Make sure that all the LayerData & the tiles have the correct tile size. They usually
// are, but wouldn't match if you try to load a 2x or 4x res tileset when the map was made
// with a 1x res tileset.
if (layerData.tileWidth !== tileset.tileWidth || layerData.tileHeight !== tileset.tileHeight)
{
this.setLayerTileSize(tileset.tileWidth, tileset.tileHeight, index);
}
// Default the x/y position to match Tiled layer offset, if it exists.
if (x === undefined && this.layers[index].x) { x = this.layers[index].x; }
if (y === undefined && this.layers[index].y) { y = this.layers[index].y; }
var layer = new DynamicTilemapLayer(this.scene, this, index, tileset, x, y);
this.scene.sys.displayList.add(layer);
return layer;
},
convertLayerToStatic: function (layer)
{
layer = this.getLayer(layer);
if (layer === null) { return null; }
var dynamicLayer = layer.tilemapLayer;
if (!dynamicLayer || !(dynamicLayer instanceof DynamicTilemapLayer))
{
return null;
}
var staticLayer = new StaticTilemapLayer(dynamicLayer.scene, dynamicLayer.map,
dynamicLayer.layerIndex, dynamicLayer.tileset, dynamicLayer.x, dynamicLayer.y);
this.scene.sys.displayList.add(staticLayer);
dynamicLayer.destroy();
return staticLayer;
},
copy: function (srcTileX, srcTileY, width, height, destTileX, destTileY, recalculateFaces, layer)
{
layer = this.getLayer(layer);
@ -472,13 +509,13 @@ var Tilemap = new Class({
return TilemapComponents.PutTileAtWorldXY(tile, worldX, worldY, recalculateFaces, camera, layer);
},
randomize: function (tileX, tileY, width, height, indices, layer)
randomize: function (tileX, tileY, width, height, indexes, layer)
{
layer = this.getLayer(layer);
if (this._isStaticCall(layer, 'randomize')) { return this; }
if (layer !== null)
{
TilemapComponents.Randomize(tileX, tileY, width, height, indices, layer);
TilemapComponents.Randomize(tileX, tileY, width, height, indexes, layer);
}
return this;
},
@ -514,6 +551,14 @@ var Tilemap = new Class({
return TilemapComponents.RemoveTileAtWorldXY(worldX, worldY, replaceWithNull, recalculateFaces, camera, layer);
},
renderDebug: function (graphics, styleConfig, layer)
{
layer = this.getLayer(layer);
if (layer === null) { return this; }
TilemapComponents.RenderDebug(graphics, styleConfig, layer);
return this;
},
replaceByIndex: function (findIndex, newIndex, tileX, tileY, width, height, layer)
{
layer = this.getLayer(layer);
@ -575,30 +620,33 @@ var Tilemap = new Class({
return this;
},
setTileSize: function (tileWidth, tileHeight)
setBaseTileSize: function (tileWidth, tileHeight)
{
this.tileWidth = tileWidth;
this.tileHeight = tileHeight;
this.widthInPixels = this.width * tileWidth;
this.heightInPixels = this.height * tileHeight;
},
// Update all the layers & tiles
for (var i = 0; i < this.layers.length; i++)
// Sets the tile size for a given layer
setLayerTileSize: function (tileWidth, tileHeight, layer)
{
layer = this.getLayer(layer);
if (layer === null) { return this; }
layer.tileWidth = tileWidth;
layer.tileHeight = tileHeight;
var mapData = layer.data;
var mapWidth = layer.width;
var mapHeight = layer.height;
for (var row = 0; row < mapHeight; ++row)
{
this.layers[i].tileWidth = tileWidth;
this.layers[i].tileHeight = tileHeight;
var mapData = this.layers[i].data;
var mapWidth = this.layers[i].width;
var mapHeight = this.layers[i].height;
for (var row = 0; row < mapHeight; ++row)
for (var col = 0; col < mapWidth; ++col)
{
for (var col = 0; col < mapWidth; ++col)
{
var tile = mapData[row][col];
if (tile !== null) { tile.setSize(tileWidth, tileHeight); }
}
var tile = mapData[row][col];
if (tile !== null) { tile.setSize(tileWidth, tileHeight); }
}
}

View file

@ -3,18 +3,33 @@ var ParseToTilemap = require('./ParseToTilemap');
// When registering a factory function 'this' refers to the GameObjectCreator context.
/**
* Creates a Tilemap from the given key or data, or creates a blank Tilemap if no key/data provided.
* When loading from CSV or a 2D array, you should specify the tileWidth & tileHeight. When parsing
* from a map from Tiled, the tileWidth, tileHeight, width & height will be pulled from the map
* data. For an empty map, you should specify tileWidth, tileHeight, width & height.
*
* @param {object} [config] - The config options for the Tilemap.
* @param {string} [config.key] - The key in the Phaser cache that corresponds to the loaded tilemap
* data.
* @param {integer[][]} [config.data] - Instead of loading from the cache, you can also load
* directly from a 2D array of tile indexes.
* @param {integer} [config.tileWidth=32] - The width of a tile in pixels.
* @param {integer} [config.tileHeight=32] - The height of a tile in pixels.
* @param {integer} [config.width=10] - The width of the map in tiles.
* @param {integer} [config.height=10] - The height of the map in tiles.
* @param {boolean} [config.insertNull=false] - Controls how empty tiles, tiles with an index of -1,
* in the map data are handled. If `true`, empty locations will get a value of `null`. If `false`,
* empty location will get a Tile object with an index of -1. If you've a large sparsely populated
* map and the tile data doesn't need to change then setting this value to `true` will help with
* memory consumption. However if your map is small or you need to update the tiles dynamically,
* then leave the default value set.
* @returns {Tilemap}
*/
GameObjectCreator.register('tilemap', function (config)
{
// config {
// key: undefined, (string|number),
// tileWidth: 32,
// tileHeight: 32,
// width: 10,
// height: 10,
// data: null (2D array of tile indices),
// insertNull: false
// }
// Defaults are applied in ParseToTilemap
var c = config !== undefined ? config : {};
return ParseToTilemap(this.scene, c.key, c.tileWidth, c.tileHeight, c.width, c.height, c.data, c.insertNull);
return ParseToTilemap(this.scene, c.key, c.tileWidth, c.tileHeight, c.width, c.height, c.data,
c.insertNull);
});

View file

@ -1,7 +1,6 @@
var GameObjectFactory = require('../../scene/plugins/GameObjectFactory');
var ParseToTilemap = require('./ParseToTilemap');
// When registering a factory function 'this' refers to the GameObjectFactory context.
//
// There are several properties available to use:
@ -10,17 +9,41 @@ var ParseToTilemap = require('./ParseToTilemap');
// this.displayList - a reference to the Display List the Scene owns
// this.updateList - a reference to the Update List the Scene owns
/**
* Creates a Tilemap from the given key or data, or creates a blank Tilemap if no key/data provided.
* When loading from CSV or a 2D array, you should specify the tileWidth & tileHeight. When parsing
* from a map from Tiled, the tileWidth, tileHeight, width & height will be pulled from the map
* data. For an empty map, you should specify tileWidth, tileHeight, width & height.
*
* @param {string} [key] - The key in the Phaser cache that corresponds to the loaded tilemap data.
* @param {integer} [tileWidth=32] - The width of a tile in pixels. Pass in `null` to leave as the
* default.
* @param {integer} [tileHeight=32] - The height of a tile in pixels. Pass in `null` to leave as the
* default.
* @param {integer} [width=10] - The width of the map in tiles. Pass in `null` to leave as the
* default.
* @param {integer} [height=10] - The height of the map in tiles. Pass in `null` to leave as the
* default.
* @param {integer[][]} [data] - Instead of loading from the cache, you can also load directly from
* a 2D array of tile indexes. Pass in `null` for no data.
* @param {boolean} [insertNull=false] - Controls how empty tiles, tiles with an index of -1, in the
* map data are handled. If `true`, empty locations will get a value of `null`. If `false`, empty
* location will get a Tile object with an index of -1. If you've a large sparsely populated map and
* the tile data doesn't need to change then setting this value to `true` will help with memory
* consumption. However if your map is small or you need to update the tiles dynamically, then leave
* the default value set.
* @returns {Tilemap}
*/
GameObjectFactory.register('tilemap', function (key, tileWidth, tileHeight, width, height, data, insertNull)
{
// Allow users to specify null as default parameter, but convert it to undefined to match what
// the creator function passed to the parser.
// Allow users to specify null to indicate that they want the default value, since null is
// shorter & more legible than undefined. Convert null to undefined to allow ParseToTilemap
// defaults to take effect.
if (key === null) { key = undefined; }
if (tileWidth === null) { tileWidth = undefined; }
if (tileHeight === null) { tileHeight = undefined; }
if (width === null) { width = undefined; }
if (height === null) { height = undefined; }
if (data === null) { data = undefined; }
if (insertNull === null) { insertNull = undefined; }
return ParseToTilemap(this.scene, key, tileWidth, tileHeight, width, height, data, insertNull);
});

View file

@ -4,48 +4,155 @@ var Tileset = new Class({
initialize:
function Tileset (name, firstgid, tileWidth, tileHeight, tileMargin, tileSpacing, properties, tileData)
/**
* A Tileset is a combination of an image containing the tiles and a container for data about
* each tile.
*
* @class Tileset
* @constructor
*
* @param {string} name - The name of the tileset in the map data.
* @param {integer} firstgid - The first tile index this tileset contains.
* @param {integer} [tileWidth=32] - Width of each tile (in pixels).
* @param {integer} [tileHeight=32] - Height of each tile (in pixels).
* @param {integer} [tileMargin=0] - The margin around all tiles in the sheet (in pixels).
* @param {integer} [tileSpacing=0] - The spacing between each tile in the sheet (in pixels).
* @param {object} [tileProperties={}] - Custom properties defined per tile in the Tileset.
* These typically are custom properties created in Tiled when editing a tileset.
* @param {object} [tileData={}] - Data stored per tile. These typically are created in Tiled
* when editing a tileset, e.g. from Tiled's tile collision editor or terrain editor.
*/
function Tileset (name, firstgid, tileWidth, tileHeight, tileMargin, tileSpacing, tileProperties, tileData)
{
if (tileWidth === undefined || tileWidth <= 0) { tileWidth = 32; }
if (tileHeight === undefined || tileHeight <= 0) { tileHeight = 32; }
if (tileMargin === undefined) { tileMargin = 0; }
if (tileSpacing === undefined) { tileSpacing = 0; }
if (properties === undefined) { properties = {}; }
if (tileProperties === undefined) { tileProperties = {}; }
if (tileData === undefined) { tileData = {}; }
/**
* The name of the Tileset.
* @property {string} name
*/
this.name = name;
/**
* The starting index of the first tile index this Tileset contains.
* @property {integer} firstgid
*/
this.firstgid = firstgid;
/**
* The width of each tile (in pixels). Use setTileSize to change.
* @property {integer} tileWidth
* @readonly
*/
this.tileWidth = tileWidth;
/**
* The height of each tile (in pixels). Use setTileSize to change.
* @property {integer} tileHeight
* @readonly
*/
this.tileHeight = tileHeight;
/**
* The margin around the tiles in the sheet (in pixels). Use `setSpacing` to change.
* @property {integer} tileMargin
* @readonly
*/
this.tileMargin = tileMargin;
/**
* The spacing between each the tile in the sheet (in pixels). Use `setSpacing` to change.
* @property {integer} tileSpacing
* @readonly
*/
this.tileSpacing = tileSpacing;
this.properties = properties;
/**
* Tileset-specific properties per tile that are typically defined in the Tiled editor.
* @property {object} tileProperties
*/
this.tileProperties = tileProperties;
/**
* Tileset-specific data per tile that are typically defined in the Tiled editor. This is
* where collision objects and terrain are stored.
* @property {object} tileData
*/
this.tileData = tileData;
/**
* The cached image that contains the individual tiles. Use setImage to set.
* @property {Texture|null} image
* @readonly
*/
this.image = null;
/**
* The number of tile rows in the the tileset.
* @property {integer} rows
* @readonly
*/
this.rows = 0;
/**
* The number of tile columns in the tileset.
* @property {integer} columns
* @readonly
*/
this.columns = 0;
/**
* The total number of tiles in the tileset.
* @property {integer} total
* @readonly
*/
this.total = 0;
/**
* The look-up table to specific tile image texture coordinates (UV in pixels). Each element
* contains the coordinates for a tile in an object of the form {x, y}.
* @property {object[]} texCoordinates
* @readonly
*/
this.texCoordinates = [];
},
/**
* Get a tile's properties that are stored in the Tileset. Returns null if tile index is not
* contained in this Tileset.
*
* @param {integer} tileIndex - The unique id of the tile across all tilesets in the map.
* @returns {object|undefined|null}
*/
getTileProperty: function (tileIndex)
{
if (!this.containsTileIndex(tileIndex)) { return null; }
return this.properties[tileIndex - this.firstgid];
return this.tileProperties[tileIndex - this.firstgid];
},
/**
* Get a tile's data that is stored in the Tileset. Returns null if tile index is not
* contained in this Tileset.
*
* @param {integer} tileIndex - The unique id of the tile across all tilesets in the map.
* @returns {object|undefined|null}
*/
getTileData: function (tileIndex)
{
if (!this.containsTileIndex(tileIndex)) { return null; }
return this.tileData[tileIndex - this.firstgid];
},
setImage: function (texture)
{
this.image = texture;
this.updateTileData(this.image.source[0].width, this.image.source[0].height);
},
/**
* Returns true if and only if this Tileset contains the given tile index.
*
* @param {integer} tileIndex - The unique id of the tile across all tilesets in the map.
* @returns {boolean}
*/
containsTileIndex: function (tileIndex)
{
return (
@ -54,12 +161,40 @@ var Tileset = new Class({
);
},
/**
* Returns the texture coordinates (UV in pixels) in the Tileset image for the given tile index.
* Returns null if tile index is not contained in this Tileset.
*
* @param {integer} tileIndex - The unique id of the tile across all tilesets in the map.
* @returns {object|null} Object in the form { x, y } representing the top-left UV coordinate
* within the Tileset image.
*/
getTileTextureCoordinates: function (tileIndex)
{
if (!this.containsTileIndex(tileIndex)) { return null; }
return this.texCoordinates[tileIndex - this.firstgid];
},
/**
* Sets the image associated with this Tileset and updates the tile data (rows, columns, etc.).
*
* @param {Texture} texture - The image that contains the tiles.
* @returns {this}
*/
setImage: function (texture)
{
this.image = texture;
this.updateTileData(this.image.source[0].width, this.image.source[0].height);
return this;
},
/**
* Sets the tile width & height and updates the tile data (rows, columns, etc.).
*
* @param {integer} [tileWidth] - The width of a tile in pixels.
* @param {integer} [tileHeight] - The height of a tile in pixels.
* @returns {this}
*/
setTileSize: function (tileWidth, tileHeight)
{
if (tileWidth !== undefined) { this.tileWidth = tileWidth; }
@ -69,8 +204,17 @@ var Tileset = new Class({
{
this.updateTileData(this.image.source[0].width, this.image.source[0].height);
}
return this;
},
/**
* Sets the tile margin & spacing and updates the tile data (rows, columns, etc.).
*
* @param {integer} [margin] - The margin around the tiles in the sheet (in pixels).
* @param {integer} [spacing] - The spacing between the tiles in the sheet (in pixels).
* @returns {this}
*/
setSpacing: function (margin, spacing)
{
if (margin !== undefined) { this.tileMargin = margin; }
@ -80,8 +224,17 @@ var Tileset = new Class({
{
this.updateTileData(this.image.source[0].width, this.image.source[0].height);
}
return this;
},
/**
* Updates tile texture coordinates and tileset data.
*
* @param {integer} imageWidth - The (expected) width of the image to slice.
* @param {integer} imageHeight - The (expected) height of the image to slice.
* @returns {this}
*/
updateTileData: function (imageWidth, imageHeight)
{
var rowCount = (imageHeight - this.tileMargin * 2 + this.tileSpacing) / (this.tileHeight + this.tileSpacing);
@ -92,8 +245,8 @@ var Tileset = new Class({
console.warn('Tileset ' + this.name + ' image tile area is not an even multiple of tile size');
}
// In Tiled a tileset image that is not an even multiple of the tile dimensions
// is truncated - hence the floor when calculating the rows/columns.
// In Tiled a tileset image that is not an even multiple of the tile dimensions is truncated
// - hence the floor when calculating the rows/columns.
rowCount = Math.floor(rowCount);
colCount = Math.floor(colCount);
@ -119,6 +272,8 @@ var Tileset = new Class({
tx = this.tileMargin;
ty += this.tileHeight + this.tileSpacing;
}
return this;
}
});

View file

@ -6,10 +6,10 @@ var GetTilesWithin = require('./GetTilesWithin');
* layer. Interesting faces are used internally for optimizing collisions against tiles. This method
* is mostly used internally.
*
* @param {number} [tileX=0] - [description]
* @param {number} [tileY=0] - [description]
* @param {number} [width=max width based on tileX] - [description]
* @param {number} [height=max height based on tileY] - [description]
* @param {integer} [tileX=0] - [description]
* @param {integer} [tileY=0] - [description]
* @param {integer} [width=max width based on tileX] - [description]
* @param {integer} [height=max height based on tileY] - [description]
* @param {LayerData} layer - [description]
*/
var CalculateFacesWithin = function (tileX, tileY, width, height, layer)

View file

@ -6,13 +6,13 @@ var CalculateFacesWithin = require('./CalculateFacesWithin');
* coordinates) within the layer. This copies all tile properties & recalculates collision
* information in the destination region.
*
* @param {number} srcTileX - [description]
* @param {number} srcTileY - [description]
* @param {number} width - [description]
* @param {number} height - [description]
* @param {number} destTileX - [description]
* @param {number} destTileY - [description]
* @param {number} destTileY - [description]
* @param {integer} srcTileX - [description]
* @param {integer} srcTileY - [description]
* @param {integer} width - [description]
* @param {integer} height - [description]
* @param {integer} destTileX - [description]
* @param {integer} destTileY - [description]
* @param {integer} destTileY - [description]
* @param {boolean} [recalculateFaces=true] - [description]
* @param {LayerData} layer - [description]
*/

View file

@ -9,8 +9,8 @@ var ReplaceByIndex = require('./ReplaceByIndex');
* created. 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.
*
* @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
* @param {integer|array} indexes - The tile index, or array of indexes, to create Sprites from.
* @param {integer|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 {object} spriteConfig - The config object to pass into the Sprite creator (i.e.
@ -18,7 +18,7 @@ var ReplaceByIndex = require('./ReplaceByIndex');
* @param {Scene} [scene=scene the map is within] - The Scene to create the Sprites within.
* @param {Camera} [camera=main camera] - The Camera to use when determining the world XY
* @param {LayerData} layer - [description]
* @return {array} An array of the Sprites that were created.
* @return {Sprite[]} An array of the Sprites that were created.
*/
var CreateFromTiles = function (indexes, replacements, spriteConfig, scene, camera, layer)
{

View file

@ -5,7 +5,7 @@
* @param {LayerData} layer - [description]
* @param {Camera} [camera=main camera] - [description]
* @param {array} [outputArray] - [description]
* @returns {array}
* @returns {Tile[]}
*/
var CullTiles = function (layer, camera, outputArray)
{

View file

@ -6,11 +6,11 @@ var CalculateFacesWithin = require('./CalculateFacesWithin');
* specified index. Tiles will be set to collide if the given index is a colliding index.
* Collision information in the region will be recalculated.
*
* @param {number} index - [description]
* @param {number} [tileX=0] - [description]
* @param {number} [tileY=0] - [description]
* @param {number} [width=max width based on tileX] - [description]
* @param {number} [height=max height based on tileY] - [description]
* @param {integer} index - [description]
* @param {integer} [tileX=0] - [description]
* @param {integer} [tileY=0] - [description]
* @param {integer} [width=max width based on tileX] - [description]
* @param {integer} [height=max height based on tileY] - [description]
* @param {boolean} [recalculateFaces=true] - [description]
* @param {LayerData} layer - [description]
*/

View file

@ -5,14 +5,14 @@ var GetTilesWithin = require('./GetTilesWithin');
* 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.
*
* @param {number} callback - The callback. Each tile in the given area will be passed to this
* @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 {number} [context] - The context under which the callback should be run.
* @param {number} [tileX=0] - [description]
* @param {number} [tileY=0] - [description]
* @param {number} [width=max width based on tileX] - [description]
* @param {number} [height=max height based on tileY] - [description]
* @param {object} [context] - The context under which the callback should be run.
* @param {integer} [tileX=0] - [description]
* @param {integer} [tileY=0] - [description]
* @param {integer} [width=max width based on tileX] - [description]
* @param {integer} [height=max height based on tileY] - [description]
* @param {object} [filteringOptions] - Optional filters to apply when getting the tiles.
* @param {boolean} [filteringOptions.isNotEmpty=false] - If true, only return tiles that don't have
* -1 for an index.
@ -21,7 +21,7 @@ var GetTilesWithin = require('./GetTilesWithin');
* @param {boolean} [filteringOptions.hasInterestingFace=false] - If true, only return tiles that
* have at least one interesting face.
* @param {LayerData} layer - [description]
* @returns {array} The filtered array of Tiles.
* @returns {Tile[]} The filtered array of Tiles.
*/
var FilterTiles = function (callback, context, tileX, tileY, width, height, filteringOptions, layer)
{

View file

@ -5,9 +5,9 @@
* If the reverse boolean is true, it scans starting from the bottom-right corner traveling up to
* the top-left.
*
* @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 {number} [reverse=false] - If true it will scan the layer in reverse, starting at the
* @param {integer} index - The tile index value to search for.
* @param {integer} [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 {LayerData} layer - [description]
* @return {Tile|null} The first (or n skipped) tile with the matching index.

View file

@ -2,20 +2,20 @@ var GetTilesWithin = require('./GetTilesWithin');
/**
* For each tile in the given rectangular area (in tile coordinates) of the layer, run the given
* callback.
* callback. Similar to Array.prototype.forEach in vanilla JS.
*
* @param {number} callback - The callback. Each tile in the given area will be passed to this
* @param {function} callback - The callback. Each tile in the given area will be passed to this
* callback as the first and only parameter.
* @param {number} context - The context under which the callback should be run.
* @param {number} [tileX=0] - [description]
* @param {number} [tileY=0] - [description]
* @param {number} [width=max width based on tileX] - [description]
* @param {number} [height=max height based on tileY] - [description]
* @param {object} [context] - The context under which the callback should be run.
* @param {integer} [tileX=0] - [description]
* @param {integer} [tileY=0] - [description]
* @param {integer} [width=max width based on tileX] - [description]
* @param {integer} [height=max height based on tileY] - [description]
* @param {object} [filteringOptions] - Optional filters to apply when getting the tiles.
* @param {boolean} [filteringOptions.isNotEmpty=false] - If true, only return tiles that don't have
* -1 for an index.
* @param {boolean} [filteringOptions.isColliding=false] - If true, only return tiles that collide on
* at least one side.
* @param {boolean} [filteringOptions.isColliding=false] - If true, only return tiles that collide
* on at least one side.
* @param {boolean} [filteringOptions.hasInterestingFace=false] - If true, only return tiles that
* have at least one interesting face.
* @param {LayerData} layer - [description]

View file

@ -3,8 +3,8 @@ var IsInLayerBounds = require('./IsInLayerBounds');
/**
* Gets a tile at the given tile coordinates from the given layer.
*
* @param {number} tileX - X position to get the tile from (given in tile units, not pixels)
* @param {number} tileY - Y position to get the tile from (given in tile units, not pixels)
* @param {integer} tileX - X position to get the tile from (given in tile units, not pixels).
* @param {integer} tileY - Y position to get the tile from (given in tile units, not pixels).
* @param {boolean} [nonNull=false] - If true getTile won't return null for empty tiles, but a Tile
* object with an index of -1.
* @param {LayerData} layer - [description]

View file

@ -1,14 +1,12 @@
var GetFastValue = require('../../../utils/object/GetFastValue');
/**
* Gets the tiles in the given rectangular area (in tile coordinates) of the layer.
*
* @param {number} [tileX=0] - [description]
* @param {number} [tileY=0] - [description]
* @param {number} [width=max width based on tileX] - [description]
* @param {number} [height=max height based on tileY] - [description]
* @param {integer} [tileX=0] - [description]
* @param {integer} [tileY=0] - [description]
* @param {integer} [width=max width based on tileX] - [description]
* @param {integer} [height=max height based on tileY] - [description]
* @param {object} [filteringOptions] - Optional filters to apply when getting the tiles.
* @param {boolean} [filteringOptions.isNotEmpty=false] - If true, only return tiles that don't have
* -1 for an index.
@ -17,7 +15,7 @@ var GetFastValue = require('../../../utils/object/GetFastValue');
* @param {boolean} [filteringOptions.hasInterestingFace=false] - If true, only return tiles that
* have at least one interesting face.
* @param {LayerData} layer - [description]
* @return {array} Array of Tile objects.
* @return {Tile[]} Array of Tile objects.
*/
var GetTilesWithin = function (tileX, tileY, width, height, filteringOptions, layer)
{

View file

@ -30,7 +30,7 @@ var TriangleToRectangle = function (triangle, rect)
* have at least one interesting face.
* @param {Camera} [camera=main camera] - [description]
* @param {LayerData} layer - [description]
* @return {array} Array of Tile objects.
* @return {Tile[]} Array of Tile objects.
*/
var GetTilesWithinShape = function (shape, filteringOptions, camera, layer)
{

View file

@ -18,7 +18,7 @@ var WorldToTileY = require('./WorldToTileY');
* have at least one interesting face.
* @param {Camera} [camera=main camera] - [description]
* @param {LayerData} layer - [description]
* @return {array} Array of Tile objects.
* @return {Tile[]} Array of Tile objects.
*/
var GetTilesWithinWorldXY = function (worldX, worldY, width, height, filteringOptions, camera, layer)
{

View file

@ -4,8 +4,8 @@ var IsInLayerBounds = require('./IsInLayerBounds');
* Checks if there is a tile at the given location (in tile coordinates) in the given layer. Returns
* false if there is no tile or if the tile at that location has an index of -1.
*
* @param {number} tileX - [description]
* @param {number} tileY - [description]
* @param {integer} tileX - [description]
* @param {integer} tileY - [description]
* @param {LayerData} layer - [description]
* @return {boolean}
*/

View file

@ -1,8 +1,8 @@
/**
* Checks if the given tile coordinates are within the bounds of the layer.
*
* @param {number} tileX - [description]
* @param {number} tileY - [description]
* @param {integer} tileX - [description]
* @param {integer} tileY - [description]
* @param {LayerData} layer - [description]
* @return {boolean}
*/

View file

@ -8,9 +8,9 @@ var RecalculateFacesAt = require('./RecalculateFacesAt');
* location. If you pass in an index, only the index at the specified location will be changed.
* Collision information will be recalculated at the specified location.
*
* @param {number|Tile} tile - The index of this tile to set or a Tile object.
* @param {number} tileX - [description]
* @param {number} tileY - [description]
* @param {integer|Tile} tile - The index of this tile to set or a Tile object.
* @param {integer} tileX - [description]
* @param {integer} tileY - [description]
* @param {boolean} [recalculateFaces=true] - [description]
* @param {LayerData} layer - [description]
* @return {Tile} The Tile object that was created or added to this map.

View file

@ -8,9 +8,9 @@ var WorldToTileY = require('./WorldToTileY');
* specified location. If you pass in an index, only the index at the specified location will be
* changed. Collision information will be recalculated at the specified location.
*
* @param {number|Tile} tile - The index of this tile to set or a Tile object.
* @param {number} worldX - [description]
* @param {number} worldY - [description]
* @param {integer|Tile} tile - The index of this tile to set or a Tile object.
* @param {integer} worldX - [description]
* @param {integer} worldY - [description]
* @param {boolean} [recalculateFaces=true] - [description]
* @param {Camera} [camera=main camera] - [description]
* @param {LayerData} layer - [description]

View file

@ -2,40 +2,40 @@ var GetTilesWithin = require('./GetTilesWithin');
var GetRandomElement = require('../../../utils/array/GetRandomElement');
/**
* Randomizes the indices of a rectangular region of tiles (in tile coordinates) within the
* specified layer. Each tile will recieve a new index. If an array of indices is passed in, then
* those will be used for randomly assigning new tile indices. If an array is not provided, the
* indices found within the region (excluding -1) will be used for randomly assigning new tile
* indices. This method only modifies tile indexes and does not change collision information.
* Randomizes the indexes of a rectangular region of tiles (in tile coordinates) within the
* specified layer. Each tile will recieve a new index. If an array of indexes is passed in, then
* those will be used for randomly assigning new tile indexes. If an array is not provided, the
* indexes found within the region (excluding -1) will be used for randomly assigning new tile
* indexes. This method only modifies tile indexes and does not change collision information.
*
* @param {number} [tileX=0] - [description]
* @param {number} [tileY=0] - [description]
* @param {number} [width=max width based on tileX] - [description]
* @param {number} [height=max height based on tileY] - [description]
* @param {array} [indices] - An array of indices to randomly draw from during randomization.
* @param {integer} [tileX=0] - [description]
* @param {integer} [tileY=0] - [description]
* @param {integer} [width=max width based on tileX] - [description]
* @param {integer} [height=max height based on tileY] - [description]
* @param {integer[]} [indexes] - An array of indexes to randomly draw from during randomization.
* @param {LayerData} layer - [description]
*/
var Randomize = function (tileX, tileY, width, height, indices, layer)
var Randomize = function (tileX, tileY, width, height, indexes, layer)
{
var i;
var tiles = GetTilesWithin(tileX, tileY, width, height, null, layer);
// If no indicies are given, then find all the unique indices within the specified region
if (indices === undefined)
// If no indicies are given, then find all the unique indexes within the specified region
if (indexes === undefined)
{
indices = [];
indexes = [];
for (i = 0; i < tiles.length; i++)
{
if (indices.indexOf(tiles[i].index) === -1)
if (indexes.indexOf(tiles[i].index) === -1)
{
indices.push(tiles[i].index);
indexes.push(tiles[i].index);
}
}
}
for (i = 0; i < tiles.length; i++)
{
tiles[i].index = GetRandomElement(indices);
tiles[i].index = GetRandomElement(indexes);
}
};

View file

@ -5,8 +5,8 @@ var GetTileAt = require('./GetTileAt');
* faces are used internally for optimizing collisions against tiles. This method is mostly used
* internally to optimize recalculating faces when only one tile has been changed.
*
* @param {number} tileX - [description]
* @param {number} tileY - [description]
* @param {integer} tileX - [description]
* @param {integer} tileY - [description]
* @param {LayerData} layer - [description]
*/
var RecalculateFacesAt = function (tileX, tileY, layer)

View file

@ -6,9 +6,9 @@ var RecalculateFacesAt = require('./RecalculateFacesAt');
* Removes the tile at the given tile coordinates in the specified layer and updates the layer's
* collision information.
*
* @param {number|Tile} tile - The index of this tile to set or a Tile object.
* @param {number} tileX - [description]
* @param {number} tileY - [description]
* @param {integer|Tile} tile - The index of this tile to set or a Tile object.
* @param {integer} tileX - [description]
* @param {integer} tileY - [description]
* @param {boolean} [replaceWithNull=true] - If true, this will replace the tile at the specified
* location with null instead of a Tile with an index of -1.
* @param {boolean} [recalculateFaces=true] - [description]

View file

@ -6,7 +6,7 @@ var WorldToTileY = require('./WorldToTileY');
* Removes the tile at the given world coordinates in the specified layer and updates the layer's
* collision information.
*
* @param {number|Tile} tile - The index of this tile to set or a Tile object.
* @param {integer|Tile} tile - The index of this tile to set or a Tile object.
* @param {number} worldX - [description]
* @param {number} worldY - [description]
* @param {boolean} [replaceWithNull=true] - If true, this will replace the tile at the specified

View file

@ -0,0 +1,70 @@
var GetTilesWithin = require('./GetTilesWithin');
var Color = require('../../../display/color');
/**
* Draws a debug representation of the layer to the given Graphics. This is helpful when you want to
* get a quick idea of which of your tiles are colliding and which have interesting faces. The tiles
* are drawn starting at (0, 0) in the Graphics, allowing you to place the debug representation
* wherever you want on the screen.
*
* @param {Graphics} graphics - The target Graphics object to draw upon.
* @param {object} styleConfig - An object specifying the colors to use for the debug drawing.
* @param {Color|null} [styleConfig.tileColor=blue] - Color to use for drawing a filled rectangle at
* non-colliding tile locations. If set to null, non-colliding tiles will not be drawn.
* @param {Color|null} [styleConfig.collidingTileColor=orange] - Color to use for drawing a filled
* rectangle at colliding tile locations. If set to null, colliding tiles will not be drawn.
* @param {Color|null} [styleConfig.faceColor=grey] - Color to use for drawing a line at interesting
* tile faces. If set to null, interesting tile faces will not be drawn.
* @param {LayerData} layer - [description]
*/
var RenderDebug = function (graphics, styleConfig, layer)
{
if (styleConfig === undefined) { styleConfig = {}; }
// Default colors without needlessly creating Color objects
var tileColor = styleConfig.tileColor !== undefined
? styleConfig.tileColor
: new Color(105, 210, 231, 150);
var collidingTileColor = styleConfig.collidingTileColor !== undefined
? styleConfig.collidingTileColor
: new Color(243, 134, 48, 200);
var faceColor = styleConfig.faceColor !== undefined
? styleConfig.faceColor
: new Color(40, 39, 37, 150);
var tiles = GetTilesWithin(0, 0, layer.width, layer.height, null, layer);
for (var i = 0; i < tiles.length; i++)
{
var tile = tiles[i];
var tw = tile.width;
var th = tile.height;
var x = tile.worldX;
var y = tile.worldY;
var color = tile.collides ? collidingTileColor : tileColor;
if (color !== null)
{
graphics.fillStyle(color.color, color.alpha / 255);
graphics.fillRect(x, y, tw, th);
}
// Inset the face line to prevent neighboring tile's lines from overlapping
x += 1;
y += 1;
tw -= 2;
th -= 2;
if (faceColor !== null)
{
graphics.lineStyle(1, faceColor.color, faceColor.alpha / 255);
if (tile.faceTop) { graphics.lineBetween(x, y, x + tw, y); }
if (tile.faceRight) { graphics.lineBetween(x + tw, y, x + tw, y + th); }
if (tile.faceBottom) { graphics.lineBetween(x, y + th, x + tw, y + th); }
if (tile.faceLeft) { graphics.lineBetween(x, y, x, y + th); }
}
}
};
module.exports = RenderDebug;

View file

@ -5,12 +5,12 @@ var GetTilesWithin = require('./GetTilesWithin');
* `findIndex` and updates their index to match `newIndex`. This only modifies the index and does
* not change collision information.
*
* @param {number} findIndex - [description]
* @param {number} newIndex - [description]
* @param {number} [tileX=0] - [description]
* @param {number} [tileY=0] - [description]
* @param {number} [width=max width based on tileX] - [description]
* @param {number} [height=max height based on tileY] - [description]
* @param {integer} findIndex - [description]
* @param {integer} newIndex - [description]
* @param {integer} [tileX=0] - [description]
* @param {integer} [tileY=0] - [description]
* @param {integer} [width=max width based on tileX] - [description]
* @param {integer} [height=max height based on tileY] - [description]
* @param {LayerData} layer - [description]
*/
var ReplaceByIndex = function (findIndex, newIndex, tileX, tileY, width, height, layer)

View file

@ -7,7 +7,7 @@ var SetLayerCollisionIndex = require('./SetLayerCollisionIndex');
* single numeric index or an array of indexes: [2, 3, 15, 20]. The `collides` parameter controls if
* collision will be enabled (true) or disabled (false).
*
* @param {number|array} indexes - Either a single tile index, or an array of tile indexes.
* @param {integer|array} indexes - Either a single tile index, or an array of tile indexes.
* @param {boolean} [collides=true] - If true it will enable collision. If false it will clear
* collision.
* @param {boolean} [recalculateFaces=true] - Whether or not to recalculate the tile faces after the

View file

@ -8,8 +8,8 @@ var SetLayerCollisionIndex = require('./SetLayerCollisionIndex');
* collision for tiles 10, 11, 12, 13 and 14. The `collides` parameter controls if collision will be
* enabled (true) or disabled (false).
*
* @param {number} start - The first index of the tile to be set for collision.
* @param {number} stop - The last index of the tile to be set for collision.
* @param {integer} start - The first index of the tile to be set for collision.
* @param {integer} stop - The last index of the tile to be set for collision.
* @param {boolean} [collides=true] - If true it will enable collision. If false it will clear
* collision.
* @param {boolean} [recalculateFaces=true] - Whether or not to recalculate the tile faces after the

View file

@ -7,7 +7,7 @@ var SetLayerCollisionIndex = require('./SetLayerCollisionIndex');
* the given array. The `collides` parameter controls if collision will be enabled (true) or
* disabled (false).
*
* @param {array} indexes - An array of the tile indexes to not be counted for collision.
* @param {integer[]} indexes - An array of the tile indexes to not be counted for collision.
* @param {boolean} [collides=true] - If true it will enable collision. If false it will clear
* collision.
* @param {boolean} [recalculateFaces=true] - Whether or not to recalculate the tile faces after the

View file

@ -2,7 +2,7 @@
* Internally used method to keep track of the tile indexes that collide within a layer. This
* updates LayerData.collideIndexes to either contain or not contain the given `tileIndex`.
*
* @param {number} tileIndex - [description]
* @param {integer} tileIndex - [description]
* @param {boolean} [collides=true] - [description]
* @param {LayerData} layer - [description]
*/

View file

@ -4,7 +4,7 @@
* will be replaced. Set the callback to null to remove it. If you want to set a callback for a tile
* at a specific location on the map then see setTileLocationCallback.
*
* @param {number|array} indexes - Either a single tile index, or an array of tile indexes to have a
* @param {integer|array} indexes - Either a single tile index, or an array of tile indexes to have a
* collision callback set for.
* @param {function} callback - The callback that will be invoked when the tile is collided with.
* @param {object} callbackContext - The context under which the callback is called.

View file

@ -5,10 +5,10 @@ var GetTilesWithin = require('./GetTilesWithin');
* If a callback is already set for the tile index it will be replaced. Set the callback to null to
* remove it.
*
* @param {number} [tileX=0] - [description]
* @param {number} [tileY=0] - [description]
* @param {number} [width=max width based on tileX] - [description]
* @param {number} [height=max height based on tileY] - [description]
* @param {integer} [tileX=0] - [description]
* @param {integer} [tileY=0] - [description]
* @param {integer} [width=max width based on tileX] - [description]
* @param {integer} [height=max height based on tileY] - [description]
* @param {function} callback - The callback that will be invoked when the tile is collided with.
* @param {object} callbackContext - The context under which the callback is called.
* @param {LayerData} layer - [description]

View file

@ -7,22 +7,22 @@ var ShuffleArray = require('../../../utils/array/Shuffle');
* appear to have changed! This method only modifies tile indexes and does not change collision
* information.
*
* @param {number} [tileX=0] - [description]
* @param {number} [tileY=0] - [description]
* @param {number} [width=max width based on tileX] - [description]
* @param {number} [height=max height based on tileY] - [description]
* @param {integer} [tileX=0] - [description]
* @param {integer} [tileY=0] - [description]
* @param {integer} [width=max width based on tileX] - [description]
* @param {integer} [height=max height based on tileY] - [description]
* @param {LayerData} layer - [description]
*/
var Shuffle = function (tileX, tileY, width, height, layer)
{
var tiles = GetTilesWithin(tileX, tileY, width, height, null, layer);
var indices = tiles.map(function (tile) { return tile.index; });
ShuffleArray(indices);
var indexes = tiles.map(function (tile) { return tile.index; });
ShuffleArray(indexes);
for (var i = 0; i < tiles.length; i++)
{
tiles[i].index = indices[i];
tiles[i].index = indexes[i];
}
};

View file

@ -5,12 +5,12 @@ var GetTilesWithin = require('./GetTilesWithin');
* `indexA` and swaps then with `indexB`. This only modifies the index and does not change collision
* information.
*
* @param {number} tileA - First tile index.
* @param {number} tileB - Second tile index.
* @param {number} [tileX=0] - [description]
* @param {number} [tileY=0] - [description]
* @param {number} [width=max width based on tileX] - [description]
* @param {number} [height=max height based on tileY] - [description]
* @param {integer} tileA - First tile index.
* @param {integer} tileB - Second tile index.
* @param {integer} [tileX=0] - [description]
* @param {integer} [tileY=0] - [description]
* @param {integer} [width=max width based on tileX] - [description]
* @param {integer} [height=max height based on tileY] - [description]
* @param {LayerData} layer - [description]
*/
var SwapByIndex = function (indexA, indexB, tileX, tileY, width, height, layer)

View file

@ -2,7 +2,7 @@
* Internally used method to convert from tile X coordinates to world X coordinates, factoring in
* layer position, scale and scroll.
*
* @param {number} tileX - [description]
* @param {integer} tileX - [description]
* @param {Camera} [camera=main camera] - [description]
* @param {LayerData} layer - [description]
* @returns {number}

View file

@ -2,7 +2,7 @@
* Internally used method to convert from tile Y coordinates to world Y coordinates, factoring in
* layer position, scale and scroll.
*
* @param {number} tileY - [description]
* @param {integer} tileY - [description]
* @param {Camera} [camera=main camera] - [description]
* @param {LayerData} layer - [description]
* @returns {number}

View file

@ -22,6 +22,7 @@ module.exports = {
RemoveTileAt: require('./RemoveTileAt'),
RemoveTileAtWorldXY: require('./RemoveTileAtWorldXY'),
ReplaceByIndex: require('./ReplaceByIndex'),
RenderDebug: require('./RenderDebug'),
SetCollision: require('./SetCollision'),
SetCollisionBetween: require('./SetCollisionBetween'),
SetCollisionByExclusion: require('./SetCollisionByExclusion'),

View file

@ -44,7 +44,7 @@ var DynamicTilemapLayer = new Class({
this.setPosition(x, y);
this.setSizeToFrame();
this.setOrigin();
this.setSize(this.map.tileWidth * this.layer.width, this.map.tileheight * this.layer.height);
this.setSize(this.layer.tileWidth * this.layer.width, this.layer.tileHeight * this.layer.height);
this.skipIndexZero = false;
},
@ -73,7 +73,8 @@ var DynamicTilemapLayer = new Class({
destroy: function ()
{
this.layer.tilemapLayer = undefined;
// Uninstall this layer only if it is still installed on the LayerData object
if (this.layer.tilemapLayer === this) { this.layer.tilemapLayer = undefined; }
this.map = undefined;
this.layer = undefined;
this.tileset = undefined;
@ -148,9 +149,9 @@ var DynamicTilemapLayer = new Class({
return TilemapComponents.PutTileAtWorldXY(tile, worldX, worldY, recalculateFaces, camera, this.layer);
},
randomize: function (tileX, tileY, width, height, indices)
randomize: function (tileX, tileY, width, height, indexes)
{
TilemapComponents.Randomize(tileX, tileY, width, height, indices, this.layer);
TilemapComponents.Randomize(tileX, tileY, width, height, indexes, this.layer);
return this;
},
@ -164,6 +165,12 @@ var DynamicTilemapLayer = new Class({
return TilemapComponents.RemoveTileAtWorldXY(worldX, worldY, replaceWithNull, recalculateFaces, camera, this.layer);
},
renderDebug: function (graphics, styleConfig)
{
TilemapComponents.RenderDebug(graphics, styleConfig, this.layer);
return this;
},
replaceByIndex: function (findIndex, newIndex, tileX, tileY, width, height)
{
TilemapComponents.ReplaceByIndex(findIndex, newIndex, tileX, tileY, width, height, this.layer);

View file

@ -5,18 +5,24 @@ var ParseTiledJSON = require('./parsetiledjson/');
var Formats = require('../Formats');
/**
* Parses raw data of a given Tilemap format into a new MapData object.
* Parses raw data of a given Tilemap format into a new MapData object. If no recognized data format
* is found, returns `null`. When loading from CSV or a 2D array, you should specify the tileWidth &
* tileHeight. When parsing from a map from Tiled, the tileWidth & tileHeight will be pulled from
* the map data.
*
* @param {string} name - The name of the tilemap, used to set the name on the MapData.
* @param {number} mapFormat - See ../Formats.js.
* @param {array|string|object} data - 2D array, CSV string or Tiled JSON object
* @param {number} tileWidth - Required for 2D array and CSV, but ignored for Tiled JSON.
* @param {number} tileHeight - Required for 2D array and CSV, but ignored for Tiled JSON.
* @param {boolean} [insertNull=false] - If true, instead of placing empty tiles at locations where
* the tile index is -1, this will place null. If you've a large sparsely populated map and the tile
* data doesn't need to change then setting this value to `true` will help with memory consumption.
* However if your map is small, or you need to update the tiles (perhaps the map dynamically
* changes during the game) then leave the default value set.
* @param {integer} mapFormat - See ../Formats.js.
* @param {integer[][]|string|object} data - 2D array, CSV string or Tiled JSON object.
* @param {integer} tileWidth - The width of a tile in pixels. Required for 2D array and CSV, but
* ignored for Tiled JSON.
* @param {integer} tileHeight - The height of a tile in pixels. Required for 2D array and CSV, but
* ignored for Tiled JSON.
* @param {boolean} insertNull - Controls how empty tiles, tiles with an index of -1, in the map
* data are handled. If `true`, empty locations will get a value of `null`. If `false`, empty
* location will get a Tile object with an index of -1. If you've a large sparsely populated map and
* the tile data doesn't need to change then setting this value to `true` will help with memory
* consumption. However if your map is small or you need to update the tiles dynamically, then leave
* the default value set.
*/
var Parse = function (name, mapFormat, data, tileWidth, tileHeight, insertNull)
{

View file

@ -3,7 +3,21 @@ var LayerData = require('../mapdata/LayerData');
var Formats = require('../Formats');
var Tile = require('../Tile');
var Parse2DArray = function (key, data, tileWidth, tileHeight, insertNull)
/**
* Parses a 2D array of tile indexes into a new MapData object with a single layer.
*
* @param {string} name - The name of the tilemap, used to set the name on the MapData.
* @param {integer[][]} data - 2D array, CSV string or Tiled JSON object.
* @param {integer} tileWidth - The width of a tile in pixels.
* @param {integer} tileHeight - The height of a tile in pixels.
* @param {boolean} insertNull - Controls how empty tiles, tiles with an index of -1, in the map
* data are handled. If `true`, empty locations will get a value of `null`. If `false`, empty
* location will get a Tile object with an index of -1. If you've a large sparsely populated map and
* the tile data doesn't need to change then setting this value to `true` will help with memory
* consumption. However if your map is small or you need to update the tiles dynamically, then leave
* the default value set.
*/
var Parse2DArray = function (name, data, tileWidth, tileHeight, insertNull)
{
var layerData = new LayerData({
tileWidth: tileWidth,
@ -11,7 +25,7 @@ var Parse2DArray = function (key, data, tileWidth, tileHeight, insertNull)
});
var mapData = new MapData({
name: key,
name: name,
tileWidth: tileWidth,
tileHeight: tileHeight,
format: Formats.TILEMAP_2D_ARRAY,

View file

@ -1,14 +1,28 @@
var Formats = require('../Formats');
var Parse2DArray = require('./Parse2DArray');
var ParseCSV = function (key, data, tileWidth, tileHeight, insertNull)
/**
* Parses a CSV string of tile indexes into a new MapData object with a single layer.
*
* @param {string} name - The name of the tilemap, used to set the name on the MapData.
* @param {string} data - CSV string of tile indexes.
* @param {integer} tileWidth - The width of a tile in pixels.
* @param {integer} tileHeight - The height of a tile in pixels.
* @param {boolean} insertNull - Controls how empty tiles, tiles with an index of -1, in the map
* data are handled. If `true`, empty locations will get a value of `null`. If `false`, empty
* location will get a Tile object with an index of -1. If you've a large sparsely populated map and
* the tile data doesn't need to change then setting this value to `true` will help with memory
* consumption. However if your map is small or you need to update the tiles dynamically, then leave
* the default value set.
*/
var ParseCSV = function (name, data, tileWidth, tileHeight, insertNull)
{
var array2D = data
.trim()
.split('\n')
.map(function (row) { return row.split(','); });
var map = Parse2DArray(key, array2D, tileWidth, tileHeight, insertNull);
var map = Parse2DArray(name, array2D, tileWidth, tileHeight, insertNull);
map.format = Formats.TILEMAP_CSV;
return map;

View file

@ -10,7 +10,19 @@ var ParseGID = require('./ParseGID');
var Base64Decode = require('./Base64Decode');
var ParseObject = require('./ParseObject');
var ParseJSONTiled = function (key, json, insertNull)
/**
* Parses a Tiled JSON object into a new MapData object.
*
* @param {string} name - The name of the tilemap, used to set the name on the MapData.
* @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. If `true`, empty locations will get a value of `null`. If `false`, empty
* location will get a Tile object with an index of -1. If you've a large sparsely populated map and
* the tile data doesn't need to change then setting this value to `true` will help with memory
* consumption. However if your map is small or you need to update the tiles dynamically, then leave
* the default value set.
*/
var ParseJSONTiled = function (name, json, insertNull)
{
if (json.orientation !== 'orthogonal')
{
@ -22,7 +34,7 @@ var ParseJSONTiled = function (key, json, insertNull)
var mapData = new MapData({
width: json.width,
height: json.height,
name: key,
name: name,
tileWidth: json.tilewidth,
tileHeight: json.tileheight,
orientation: json.orientation,
@ -139,7 +151,7 @@ var ParseJSONTiled = function (key, json, insertNull)
y: GetFastValue(curi, 'offsety', 0) + curi.y,
alpha: curi.opacity,
visible: curi.visible,
properties: GetFastValue(curi, "properties", {})
properties: GetFastValue(curi, 'properties', {})
});
}
@ -157,15 +169,15 @@ var ParseJSONTiled = function (key, json, insertNull)
if (set.image)
{
var newSet = new Tileset(set.name, set.firstgid, set.tilewidth, set.tileheight, set.margin, set.spacing, set.properties);
var newSet = new Tileset(set.name, set.firstgid, set.tilewidth, set.tileheight, set.margin, set.spacing);
// Properties stored per-tile in object with string indices starting at "0"
// Properties stored per-tile in object with string indexes starting at "0"
if (set.tileproperties)
{
newSet.tileProperties = set.tileproperties;
}
// Object & terrain shapes stored per-tile in object with string indices starting at "0"
// Object & terrain shapes stored per-tile in object with string indexes starting at "0"
if (set.tiles)
{
newSet.tileData = set.tiles;
@ -327,9 +339,11 @@ var ParseJSONTiled = function (key, json, insertNull)
sid = mapData.tiles[tile.index][2];
set = mapData.tilesets[sid];
// Ensure that a tile's size matches its tileset
tile.width = set.tileWidth;
tile.height = set.tileHeight;
// if that tile type has any properties, add them to the tile object
if (set.tileProperties && set.tileProperties[tile.index - set.firstgid])
{
tile.properties = Extend(tile.properties, set.tileProperties[tile.index - set.firstgid]);

View file

@ -36,7 +36,7 @@ var StaticTilemapLayer = new Class({
this.layer = tilemap.layers[layerIndex];
this.tileset = tileset;
// Link the layer data with this dynamic tilemap layer
// Link the layer data with this static tilemap layer
this.layer.tilemapLayer = this;
this.vbo = null;
@ -53,7 +53,7 @@ var StaticTilemapLayer = new Class({
this.setPosition(x, y);
this.setSizeToFrame();
this.setOrigin();
this.setSize(this.map.tileWidth * this.layer.width, this.map.tileHeight * this.layer.height);
this.setSize(this.layer.tileWidth * this.layer.width, this.layer.tileHeight * this.layer.height);
this.skipIndexZero = false;
@ -74,8 +74,8 @@ var StaticTilemapLayer = new Class({
var tileset = this.tileset;
var mapWidth = this.layer.width;
var mapHeight = this.layer.height;
var tileWidth = this.map.tileWidth;
var tileHeight = this.map.tileHeight;
var tileWidth = this.layer.tileWidth;
var tileHeight = this.layer.tileHeight;
var width = this.texture.source[0].width;
var height = this.texture.source[0].height;
var mapData = this.layer.data;
@ -221,7 +221,8 @@ var StaticTilemapLayer = new Class({
destroy: function ()
{
this.layer.tilemapLayer = undefined;
// Uninstall this layer only if it is still installed on the LayerData object
if (this.layer.tilemapLayer === this) { this.layer.tilemapLayer = undefined; }
this.map = undefined;
this.layer = undefined;
this.tileset = undefined;
@ -279,6 +280,12 @@ var StaticTilemapLayer = new Class({
return TilemapComponents.HasTileAtWorldXY(worldX, worldY, camera, this.layer);
},
renderDebug: function (graphics, styleConfig)
{
TilemapComponents.RenderDebug(graphics, styleConfig, this.layer);
return this;
},
setCollision: function (indexes, collides, recalculateFaces)
{
TilemapComponents.SetCollision(indexes, collides, recalculateFaces, this.layer);