phaser/src/gameobjects/tilemap/Tile.js

606 lines
19 KiB
JavaScript
Raw Normal View History

var Class = require('../../utils/Class');
var Components = require('../components');
2018-01-25 19:48:22 +00:00
var Rectangle = require('../../geom/rectangle');
var Tile = new Class({
Mixins: [
Components.Alpha,
Components.Flip,
Components.Visible
],
initialize:
2017-11-29 21:37:23 +00:00
/**
* 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.
* @param {integer} baseWidth - The base width a tile in the map (in pixels). Tiled maps support
* multiple tileset sizes within one map, but they are still placed at intervals of the base
* tile width.
* @param {integer} baseHeight - The base height of the tile in pixels (in pixels). Tiled maps
* support multiple tileset sizes within one map, but they are still placed at intervals of the
* base tile height.
2017-11-29 21:37:23 +00:00
*/
function Tile (layer, index, x, y, width, height, baseWidth, baseHeight)
{
2017-11-29 21:37:23 +00:00
/**
* The LayerData in the Tilemap data that this tile belongs to.
* @property {LayerData} layer
*/
this.layer = layer;
2017-11-29 21:37:23 +00:00
/**
* 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;
2017-11-29 21:37:23 +00:00
/**
* The x map coordinate of this tile in tile units.
* @property {integer} x
*/
this.x = x;
2017-11-29 21:37:23 +00:00
/**
* The y map coordinate of this tile in tile units.
* @property {integer} y
*/
this.y = y;
2017-11-29 21:37:23 +00:00
/**
* The width of the tile in pixels.
* @property {integer} width
*/
this.width = width;
2017-11-29 21:37:23 +00:00
/**
* The height of the tile in pixels.
* @property {integer} height
*/
this.height = height;
2017-11-29 21:37:23 +00:00
/**
* The map's base width of a tile in pixels. Tiled maps support multiple tileset sizes
* within one map, but they are still placed at intervals of the base tile size.
* @property {integer} baseWidth
*/
this.baseWidth = (baseWidth !== undefined) ? baseWidth : width;
/**
* The map's base height of a tile in pixels. Tiled maps support multiple tileset sizes
* within one map, but they are still placed at intervals of the base tile size.
* @property {integer} baseHeight
*/
this.baseHeight = (baseHeight !== undefined) ? baseHeight : height;
/**
* The x coordinate of the top left of this tile in pixels. This is relative to the top left
* of the layer this tile is being rendered within. This property does NOT factor in camera
* scroll, layer scale or layer position.
* @property {number} pixelX
*/
this.pixelX = 0;
/**
* The y coordinate of the top left of this tile in pixels. This is relative to the top left
* of the layer this tile is being rendered within. This property does NOT factor in camera
* scroll, layer scale or layer position.
* @property {number} pixelY
*/
this.pixelY = 0;
this.updatePixelXY();
2017-11-29 21:37:23 +00:00
/**
* Tile specific properties. These usually come from Tiled.
* @property {object} properties
*/
2017-11-16 19:09:07 +00:00
this.properties = {};
2017-11-29 21:37:23 +00:00
/**
* The rotation angle of this tile.
* @property {number} rotation
*/
this.rotation = 0;
2017-11-29 21:37:23 +00:00
/**
* Whether the tile should collide with any object on the left side.
* @property {boolean} collideLeft
*/
this.collideLeft = false;
2017-11-29 21:37:23 +00:00
/**
* Whether the tile should collide with any object on the right side.
* @property {boolean} collideRight
*/
this.collideRight = false;
2017-11-29 21:37:23 +00:00
/**
* Whether the tile should collide with any object on the top side.
* @property {boolean} collideUp
*/
this.collideUp = false;
2017-11-29 21:37:23 +00:00
/**
* Whether the tile should collide with any object on the bottom side.
* @property {boolean} collideDown
*/
this.collideDown = false;
2017-11-29 21:37:23 +00:00
/**
* Whether the tile's left edge is interesting for collisions.
* @property {boolean} faceLeft
*/
this.faceLeft = false;
2017-11-29 21:37:23 +00:00
/**
* Whether the tile's right edge is interesting for collisions.
* @property {boolean} faceRight
*/
this.faceRight = false;
2017-11-29 21:37:23 +00:00
/**
* Whether the tile's top edge is interesting for collisions.
* @property {boolean} faceTop
*/
this.faceTop = false;
2017-11-29 21:37:23 +00:00
/**
* Whether the tile's bottom edge is interesting for collisions.
* @property {boolean} faceBottom
*/
this.faceBottom = false;
2017-11-29 21:37:23 +00:00
/**
* Tile collision callback.
* @property {function} collisionCallback
*/
this.collisionCallback = null;
2017-11-29 21:37:23 +00:00
/**
* The context in which the collision callback will be called.
* @property {object} collisionCallbackContext
*/
this.collisionCallbackContext = this;
2017-11-29 21:37:23 +00:00
/**
* 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;
/**
* An empty object where physics-engine specific information (e.g. bodies) may be stored.
* @property {object} physics
*/
this.physics = {};
2017-11-16 19:09:07 +00:00
},
2017-11-29 21:37:23 +00:00
/**
* 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.pixelX || y < this.pixelY || x > this.right || y > this.bottom);
2017-11-29 21:37:23 +00:00
},
/**
* 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}
*/
2017-11-16 19:09:07 +00:00
copy: function (tile)
{
this.index = tile.index;
this.alpha = tile.alpha;
this.properties = tile.properties;
this.visible = tile.visible;
this.setFlip(tile.flipX, tile.flipY);
this.tint = tile.tint;
this.rotation = tile.rotation;
this.collideUp = tile.collideUp;
this.collideDown = tile.collideDown;
this.collideLeft = tile.collideLeft;
this.collideRight = tile.collideRight;
this.collisionCallback = tile.collisionCallback;
this.collisionCallbackContext = tile.collisionCallbackContext;
2017-11-16 19:09:07 +00:00
return this;
},
/**
* The collision group for this Tile, defined within the Tileset. This returns a reference to
* the collision group stored within the Tileset, so any modification of the returned object
* will impact all tiles that have the same index as this tile.
* @returns {object|null} tileset
*/
getCollisionGroup: function ()
{
return this.tileset ? this.tileset.getTileCollisionGroup(this.index) : null;
},
/**
* The tile data for this Tile, defined within the Tileset. This typically contains Tiled
* collision data, tile animations and terrain information. This returns a reference to the tile
* data stored within the Tileset, so any modification of the returned object will impact all
* tiles that have the same index as this tile.
* @returns {object|null} tileset
*/
getTileData: function ()
{
return this.tileset ? this.tileset.getTileData(this.index) : null;
},
/**
* Gets the world X position of the left side of the tile, factoring in the layer's position,
* scale and scroll.
* @param {Camera} [camera=main camera] - [description]
* @returns {number}
*/
getLeft: function (camera)
{
var tilemapLayer = this.tilemapLayer;
return tilemapLayer
? tilemapLayer.tileToWorldX(this.x, camera)
: this.x * this.baseWidth;
},
/**
* Gets the world X position of the right side of the tile, factoring in the layer's position,
* scale and scroll.
* @param {Camera} [camera=main camera] - [description]
* @returns {number}
*/
getRight: function (camera)
{
var tilemapLayer = this.tilemapLayer;
return tilemapLayer
? this.getLeft(camera) + this.width * tilemapLayer.scaleX
: this.getLeft(camera) + this.width;
},
/**
* Gets the world Y position of the top side of the tile, factoring in the layer's position,
* scale and scroll.
* @param {Camera} [camera=main camera] - [description]
* @returns {number}
*/
getTop: function (camera)
{
var tilemapLayer = this.tilemapLayer;
// Tiled places tiles on a grid of baseWidth x baseHeight. The origin for a tile in grid
// units is the bottom left, so the y coordinate needs to be adjusted by the difference
// between the base size and this tile's size.
return tilemapLayer
? tilemapLayer.tileToWorldY(this.y, camera) - (this.height - this.baseHeight) * tilemapLayer.scaleY
: this.y * this.baseHeight - (this.height - this.baseHeight);
},
/**
* Gets the world Y position of the bottom side of the tile, factoring in the layer's position,
* scale and scroll.
* @param {Camera} [camera=main camera] - [description]
* @returns {number}
*/
getBottom: function (camera)
{
var tilemapLayer = this.tilemapLayer;
return tilemapLayer
? this.getTop(camera) + this.height * tilemapLayer.scaleY
: this.getTop(camera) + this.height;
},
2018-01-25 19:48:22 +00:00
/**
* Gets the world rectangle bounding box for the tile, factoring in the layer's position,
* scale and scroll.
* @param {Camera} [camera=main camera] - [description]
* @param {object} [output] - [description]
* @returns {Phaser.Geom.Rectangle|object}
*/
getBounds: function (camera, output)
{
if (output === undefined) { output = new Rectangle(); }
output.x = this.getLeft();
output.y = this.getTop();
output.width = this.getRight() - output.x;
output.height = this.getBottom() - output.y;
return output;
},
/**
* Gets the world X position of the center of the tile, factoring in the layer's position,
* scale and scroll.
* @param {Camera} [camera=main camera] - [description]
* @returns {number}
*/
getCenterX: function (camera)
{
return this.getLeft(camera) + this.width / 2;
},
/**
* Gets the world Y position of the center of the tile, factoring in the layer's position,
* scale and scroll.
* @param {Camera} [camera=main camera] - [description]
* @returns {number}
*/
getCenterY: function (camera)
{
return this.getTop(camera) + this.height / 2;
},
2017-11-29 21:37:23 +00:00
/**
* Clean up memory.
*/
destroy: function ()
{
this.collisionCallback = undefined;
this.collisionCallbackContext = undefined;
this.properties = undefined;
},
2017-11-29 21:37:23 +00:00
/**
* 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)
{
2017-11-29 21:37:23 +00:00
return !(
right <= this.pixelX || bottom <= this.pixelY ||
2017-11-29 21:37:23 +00:00
x >= this.right || y >= this.bottom
);
},
2017-11-29 21:37:23 +00:00
/**
* 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); }
else if (collides) { return this.collides; }
else if (faces) { return this.hasInterestingFace; }
return false;
},
2017-11-29 21:37:23 +00:00
/**
* Reset collision status flags.
*
* @returns {this}
*/
resetCollision: function ()
{
this.collideLeft = false;
this.collideRight = false;
this.collideUp = false;
this.collideDown = false;
this.faceTop = false;
this.faceBottom = false;
this.faceLeft = false;
this.faceRight = false;
2017-11-29 21:37:23 +00:00
return this;
},
/**
* Reset faces.
*
* @returns {this}
*/
resetFaces: function ()
{
this.faceTop = false;
this.faceBottom = false;
this.faceLeft = false;
this.faceRight = false;
return this;
},
2017-11-29 21:37:23 +00:00
/**
* 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; }
if (up === undefined) { up = left; }
if (down === undefined) { down = left; }
this.collideLeft = left;
this.collideRight = right;
this.collideUp = up;
this.collideDown = down;
this.faceLeft = left;
this.faceRight = right;
this.faceTop = up;
this.faceBottom = down;
2017-11-29 21:37:23 +00:00
return this;
},
2017-11-29 21:37:23 +00:00
/**
* 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)
{
2017-11-29 21:37:23 +00:00
if (callback === null)
{
2017-11-29 14:20:24 +00:00
this.collisionCallback = undefined;
this.collisionCallbackContext = undefined;
}
else
{
this.collisionCallback = callback;
this.collisionCallbackContext = context;
}
2017-11-29 21:37:23 +00:00
return this;
},
2017-11-29 21:37:23 +00:00
/**
* Sets the size of the tile and updates its pixelX and pixelY.
2017-11-29 21:37:23 +00:00
*
* @param {integer} tileWidth - The width of the tile in pixels.
* @param {integer} tileHeight - The height of the tile in pixels.
* @param {integer} baseWidth - The base width a tile in the map (in pixels).
* @param {integer} baseHeight - The base height of the tile in pixels (in pixels).
* @returns {this}
*/
setSize: function (tileWidth, tileHeight, baseWidth, baseHeight)
{
if (tileWidth !== undefined) { this.width = tileWidth; }
if (tileHeight !== undefined) { this.height = tileHeight; }
if (baseWidth !== undefined) { this.baseWidth = baseWidth; }
if (baseHeight !== undefined) { this.baseHeight = baseHeight; }
this.updatePixelXY();
return this;
},
/**
* Used internally. Updates the tile's world XY position based on the current tile size.
*
2017-11-29 21:37:23 +00:00
* @returns {this}
*/
updatePixelXY: function ()
{
// Tiled places tiles on a grid of baseWidth x baseHeight. The origin for a tile is the
// bottom left, while the Phaser renderer assumes the origin is the top left. The y
// coordinate needs to be adjusted by the difference.
this.pixelX = this.x * this.baseWidth;
this.pixelY = this.y * this.baseHeight - (this.height - this.baseHeight);
2017-11-29 21:37:23 +00:00
return this;
},
2017-11-29 21:37:23 +00:00
/**
* True if this tile can collide on any of its faces or has a collision callback set.
* @property {boolean} canCollide
* @readonly
*/
canCollide: {
get: function ()
{
return (this.collideLeft || this.collideRight || this.collideUp || this.collideDown || this.collisionCallback);
}
},
2017-11-29 21:37:23 +00:00
/**
* True if this tile can collide on any of its faces.
* @property {boolean} canCollide
* @readonly
*/
collides: {
get: function ()
{
return (this.collideLeft || this.collideRight || this.collideUp || this.collideDown);
}
},
2017-11-29 21:37:23 +00:00
/**
* True if this tile has any interesting faces.
* @property {boolean} canCollide
* @readonly
*/
hasInterestingFace: {
get: function ()
{
return (this.faceTop || this.faceBottom || this.faceLeft || this.faceRight);
}
},
/**
* The tileset that contains this Tile. This will only return null if accessed from a LayerData
* instance before the tile is placed within a StaticTilemapLayer or DynamicTilemapLayer.
* @property {Tileset|null} tileset
* @readonly
*/
tileset: {
get: function ()
{
var tilemapLayer = this.tilemapLayer;
return tilemapLayer ? tilemapLayer.tileset : null;
}
},
/**
* The tilemap layer that contains this Tile. This will only return null if accessed from a
* LayerData instance before the tile is placed within a StaticTilemapLayer or
* DynamicTilemapLayer.
* @property {StaticTilemapLayer|DynamicTilemapLayer|null} tilemapLayer
* @readonly
*/
tilemapLayer: {
get: function ()
{
return this.layer.tilemapLayer;
}
2018-01-25 13:25:14 +00:00
},
/**
* The tilemap that contains this Tile. This will only return null if accessed from a LayerData
* instance before the tile is placed within a StaticTilemapLayer or DynamicTilemapLayer.
* @property {Tilemap|null} tilemap
* @readonly
*/
tilemap: {
get: function ()
{
var tilemapLayer = this.tilemapLayer;
return tilemapLayer ? tilemapLayer.tilemap : null;
2018-01-25 13:25:14 +00:00
}
}
});
module.exports = Tile;