From dbaf7269e9b167c60e02c1b12a02d31ffdfce9bb Mon Sep 17 00:00:00 2001 From: Richard Davey Date: Thu, 12 Sep 2013 04:24:01 +0100 Subject: [PATCH] Promoted the Tilemap to a DisplayObject and vastly simplified the load process. --- examples/mario.php | 22 +- examples/tilemap.php | 25 +- src/core/Game.js | 3 +- src/core/Group.js | 23 +- src/gameobjects/GameObjectFactory.js | 6 + src/loader/Cache.js | 37 ++ src/loader/Loader.js | 108 ++++- src/tilemap/Tilemap.js | 667 ++++++++++++++------------- src/tilemap/TilemapLayer.js | 42 +- src/tilemap/TilemapRenderer.js | 5 +- 10 files changed, 541 insertions(+), 397 deletions(-) diff --git a/examples/mario.php b/examples/mario.php index 6205bd1c..c05900fd 100644 --- a/examples/mario.php +++ b/examples/mario.php @@ -16,38 +16,20 @@ function preload() { - // First we load our map data (a csv file) - game.load.text('marioMap', 'assets/maps/mario1.json'); - - // Then we load the actual tile sheet image - game.load.image('marioTiles', 'assets/maps/mario1.png'); + game.load.tilemap('mario', 'assets/maps/mario1.png', 'assets/maps/mario1.json', null, Phaser.Tilemap.JSON); } - var r; - var t; - function create() { game.stage.backgroundColor = '#787878'; - // game, key, mapData, format, resizeWorld, tileWidth, tileHeight - - // This creates the tilemap using the csv and tile sheet we loaded. - // We tell it use to CSV format parser. The 16x16 are the tile sizes. - // The 4th parameter (true) tells the game world to resize itself based on the map dimensions or not. - - t = new Phaser.Tilemap(game, 'marioTiles', 'marioMap', Phaser.Tilemap.FORMAT_TILED_JSON); - - // SHould be added to the World and rendered automatically :) - r = new Phaser.TilemapRenderer(game); + game.add.tilemap(0, 0, 'mario'); } function update() { - r.render(t); - if (game.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { game.camera.x -= 8; diff --git a/examples/tilemap.php b/examples/tilemap.php index f3417566..b37e1945 100644 --- a/examples/tilemap.php +++ b/examples/tilemap.php @@ -16,43 +16,22 @@ function preload() { - // CSV Tilemap Test - - // First we load our map data (a csv file) - game.load.text('csvtest', 'assets/maps/catastrophi_level2.csv'); - - // Then we load the actual tile sheet image - game.load.image('csvtiles', 'assets/tiles/catastrophi_tiles_16.png'); + game.load.tilemap('catastrophi', 'assets/tiles/catastrophi_tiles_16.png', 'assets/maps/catastrophi_level2.csv', null, Phaser.Tilemap.CSV); } - var canvas; - var context; - var baseTexture; - var texture; - var s; - var r; - var t; - function create() { - // game, key, mapData, format, resizeWorld, tileWidth, tileHeight - // This creates the tilemap using the csv and tile sheet we loaded. // We tell it use to CSV format parser. The 16x16 are the tile sizes. // The 4th parameter (true) tells the game world to resize itself based on the map dimensions or not. - t = new Phaser.Tilemap(game, 'csvtiles', 'csvtest', Phaser.Tilemap.FORMAT_CSV, true, 16, 16); - - // SHould be added to the World and rendered automatically :) - r = new Phaser.TilemapRenderer(game); + game.add.tilemap(0, 0, 'catastrophi', true, 16, 16); } function update() { - r.render(t); - if (game.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { game.camera.x -= 8; diff --git a/src/core/Game.js b/src/core/Game.js index 65134796..b10bb857 100644 --- a/src/core/Game.js +++ b/src/core/Game.js @@ -351,7 +351,8 @@ Phaser.Game.prototype = { } else { - // They must have requested WebGL and their browser supports it + // They requested WebGL, and their browser supports it + this.renderType = Phaser.WEBGL; this.renderer = new PIXI.WebGLRenderer(this.width, this.height, this.stage.canvas, this.transparent, this.antialias); this.canvas = this.renderer.view; this.context = null; diff --git a/src/core/Group.js b/src/core/Group.js index 45fe9e0b..86681283 100644 --- a/src/core/Group.js +++ b/src/core/Group.js @@ -52,7 +52,12 @@ Phaser.Group.prototype = { if (child.group !== this) { child.group = this; - child.events.onAddedToGroup.dispatch(child, this); + + if (child.events) + { + child.events.onAddedToGroup.dispatch(child, this); + } + this._container.addChild(child); } @@ -65,7 +70,12 @@ Phaser.Group.prototype = { if (child.group !== this) { child.group = this; - child.events.onAddedToGroup.dispatch(child, this); + + if (child.events) + { + child.events.onAddedToGroup.dispatch(child, this); + } + this._container.addChildAt(child, index); } @@ -82,9 +92,16 @@ Phaser.Group.prototype = { create: function (x, y, key, frame) { var child = new Phaser.Sprite(this.game, x, y, key, frame); + child.group = this; - child.events.onAddedToGroup.dispatch(child, this); + + if (child.events) + { + child.events.onAddedToGroup.dispatch(child, this); + } + this._container.addChild(child); + return child; }, diff --git a/src/gameobjects/GameObjectFactory.js b/src/gameobjects/GameObjectFactory.js index 8abd4c5b..c1454738 100644 --- a/src/gameobjects/GameObjectFactory.js +++ b/src/gameobjects/GameObjectFactory.js @@ -108,6 +108,12 @@ Phaser.GameObjectFactory.prototype = { }, + tilemap: function (x, y, key, resizeWorld, tileWidth, tileHeight) { + + return this.world.group.add(new Phaser.Tilemap(this.game, key, resizeWorld, tileWidth, tileHeight)); + + }, + renderTexture: function (key, width, height) { var texture = new Phaser.RenderTexture(this.game, key, width, height); diff --git a/src/loader/Cache.js b/src/loader/Cache.js index 86627538..0d924473 100644 --- a/src/loader/Cache.js +++ b/src/loader/Cache.js @@ -47,6 +47,12 @@ Phaser.Cache = function (game) { */ this._text = {}; + /** + * Tilemap key-value container. + * @type {object} + */ + this._tilemaps = {}; + this.addDefaultImage(); this.onSoundUnlock = new Phaser.Signal; @@ -100,6 +106,22 @@ Phaser.Cache.prototype = { }, + /** + * Add a new tilemap. + * @param key {string} Asset key for the texture atlas. + * @param url {string} URL of this texture atlas file. + * @param data {object} Extra texture atlas data. + * @param atlasData {object} Texture atlas frames data. + */ + addTilemap: function (key, url, data, mapData, format) { + + this._tilemaps[key] = { url: url, data: data, spriteSheet: true, mapData: mapData, format: format }; + + PIXI.BaseTextureCache[key] = new PIXI.BaseTexture(data); + PIXI.TextureCache[key] = new PIXI.Texture(PIXI.BaseTextureCache[key]); + + }, + /** * Add a new texture atlas. * @param key {string} Asset key for the texture atlas. @@ -315,6 +337,21 @@ Phaser.Cache.prototype = { return null; }, + /** + * Get tilemap data by key. + * @param key Asset key of the tilemap you want. + * @return {object} The tilemap data. The tileset image is in the data property, the map data in mapData. + */ + getTilemap: function (key) { + + if (this._tilemaps[key]) + { + return this._tilemaps[key]; + } + + return null; + }, + /** * Get frame data by key. * @param key Asset key of the frame data you want. diff --git a/src/loader/Loader.js b/src/loader/Loader.js index e5b1c2b6..4503c36c 100644 --- a/src/loader/Loader.js +++ b/src/loader/Loader.js @@ -217,6 +217,52 @@ Phaser.Loader.prototype = { }, + /** + * Add a new tilemap loading request. + * @param key {string} Unique asset key of the tilemap data. + * @param tilesetURL {string} The url of the tile set image file. + * @param [mapDataURL] {string} The url of the map data file (csv/json) + * @param [mapData] {object} An optional JSON data object (can be given in place of a URL). + * @param [format] {string} The format of the map data. + */ + tilemap: function (key, tilesetURL, mapDataURL, mapData, format) { + + if (typeof mapDataURL === "undefined") { mapDataURL = null; } + if (typeof mapData === "undefined") { mapData = null; } + if (typeof format === "undefined") { format = Phaser.Tilemap.CSV; } + + if (this.checkKeyExists(key) === false) + { + // A URL to a json/csv file has been given + if (mapDataURL) + { + this.addToFileList('tilemap', key, tilesetURL, { mapDataURL: mapDataURL, format: format }); + } + else + { + switch (format) + { + // A csv string or object has been given + case Phaser.Tilemap.CSV: + break; + + // An xml string or object has been given + case Phaser.Tilemap.JSON: + + if (typeof mapData === 'string') + { + mapData = JSON.parse(mapData); + } + break; + } + + this.addToFileList('tilemap', key, tilesetURL, { mapDataURL: null, mapData: mapData, format: format }); + + } + } + + }, + /** * Add a new bitmap font loading request. * @param key {string} Unique asset key of the bitmap font. @@ -437,6 +483,7 @@ Phaser.Loader.prototype = { case 'spritesheet': case 'textureatlas': case 'bitmapfont': + case 'tilemap': file.data = new Image(); file.data.name = file.key; file.data.onload = function () { @@ -572,14 +619,50 @@ Phaser.Loader.prototype = { switch (file.type) { case 'image': + this.game.cache.addImage(file.key, file.url, file.data); break; case 'spritesheet': + this.game.cache.addSpriteSheet(file.key, file.url, file.data, file.frameWidth, file.frameHeight, file.frameMax); break; + case 'tilemap': + + if (file.mapDataURL == null) + { + this.game.cache.addTilemap(file.key, file.url, file.data, file.mapData, file.format); + } + else + { + // Load the JSON or CSV before carrying on with the next file + loadNext = false; + this._xhr.open("GET", this.baseURL + file.mapDataURL, true); + this._xhr.responseType = "text"; + + if (file.format == Phaser.Tilemap.JSON) + { + this._xhr.onload = function () { + return _this.jsonLoadComplete(file.key); + }; + } + else if (file.format == Phaser.Tilemap.CSV) + { + this._xhr.onload = function () { + return _this.csvLoadComplete(file.key); + }; + } + + this._xhr.onerror = function () { + return _this.dataLoadError(file.key); + }; + this._xhr.send(); + } + break; + case 'textureatlas': + if (file.atlasURL == null) { this.game.cache.addTextureAtlas(file.key, file.url, file.data, file.atlasData, file.format); @@ -612,6 +695,7 @@ Phaser.Loader.prototype = { break; case 'bitmapfont': + if (file.xmlURL == null) { this.game.cache.addBitmapFont(file.key, file.url, file.data, file.xmlData); @@ -686,7 +770,29 @@ Phaser.Loader.prototype = { var data = JSON.parse(this._xhr.response); var file = this._fileList[key]; - this.game.cache.addTextureAtlas(file.key, file.url, file.data, data, file.format); + if (file.type == 'tilemap') + { + this.game.cache.addTilemap(file.key, file.url, file.data, data, file.format); + } + else + { + this.game.cache.addTextureAtlas(file.key, file.url, file.data, data, file.format); + } + + this.nextFile(key, true); + + }, + + /** + * Successfully loaded a CSV file. + * @param key {string} Key of the loaded CSV file. + */ + csvLoadComplete: function (key) { + + var data = this._xhr.response; + var file = this._fileList[key]; + + this.game.cache.addTilemap(file.key, file.url, file.data, data, file.format); this.nextFile(key, true); diff --git a/src/tilemap/Tilemap.js b/src/tilemap/Tilemap.js index a05861a8..cfae0a84 100644 --- a/src/tilemap/Tilemap.js +++ b/src/tilemap/Tilemap.js @@ -12,12 +12,12 @@ * @param game {Phaser.Game} Current game instance. * @param key {string} Asset key for this map. * @param mapData {string} Data of this map. (a big 2d array, normally in csv) -* @param format {number} Format of this map data, available: Tilemap.FORMAT_CSV or Tilemap.FORMAT_TILED_JSON. +* @param format {number} Format of this map data, available: Tilemap.CSV or Tilemap.JSON. * @param resizeWorld {bool} Resize the world bound automatically based on this tilemap? -* @param tileWidth {number} Width of tiles in this map. -* @param tileHeight {number} Height of tiles in this map. +* @param tileWidth {number} Width of tiles in this map (used for CSV maps). +* @param tileHeight {number} Height of tiles in this map (used for CSV maps). */ -Phaser.Tilemap = function (game, key, mapData, format, resizeWorld, tileWidth, tileHeight) { +Phaser.Tilemap = function (game, key, resizeWorld, tileWidth, tileHeight) { if (typeof resizeWorld === "undefined") { resizeWorld = true; } if (typeof tileWidth === "undefined") { tileWidth = 0; } @@ -26,11 +26,7 @@ Phaser.Tilemap = function (game, key, mapData, format, resizeWorld, tileWidth, t this.game = game; this.group = null; this.name = ''; - - /** - * z order value of the object. - */ - this.z = -1; + this.key = key; /** * Render iteration counter @@ -48,16 +44,24 @@ Phaser.Tilemap = function (game, key, mapData, format, resizeWorld, tileWidth, t this.tiles = []; this.layers = []; - this.mapFormat = format; - switch (format) + var map = this.game.cache.getTilemap(key); + + // this._container = new PIXI.DisplayObjectContainer(); + PIXI.DisplayObjectContainer.call(this); + + this.renderer = new Phaser.TilemapRenderer(this.game); + + this.mapFormat = map.format; + + switch (this.mapFormat) { - case Phaser.Tilemap.FORMAT_CSV: - this.parseCSV(game.cache.getText(mapData), key, tileWidth, tileHeight); + case Phaser.Tilemap.CSV: + this.parseCSV(map.mapData, key, tileWidth, tileHeight); break; - case Phaser.Tilemap.FORMAT_TILED_JSON: - this.parseTiledJSON(game.cache.getText(mapData), key); + case Phaser.Tilemap.JSON: + this.parseTiledJSON(map.mapData, key); break; } @@ -68,343 +72,342 @@ Phaser.Tilemap = function (game, key, mapData, format, resizeWorld, tileWidth, t }; -Phaser.Tilemap.FORMAT_CSV = 0; -Phaser.Tilemap.FORMAT_TILED_JSON = 1; +// Needed to keep the PIXI.Sprite constructor in the prototype chain (as the core pixi renderer uses an instanceof check sadly) +Phaser.Tilemap.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); +Phaser.Tilemap.prototype.constructor = Phaser.Tilemap; -Phaser.Tilemap.prototype = { +Phaser.Tilemap.CSV = 0; +Phaser.Tilemap.JSON = 1; - /** - * Parset csv map data and generate tiles. - * @param data {string} CSV map data. - * @param key {string} Asset key for tileset image. - * @param tileWidth {number} Width of its tile. - * @param tileHeight {number} Height of its tile. - */ - parseCSV: function (data, key, tileWidth, tileHeight) { +/** +* Parset csv map data and generate tiles. +* @param data {string} CSV map data. +* @param key {string} Asset key for tileset image. +* @param tileWidth {number} Width of its tile. +* @param tileHeight {number} Height of its tile. +*/ +Phaser.Tilemap.prototype.parseCSV = function (data, key, tileWidth, tileHeight) { - var layer = new Phaser.TilemapLayer(this, 0, key, Phaser.Tilemap.FORMAT_CSV, 'TileLayerCSV' + this.layers.length.toString(), tileWidth, tileHeight); + var layer = new Phaser.TilemapLayer(this, 0, key, Phaser.Tilemap.CSV, 'TileLayerCSV' + this.layers.length.toString(), tileWidth, tileHeight); - // Trim any rogue whitespace from the data - data = data.trim(); + // Trim any rogue whitespace from the data + data = data.trim(); - var rows = data.split("\n"); + var rows = data.split("\n"); - for (var i = 0; i < rows.length; i++) + for (var i = 0; i < rows.length; i++) + { + var column = rows[i].split(","); + + if (column.length > 0) { - var column = rows[i].split(","); + layer.addColumn(column); + } + } - if (column.length > 0) + layer.updateBounds(); + layer.createCanvas(); + + var tileQuantity = layer.parseTileOffsets(); + + this.currentLayer = layer; + this.collisionLayer = layer; + this.layers.push(layer); + + this.generateTiles(tileQuantity); + +}; + +/** +* Parse JSON map data and generate tiles. +* @param data {string} JSON map data. +* @param key {string} Asset key for tileset image. +*/ +Phaser.Tilemap.prototype.parseTiledJSON = function (json, key) { + + for (var i = 0; i < json.layers.length; i++) + { + var layer = new Phaser.TilemapLayer(this, i, key, Phaser.Tilemap.JSON, json.layers[i].name, json.tilewidth, json.tileheight); + + // Check it's a data layer + if (!json.layers[i].data) + { + continue; + } + + layer.alpha = json.layers[i].opacity; + layer.visible = json.layers[i].visible; + layer.tileMargin = json.tilesets[0].margin; + layer.tileSpacing = json.tilesets[0].spacing; + + var c = 0; + var row; + + for (var t = 0; t < json.layers[i].data.length; t++) + { + if (c == 0) { - layer.addColumn(column); + row = []; + } + + row.push(json.layers[i].data[t]); + c++; + + if (c == json.layers[i].width) + { + layer.addColumn(row); + c = 0; } } layer.updateBounds(); layer.createCanvas(); - + var tileQuantity = layer.parseTileOffsets(); - + this.currentLayer = layer; this.collisionLayer = layer; this.layers.push(layer); - - this.generateTiles(tileQuantity); - - }, - - /** - * Parse JSON map data and generate tiles. - * @param data {string} JSON map data. - * @param key {string} Asset key for tileset image. - */ - parseTiledJSON: function (data, key) { - - // Trim any rogue whitespace from the data - data = data.trim(); - - var json = JSON.parse(data); - - for (var i = 0; i < json.layers.length; i++) - { - var layer = new Phaser.TilemapLayer(this, i, key, Phaser.Tilemap.FORMAT_TILED_JSON, json.layers[i].name, json.tilewidth, json.tileheight); - - // Check it's a data layer - if (!json.layers[i].data) - { - continue; - } - - layer.alpha = json.layers[i].opacity; - layer.visible = json.layers[i].visible; - layer.tileMargin = json.tilesets[0].margin; - layer.tileSpacing = json.tilesets[0].spacing; - - var c = 0; - var row; - - for (var t = 0; t < json.layers[i].data.length; t++) - { - if (c == 0) - { - row = []; - } - - row.push(json.layers[i].data[t]); - c++; - - if (c == json.layers[i].width) - { - layer.addColumn(row); - c = 0; - } - } - - layer.updateBounds(); - layer.createCanvas(); - - var tileQuantity = layer.parseTileOffsets(); - - this.currentLayer = layer; - this.collisionLayer = layer; - this.layers.push(layer); - } - - this.generateTiles(tileQuantity); - - }, - - /** - * Create tiles of given quantity. - * @param qty {number} Quentity of tiles to be generated. - */ - generateTiles: function (qty) { - - for (var i = 0; i < qty; i++) - { - this.tiles.push(new Phaser.Tile(this.game, this, i, this.currentLayer.tileWidth, this.currentLayer.tileHeight)); - } - - }, - - /** - * Set callback to be called when this tilemap collides. - * @param context {object} Callback will be called with this context. - * @param callback {function} Callback function. - */ - setCollisionCallback: function (context, callback) { - - this.collisionCallbackContext = context; - this.collisionCallback = callback; - - }, - - /** - * Set collision configs of tiles in a range index. - * @param start {number} First index of tiles. - * @param end {number} Last index of tiles. - * @param collision {number} Bit field of flags. (see Tile.allowCollision) - * @param resetCollisions {bool} Reset collision flags before set. - * @param separateX {bool} Enable seprate at x-axis. - * @param separateY {bool} Enable seprate at y-axis. - */ - setCollisionRange: function (start, end, collision, resetCollisions, separateX, separateY) { - - if (typeof collision === "undefined") { collision = Phaser.Types.ANY; } - if (typeof resetCollisions === "undefined") { resetCollisions = false; } - if (typeof separateX === "undefined") { separateX = true; } - if (typeof separateY === "undefined") { separateY = true; } - - for (var i = start; i < end; i++) - { - this.tiles[i].setCollision(collision, resetCollisions, separateX, separateY); - } - - }, - - /** - * Set collision configs of tiles with given index. - * @param values {number[]} Index array which contains all tile indexes. The tiles with those indexes will be setup with rest parameters. - * @param collision {number} Bit field of flags. (see Tile.allowCollision) - * @param resetCollisions {bool} Reset collision flags before set. - * @param separateX {bool} Enable seprate at x-axis. - * @param separateY {bool} Enable seprate at y-axis. - */ - setCollisionByIndex: function (values, collision, resetCollisions, separateX, separateY) { - - if (typeof collision === "undefined") { collision = Phaser.Types.ANY; } - if (typeof resetCollisions === "undefined") { resetCollisions = false; } - if (typeof separateX === "undefined") { separateX = true; } - if (typeof separateY === "undefined") { separateY = true; } - - for (var i = 0; i < values.length; i++) - { - this.tiles[values[i]].setCollision(collision, resetCollisions, separateX, separateY); - } - - }, - - // Tile Management - - /** - * Get the tile by its index. - * @param value {number} Index of the tile you want to get. - * @return {Tile} The tile with given index. - */ - getTileByIndex: function (value) { - - if (this.tiles[value]) - { - return this.tiles[value]; - } - - return null; - - }, - - /** - * Get the tile located at specific position and layer. - * @param x {number} X position of this tile located. - * @param y {number} Y position of this tile located. - * @param [layer] {number} layer of this tile located. - * @return {Tile} The tile with specific properties. - */ - getTile: function (x, y, layer) { - - if (typeof layer === "undefined") { layer = this.currentLayer.ID; } - - return this.tiles[this.layers[layer].getTileIndex(x, y)]; - - }, - - /** - * Get the tile located at specific position (in world coordinate) and layer. (thus you give a position of a point which is within the tile) - * @param x {number} X position of the point in target tile. - * @param x {number} Y position of the point in target tile. - * @param [layer] {number} layer of this tile located. - * @return {Tile} The tile with specific properties. - */ - getTileFromWorldXY: function (x, y, layer) { - - if (typeof layer === "undefined") { layer = this.currentLayer.ID; } - - return this.tiles[this.layers[layer].getTileFromWorldXY(x, y)]; - - }, - - /** - * Gets the tile underneath the Input.x/y position - * @param layer The layer to check, defaults to 0 - * @returns {Tile} - */ - getTileFromInputXY: function (layer) { - - if (typeof layer === "undefined") { layer = this.currentLayer.ID; } - - return this.tiles[this.layers[layer].getTileFromWorldXY(this.game.input.worldX, this.game.input.worldY)]; - - }, - - /** - * Get tiles overlaps the given object. - * @param object {GameObject} Tiles you want to get that overlaps this. - * @return {array} Array with tiles information. (Each contains x, y and the tile.) - */ - getTileOverlaps: function (object) { - - return this.currentLayer.getTileOverlaps(object); - - }, - - // COLLIDE - - /** - * Check whether this tilemap collides with the given game object or group of objects. - * @param objectOrGroup {function} Target object of group you want to check. - * @param callback {function} This is called if objectOrGroup collides the tilemap. - * @param context {object} Callback will be called with this context. - * @return {bool} Return true if this collides with given object, otherwise return false. - */ - collide: function (objectOrGroup, callback, context) { - - if (typeof objectOrGroup === "undefined") { objectOrGroup = null; } - if (typeof callback === "undefined") { callback = null; } - if (typeof context === "undefined") { context = null; } - - if (callback !== null && context !== null) - { - this.collisionCallback = callback; - this.collisionCallbackContext = context; - } - - if (objectOrGroup == null) - { - objectOrGroup = this.game.world.group; - } - - // Group? - if (objectOrGroup.isGroup == false) - { - this.collideGameObject(objectOrGroup); - } - else - { - objectOrGroup.forEachAlive(this, this.collideGameObject, true); - } - - }, - - /** - * Check whether this tilemap collides with the given game object. - * @param object {GameObject} Target object you want to check. - * @return {bool} Return true if this collides with given object, otherwise return false. - */ - collideGameObject: function (object) { - - if (object.body.type == Phaser.Types.BODY_DYNAMIC && object.exists == true && object.body.allowCollisions != Phaser.Types.NONE) - { - this._tempCollisionData = this.collisionLayer.getTileOverlaps(object); - - if (this.collisionCallback !== null && this._tempCollisionData.length > 0) - { - this.collisionCallback.call(this.collisionCallbackContext, object, this._tempCollisionData); - } - - return true; - } - else - { - return false; - } - - }, - - /** - * Set a tile to a specific layer. - * @param x {number} X position of this tile. - * @param y {number} Y position of this tile. - * @param index {number} The index of this tile type in the core map data. - * @param [layer] {number} which layer you want to set the tile to. - */ - putTile: function (x, y, index, layer) { - - if (typeof layer === "undefined") { layer = this.currentLayer.ID; } - - this.layers[layer].putTile(x, y, index); - - }, - - /** - * Can be over-ridden by custom classes - */ - update: function () {}, - - destroy: function () { - - this.tiles.length = 0; - this.layers.length = 0; - } + this.generateTiles(tileQuantity); + +}; + +/** +* Create tiles of given quantity. +* @param qty {number} Quentity of tiles to be generated. +*/ +Phaser.Tilemap.prototype.generateTiles = function (qty) { + + for (var i = 0; i < qty; i++) + { + this.tiles.push(new Phaser.Tile(this.game, this, i, this.currentLayer.tileWidth, this.currentLayer.tileHeight)); + } + +}; + +/** +* Set callback to be called when this tilemap collides. +* @param context {object} Callback will be called with this context. +* @param callback {function} Callback function. +*/ +Phaser.Tilemap.prototype.setCollisionCallback = function (context, callback) { + + this.collisionCallbackContext = context; + this.collisionCallback = callback; + +}; + +/** +* Set collision configs of tiles in a range index. +* @param start {number} First index of tiles. +* @param end {number} Last index of tiles. +* @param collision {number} Bit field of flags. (see Tile.allowCollision) +* @param resetCollisions {bool} Reset collision flags before set. +* @param separateX {bool} Enable seprate at x-axis. +* @param separateY {bool} Enable seprate at y-axis. +*/ +Phaser.Tilemap.prototype.setCollisionRange = function (start, end, collision, resetCollisions, separateX, separateY) { + + if (typeof collision === "undefined") { collision = Phaser.Types.ANY; } + if (typeof resetCollisions === "undefined") { resetCollisions = false; } + if (typeof separateX === "undefined") { separateX = true; } + if (typeof separateY === "undefined") { separateY = true; } + + for (var i = start; i < end; i++) + { + this.tiles[i].setCollision(collision, resetCollisions, separateX, separateY); + } + +}; + +/** +* Set collision configs of tiles with given index. +* @param values {number[]} Index array which contains all tile indexes. The tiles with those indexes will be setup with rest parameters. +* @param collision {number} Bit field of flags. (see Tile.allowCollision) +* @param resetCollisions {bool} Reset collision flags before set. +* @param separateX {bool} Enable seprate at x-axis. +* @param separateY {bool} Enable seprate at y-axis. +*/ +Phaser.Tilemap.prototype.setCollisionByIndex = function (values, collision, resetCollisions, separateX, separateY) { + + if (typeof collision === "undefined") { collision = Phaser.Types.ANY; } + if (typeof resetCollisions === "undefined") { resetCollisions = false; } + if (typeof separateX === "undefined") { separateX = true; } + if (typeof separateY === "undefined") { separateY = true; } + + for (var i = 0; i < values.length; i++) + { + this.tiles[values[i]].setCollision(collision, resetCollisions, separateX, separateY); + } + +}; + +// Tile Management + +/** +* Get the tile by its index. +* @param value {number} Index of the tile you want to get. +* @return {Tile} The tile with given index. +*/ +Phaser.Tilemap.prototype.getTileByIndex = function (value) { + + if (this.tiles[value]) + { + return this.tiles[value]; + } + + return null; + +}; + +/** +* Get the tile located at specific position and layer. +* @param x {number} X position of this tile located. +* @param y {number} Y position of this tile located. +* @param [layer] {number} layer of this tile located. +* @return {Tile} The tile with specific properties. +*/ +Phaser.Tilemap.prototype.getTile = function (x, y, layer) { + + if (typeof layer === "undefined") { layer = this.currentLayer.ID; } + + return this.tiles[this.layers[layer].getTileIndex(x, y)]; + +}; + +/** +* Get the tile located at specific position (in world coordinate) and layer. (thus you give a position of a point which is within the tile) +* @param x {number} X position of the point in target tile. +* @param x {number} Y position of the point in target tile. +* @param [layer] {number} layer of this tile located. +* @return {Tile} The tile with specific properties. +*/ +Phaser.Tilemap.prototype.getTileFromWorldXY = function (x, y, layer) { + + if (typeof layer === "undefined") { layer = this.currentLayer.ID; } + + return this.tiles[this.layers[layer].getTileFromWorldXY(x, y)]; + +}; + +/** +* Gets the tile underneath the Input.x/y position +* @param layer The layer to check, defaults to 0 +* @returns {Tile} +*/ +Phaser.Tilemap.prototype.getTileFromInputXY = function (layer) { + + if (typeof layer === "undefined") { layer = this.currentLayer.ID; } + + return this.tiles[this.layers[layer].getTileFromWorldXY(this.game.input.worldX, this.game.input.worldY)]; + +}; + +/** +* Get tiles overlaps the given object. +* @param object {GameObject} Tiles you want to get that overlaps this. +* @return {array} Array with tiles information. (Each contains x, y and the tile.) +*/ +Phaser.Tilemap.prototype.getTileOverlaps = function (object) { + + return this.currentLayer.getTileOverlaps(object); + +}; + +// COLLIDE + +/** +* Check whether this tilemap collides with the given game object or group of objects. +* @param objectOrGroup {function} Target object of group you want to check. +* @param callback {function} This is called if objectOrGroup collides the tilemap. +* @param context {object} Callback will be called with this context. +* @return {bool} Return true if this collides with given object, otherwise return false. +*/ +Phaser.Tilemap.prototype.collide = function (objectOrGroup, callback, context) { + + if (typeof objectOrGroup === "undefined") { objectOrGroup = null; } + if (typeof callback === "undefined") { callback = null; } + if (typeof context === "undefined") { context = null; } + + if (callback !== null && context !== null) + { + this.collisionCallback = callback; + this.collisionCallbackContext = context; + } + + if (objectOrGroup == null) + { + objectOrGroup = this.game.world.group; + } + + // Group? + if (objectOrGroup.isGroup == false) + { + this.collideGameObject(objectOrGroup); + } + else + { + objectOrGroup.forEachAlive(this, this.collideGameObject, true); + } + +}; + +/** +* Check whether this tilemap collides with the given game object. +* @param object {GameObject} Target object you want to check. +* @return {bool} Return true if this collides with given object, otherwise return false. +*/ +Phaser.Tilemap.prototype.collideGameObject = function (object) { + + if (object.body.type == Phaser.Types.BODY_DYNAMIC && object.exists == true && object.body.allowCollisions != Phaser.Types.NONE) + { + this._tempCollisionData = this.collisionLayer.getTileOverlaps(object); + + if (this.collisionCallback !== null && this._tempCollisionData.length > 0) + { + this.collisionCallback.call(this.collisionCallbackContext, object, this._tempCollisionData); + } + + return true; + } + else + { + return false; + } + +}; + +/** +* Set a tile to a specific layer. +* @param x {number} X position of this tile. +* @param y {number} Y position of this tile. +* @param index {number} The index of this tile type in the core map data. +* @param [layer] {number} which layer you want to set the tile to. +*/ +Phaser.Tilemap.prototype.putTile = function (x, y, index, layer) { + + if (typeof layer === "undefined") { layer = this.currentLayer.ID; } + + this.layers[layer].putTile(x, y, index); + +}; + +/** +* Calls the renderer +*/ +Phaser.Tilemap.prototype.update = function () { + + this.renderer.render(this); + +}; + +Phaser.Tilemap.prototype.destroy = function () { + + this.tiles.length = 0; + this.layers.length = 0; + }; Object.defineProperty(Phaser.Tilemap.prototype, "widthInPixels", { diff --git a/src/tilemap/TilemapLayer.js b/src/tilemap/TilemapLayer.js index 15228e79..0b01f499 100644 --- a/src/tilemap/TilemapLayer.js +++ b/src/tilemap/TilemapLayer.js @@ -11,7 +11,7 @@ * @param parent {Tilemap} The tilemap that contains this layer. * @param id {number} The ID of this layer within the Tilemap array. * @param key {string} Asset key for this map. -* @param mapFormat {number} Format of this map data, available: Tilemap.FORMAT_CSV or Tilemap.FORMAT_TILED_JSON. +* @param mapFormat {number} Format of this map data, available: Tilemap.CSV or Tilemap.JSON. * @param name {string} Name of this layer, so you can get this layer by its name. * @param tileWidth {number} Width of tiles in this map. * @param tileHeight {number} Height of tiles in this map. @@ -72,18 +72,19 @@ Phaser.TilemapLayer = function (parent, id, key, mapFormat, name, tileWidth, til this.game = parent.game; this.ID = id; this.name = name; + this.key = key; this.mapFormat = mapFormat; - this.tileWidth = tileWidth; this.tileHeight = tileHeight; this.boundsInTiles = new Phaser.Rectangle(); - this.tileset = this.game.cache.getImage(key); + var map = this.game.cache.getTilemap(key); - // Sprite property surely? - this.alpha = 1; + this.tileset = map.data; + + this._alpha = 1; this.canvas = null; this.context = null; @@ -91,15 +92,6 @@ Phaser.TilemapLayer = function (parent, id, key, mapFormat, name, tileWidth, til this.texture = null; this.sprite = null; - // this.canvas = Phaser.Canvas.create(this.game.width, this.game.height); - // this.context = this.canvas.getContext('2d'); - - // this.baseTexture = new PIXI.BaseTexture(this.canvas); - // this.texture = new PIXI.Texture(this.baseTexture); - // this.sprite = new PIXI.Sprite(this.texture); - - // this.game.stage._stage.addChild(this.sprite); - this.mapData = []; this._tempTileBlock = []; @@ -477,7 +469,7 @@ Phaser.TilemapLayer.prototype = { this.texture = new PIXI.Texture(this.baseTexture); this.sprite = new PIXI.Sprite(this.texture); - this.game.stage._stage.addChild(this.sprite); + this.parent.addChild(this.sprite); }, @@ -503,7 +495,7 @@ Phaser.TilemapLayer.prototype = { var i = 0; - if (this.mapFormat == Phaser.Tilemap.FORMAT_TILED_JSON) + if (this.mapFormat == Phaser.Tilemap.JSON) { // For some reason Tiled counts from 1 not 0 this.tileOffsets[0] = null; @@ -527,3 +519,21 @@ Phaser.TilemapLayer.prototype = { } }; + +Object.defineProperty(Phaser.TilemapLayer.prototype, 'alpha', { + + get: function() { + return this._alpha; + }, + + set: function(value) { + + if (this.sprite) + { + this.sprite.alpha = value; + } + + this._alpha = value; + } + +}); diff --git a/src/tilemap/TilemapRenderer.js b/src/tilemap/TilemapRenderer.js index 964a3db6..dc7a4752 100644 --- a/src/tilemap/TilemapRenderer.js +++ b/src/tilemap/TilemapRenderer.js @@ -129,7 +129,10 @@ Phaser.TilemapRenderer.prototype = { } // Only needed if running in WebGL, otherwise this array will never get cleared down I don't think! - PIXI.texturesToUpdate.push(layer.baseTexture); + if (this.game.renderType == Phaser.WEBGL) + { + PIXI.texturesToUpdate.push(layer.baseTexture); + } }