diff --git a/README.md b/README.md index b72d4758..d3d6ad5f 100644 --- a/README.md +++ b/README.md @@ -160,6 +160,7 @@ Updates: * ScaleManager has 2 new events: ScaleManager.enterFullScreen and ScaleManager.leaveFullScreen, so you can respond to fullscreen changes directly. * RandomDataGenerator.integerInRange(min, max) now includes both `min` and `max` within its range (#501) * Tween no longer copies all the object properties into the `_valuesStart` object on creation. +* Completely empty Tilemaps can now be created. This allows for dynamic map generation at runtime. Bug Fixes: diff --git a/examples/tilemaps/paint tiles.js b/examples/tilemaps/paint tiles.js index fa2748de..2c06f6ba 100644 --- a/examples/tilemaps/paint tiles.js +++ b/examples/tilemaps/paint tiles.js @@ -13,6 +13,7 @@ var layer; var marker; var currentTile; +var cursors; function create() { @@ -30,6 +31,8 @@ function create() { marker.lineStyle(2, 0x000000, 1); marker.drawRect(0, 0, 32, 32); + cursors = game.input.keyboard.createCursorKeys(); + } function update() { @@ -52,11 +55,28 @@ function update() { } } + if (cursors.left.isDown) + { + game.camera.x -= 4; + } + else if (cursors.right.isDown) + { + game.camera.x += 4; + } + + if (cursors.up.isDown) + { + game.camera.y -= 4; + } + else if (cursors.down.isDown) + { + game.camera.y += 4; + } + } function render() { - game.debug.text('Left-click to paint. Shift + Left-click to select tile.', 32, 32, 'rgb(0,0,0)'); - game.debug.text('Tile: ' + map.getTile(layer.getTileX(marker.x), layer.getTileY(marker.y)), 32, 48, 'rgb(0,0,0)'); + game.debug.text('Left-click to paint. Shift + Left-click to select tile. Arrows to scroll.', 32, 32, '#efefef'); } diff --git a/examples/wip/tilemap blank.js b/examples/wip/tilemap blank.js new file mode 100644 index 00000000..a5a05deb --- /dev/null +++ b/examples/wip/tilemap blank.js @@ -0,0 +1,68 @@ + +var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', { preload: preload, create: create, update: update, render: render }); +// var game = new Phaser.Game(800, 600, Phaser.AUTO, 'phaser-example', { preload: preload, create: create, update: update, render: render }); + +function preload() { + + game.load.image('ground_1x1', 'assets/tilemaps/tiles/ground_1x1.png'); + +} + +var map; +var tileset; +var layer; +var cursors; + +function create() { + + game.stage.backgroundColor = '#787878'; + + // Creates a blank tilemap + map = game.add.tilemap(); + + // Creates a layer and sets-up the map dimensions. + // In this case the map is 30x30 tiles in size and the tiles are 32x32 pixels in size. + map.create('level1', 30, 30, 32, 32); + + // Add a Tileset image to the map + map.addTilesetImage('ground_1x1'); + + map.putTile(4, 1, 1) + map.putTile(10, 2, 1) + map.putTile(10, 3, 1) + map.putTile(10, 4, 1) + + // Create a layer. This is where the map is rendered to. + layer = map.createLayer('level1'); + + // layer.resizeWorld(); + // map.setCollisionBetween(1, 12); + // layer.debug = true; + + cursors = game.input.keyboard.createCursorKeys(); + +} + +function update() { + + if (cursors.left.isDown) + { + } + else if (cursors.right.isDown) + { + } + + if (cursors.up.isDown) + { + } + else if (cursors.down.isDown) + { + } + +} + +function render() { + + game.debug.cameraInfo(game.camera, 32, 32); + +} diff --git a/examples/wip/tween var.js b/examples/wip/tween var.js new file mode 100644 index 00000000..2287ac3b --- /dev/null +++ b/examples/wip/tween var.js @@ -0,0 +1,39 @@ + +var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', { preload: preload, create: create, update: update, render: render }); + +function preload() { + + game.load.spritesheet('mummy', 'assets/sprites/metalslug_mummy37x45.png', 37, 45, 18); + +} + +var mummy; + +function create() { + + game.stage.backgroundColor = 0x3d4d3d; + + mummy = game.add.sprite(300, 300, 'mummy', 5); + + var t = game.add.tween(mummy).to( { "x": 400, "y": 400 }, 5000, Phaser.Easing.Linear.None, true); + + // var t = game.add.tween(mummy).to( { "scale.x": 4, "scale.y": 4 }, 5000, Phaser.Easing.Linear.None, true); + + t.onComplete.add(tweenOver, this); + +} + +function tweenOver(a) { + + console.log('over'); + console.log(a); + +} + +function update() { + +} + +function render() { + +} \ No newline at end of file diff --git a/src/gameobjects/GameObjectCreator.js b/src/gameobjects/GameObjectCreator.js index da0d4805..c1d4ec61 100644 --- a/src/gameobjects/GameObjectCreator.js +++ b/src/gameobjects/GameObjectCreator.js @@ -273,13 +273,12 @@ Phaser.GameObjectCreator.prototype = { * Creates a new Tilemap object. * * @method Phaser.GameObjectCreator#tilemap - * @param {string} key - Asset key for the JSON or CSV map data in the cache. - * @param {object|string} tilesets - An object mapping Cache.tileset keys with the tileset names in the JSON file. If a string is provided that will be used. + * @param {string} [key] - The key of the tilemap data as stored in the Cache. If you're creating a blank map don't pass anything for this parameter. * @return {Phaser.Tilemap} The newly created tilemap object. */ - tilemap: function (key, tilesets) { + tilemap: function (key) { - return new Phaser.Tilemap(this.game, key, tilesets); + return new Phaser.Tilemap(this.game, key); }, diff --git a/src/gameobjects/GameObjectFactory.js b/src/gameobjects/GameObjectFactory.js index 4320eba6..f4c6aab8 100644 --- a/src/gameobjects/GameObjectFactory.js +++ b/src/gameobjects/GameObjectFactory.js @@ -304,13 +304,12 @@ Phaser.GameObjectFactory.prototype = { * Creates a new Tilemap object. * * @method Phaser.GameObjectFactory#tilemap - * @param {string} key - Asset key for the JSON or CSV map data in the cache. - * @param {object|string} tilesets - An object mapping Cache.tileset keys with the tileset names in the JSON file. If a string is provided that will be used. + * @param {string} [key] - The key of the tilemap data as stored in the Cache. If you're creating a blank map don't pass anything for this parameter. * @return {Phaser.Tilemap} The newly created tilemap object. */ - tilemap: function (key, tilesets) { + tilemap: function (key) { - return new Phaser.Tilemap(this.game, key, tilesets); + return new Phaser.Tilemap(this.game, key); }, diff --git a/src/physics/World.js b/src/physics/World.js index 2ca0a844..4f8143dc 100644 --- a/src/physics/World.js +++ b/src/physics/World.js @@ -367,7 +367,10 @@ Phaser.Physics.World.prototype = { if (this.bounds !== null) { - this.world.removeBody(this.bounds); + if (this.bounds.world) + { + this.world.removeBody(this.bounds); + } var i = this.bounds.shapes.length; diff --git a/src/tilemap/Tilemap.js b/src/tilemap/Tilemap.js index c594f9df..a46a9c1d 100644 --- a/src/tilemap/Tilemap.js +++ b/src/tilemap/Tilemap.js @@ -11,7 +11,7 @@ * @class Phaser.Tilemap * @constructor * @param {Phaser.Game} game - Game reference to the currently running game. -* @param {string} [key] - The key of the tilemap data as stored in the Cache. +* @param {string} [key] - The key of the tilemap data as stored in the Cache. If you're creating a blank map don't pass anything for this parameter. */ Phaser.Tilemap = function (game, key) { @@ -155,37 +155,52 @@ Phaser.Tilemap.prototype = { * Creates an empty map of the given dimensions. * * @method Phaser.Tilemap#create - * @param {string} name - The name of the map (mostly used for debugging) + * @param {string} name - The name of the default layer of the map * @param {number} width - The width of the map in tiles. * @param {number} height - The height of the map in tiles. + * @param {number} tileWidth - The width of the tiles the map uses for calculations. + * @param {number} tileHeight - The height of the tiles the map uses for calculations. */ - create: function (name, width, height) { + create: function (name, width, height, tileWidth, tileHeight) { - var data = []; + this.width = width; + this.height = height; + this.tileWidth = tileWidth; + this.tileHeight = tileHeight; + this.widthInPixels = width * tileWidth; + this.heightInPixels = height * tileHeight; + + var row; + var output = []; for (var y = 0; y < height; y++) { - data[y] = []; + row = []; for (var x = 0; x < width; x++) { - data[y][x] = 0; + row.push(null); } + + output.push(row); } this.layers.push({ name: name, + x: 0, + y: 0, width: width, height: height, + widthInPixels: this.widthInPixels, + heightInPixels: this.heightInPixels, alpha: 1, visible: true, - tileMargin: 0, - tileSpacing: 0, - format: Phaser.Tilemap.CSV, - data: data, + properties: {}, indexes: [], - dirty: true + callbacks: [], + bodies: [], + data: output }); @@ -200,8 +215,17 @@ Phaser.Tilemap.prototype = { * @method Phaser.Tilemap#addTilesetImage * @param {string} tileset - The name of the tileset as specified in the map data. * @param {string} [key] - The key of the Phaser.Cache image used for this tileset. If not specified it will look for an image with a key matching the tileset parameter. + * @param {number} [tileWidth] - The width of the tiles in the Tileset Image. If not given it will default to the map.tileWidth value. + * @param {number} [tileHeight] - The height of the tiles in the Tileset Image. If not given it will default to the map.tileHeight value. + * @param {number} [tileMargin=0] - The width of the tiles in the Tileset Image. If not given it will default to the map.tileWidth value. + * @param {number} [tileSpacing=0] - The height of the tiles in the Tileset Image. If not given it will default to the map.tileHeight value. */ - addTilesetImage: function (tileset, key) { + addTilesetImage: function (tileset, key, tileWidth, tileHeight, tileMargin, tileSpacing) { + + if (typeof tileWidth === 'undefined') { tileWidth = this.tileWidth; } + if (typeof tileHeight === 'undefined') { tileHeight = this.tileHeight; } + if (typeof tileMargin === 'undefined') { tileMargin = 0; } + if (typeof tileSpacing === 'undefined') { tileSpacing = 0; } if (typeof key === 'undefined') { @@ -222,10 +246,17 @@ Phaser.Tilemap.prototype = { if (this.tilesets[tileset]) { - this.tilesets[tileset].image = this.game.cache.getImage(key); - + this.tilesets[tileset].setImage(this.game.cache.getImage(key)); return true; } + else + { + var newSet = new Phaser.Tileset(key, 0, tileWidth, tileHeight, tileMargin, tileSpacing, {}); + + newSet.setImage(this.game.cache.getImage(key)); + + this.tilesets.push(newSet); + } return false; @@ -920,6 +951,12 @@ Phaser.Tilemap.prototype = { }, + hasTile: function (x, y, layer) { + + return (this.layers[layer].data[y] !== null && this.layers[layer].data[y][x] !== null); + + }, + /** * Puts a tile of the given index value at the coordinate specified. * @@ -937,14 +974,30 @@ Phaser.Tilemap.prototype = { { if (tile instanceof Phaser.Tile) { - this.layers[layer].data[y][x].copy(tile); + if (this.hasTile(x, y, layer)) + { + this.layers[layer].data[y][x].copy(tile); + } + else + { + //Phaser.Tile = function (layer, index, x, y, width, height) { + this.layers[layer].data[y][x] = new Phaser.Tile(layer, tile.index, x, y, tile.width, tile.height); + } } else { - this.layers[layer].data[y][x].index = tile; + if (this.hasTile(x, y, layer)) + { + this.layers[layer].data[y][x].index = tile; + } + else + { + this.layers[layer].data[y][x] = new Phaser.Tile(layer, tile, x, y, this.tileWidth, this.tileHeight); + } } this.layers[layer].dirty = true; + this.calculateFaces(layer); } diff --git a/src/tilemap/TilemapLayer.js b/src/tilemap/TilemapLayer.js index ad7ef6a1..ed92a2a8 100644 --- a/src/tilemap/TilemapLayer.js +++ b/src/tilemap/TilemapLayer.js @@ -62,7 +62,8 @@ Phaser.TilemapLayer = function (game, tilemap, index, width, height) { */ this.textureFrame = new Phaser.Frame(0, 0, 0, width, height, 'tilemapLayer', game.rnd.uuid()); - Phaser.Sprite.call(this, this.game, 0, 0, this.texture, this.textureFrame); + // Phaser.Sprite.call(this, this.game, 0, 0, this.texture, this.textureFrame); + Phaser.Image.call(this, this.game, 0, 0, this.texture, this.textureFrame); /** * @property {string} name - The name of the layer. @@ -280,8 +281,8 @@ Phaser.TilemapLayer = function (game, tilemap, index, width, height) { }; -Phaser.TilemapLayer.prototype = Object.create(Phaser.Sprite.prototype); -Phaser.TilemapLayer.prototype = Phaser.Utils.extend(true, Phaser.TilemapLayer.prototype, Phaser.Sprite.prototype, PIXI.Sprite.prototype); +Phaser.TilemapLayer.prototype = Object.create(Phaser.Image.prototype); +// Phaser.TilemapLayer.prototype = Phaser.Utils.extend(true, Phaser.TilemapLayer.prototype, Phaser.Sprite.prototype, PIXI.Sprite.prototype); Phaser.TilemapLayer.prototype.constructor = Phaser.TilemapLayer; /** @@ -292,7 +293,8 @@ Phaser.TilemapLayer.prototype.constructor = Phaser.TilemapLayer; */ Phaser.TilemapLayer.prototype.postUpdate = function () { - Phaser.Sprite.prototype.postUpdate.call(this); + // Phaser.Sprite.prototype.postUpdate.call(this); + Phaser.Image.prototype.postUpdate.call(this); // Stops you being able to auto-scroll the camera if it's not following a sprite this.scrollX = this.game.camera.x * this.scrollFactorX; @@ -554,7 +556,7 @@ Phaser.TilemapLayer.prototype.updateMax = function () { */ Phaser.TilemapLayer.prototype.render = function () { - if (this.layer.dirty) + if (this.layer.dirty) { this.dirty = true; } @@ -587,6 +589,102 @@ Phaser.TilemapLayer.prototype.render = function () { this.context.globalAlpha = this.debugAlpha; } + for (var y = this._startY, lenY = this._startY + this._maxY; y < lenY; y++) + { + this._column = this.layer.data[y]; + + for (var x = this._startX, lenX = this._startX + this._maxX; x < lenX; x++) + { + if (this._column[x]) + { + tile = this._column[x]; + + // this needs to know which set the index is from + // set = this.map.tilesets[this.map.tiles[tile.index][2]] + set = this.map.tilesets[0]; + + if (this.debug === false && tile.alpha !== this.context.globalAlpha) + { + this.context.globalAlpha = tile.alpha; + } + + set.draw(this.context, Math.floor(this._tx), Math.floor(this._ty), tile.index); + + if (tile.debug) + { + this.context.fillStyle = 'rgba(0, 255, 0, 0.4)'; + this.context.fillRect(Math.floor(this._tx), Math.floor(this._ty), this.map.tileWidth, this.map.tileHeight); + } + } + + this._tx += this.map.tileWidth; + + } + + this._tx = this._dx; + this._ty += this.map.tileHeight; + + } + + if (this.debug) + { + this.context.globalAlpha = 1; + this.renderDebug(); + } + + if (this.game.renderType === Phaser.WEBGL) + { + // PIXI.updateWebGLTexture(this.baseTexture, renderSession.gl); + PIXI.updateWebGLTexture(this.baseTexture, this.game.renderer.gl); + } + + this.dirty = false; + this.layer.dirty = false; + + return true; + +} + +/** +* Renders the tiles to the layer canvas and pushes to the display. +* @method Phaser.TilemapLayer#render +* @memberof Phaser.TilemapLayer +*/ +Phaser.TilemapLayer.prototype.OLDrender = function () { + + if (this.layer.dirty) + { + this.dirty = true; + } + + if (!this.dirty || !this.visible) + { + // return; + } + + this._prevX = this._dx; + this._prevY = this._dy; + + this._dx = -(this._x - (this._startX * this.map.tileWidth)); + this._dy = -(this._y - (this._startY * this.map.tileHeight)); + + this._tx = this._dx; + this._ty = this._dy; + + this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); + + this.context.fillStyle = this.tileColor; + + var tile; + var set; + var ox = 0; + var oy = 0; + + if (this.debug) + { + this.context.globalAlpha = this.debugAlpha; + } + for (var y = this._startY, lenY = this._startY + this._maxY; y < lenY; y++) { this._column = this.layer.data[y]; @@ -638,11 +736,11 @@ Phaser.TilemapLayer.prototype.render = function () { ); } - if (tile.debug) - { + // if (tile.debug) + // { this.context.fillStyle = 'rgba(0, 255, 0, 0.4)'; this.context.fillRect(Math.floor(this._tx), Math.floor(this._ty), this.map.tileWidth, this.map.tileHeight); - } + // } } else { diff --git a/src/tilemap/TilemapParser.js b/src/tilemap/TilemapParser.js index e6b1e12a..8ff6470e 100644 --- a/src/tilemap/TilemapParser.js +++ b/src/tilemap/TilemapParser.js @@ -74,6 +74,11 @@ Phaser.TilemapParser = { */ parse: function (game, key) { + if (typeof key === 'undefined') + { + return this.getEmptyData(); + } + var map = game.cache.getTilemapData(key); if (map) @@ -89,7 +94,7 @@ Phaser.TilemapParser = { } else { - return this.getEmptyData(); + console.warn('Phaser.TilemapParser.parse - No map data found for key ' + key); } }, diff --git a/src/tilemap/Tileset.js b/src/tilemap/Tileset.js index 1737a6cf..1ddb96e1 100644 --- a/src/tilemap/Tileset.js +++ b/src/tilemap/Tileset.js @@ -11,7 +11,7 @@ * @class Phaser.Tileset * @constructor * @param {string} name - The name of the tileset in the map data. -* @param {number} firstgid - The Tiled firstgid value. +* @param {number} firstgid - The Tiled firstgid value. In non-Tiled data this should be considered the starting index value of the first tile in this set. * @param {number} width - Width of each tile in pixels. * @param {number} height - Height of each tile in pixels. * @param {number} margin - The amount of margin around the tilesheet. @@ -26,8 +26,7 @@ Phaser.Tileset = function (name, firstgid, width, height, margin, spacing, prope this.name = name; /** - * @property {number} firstgid - The Tiled firstgid value. - * @default + * @property {number} firstgid - The Tiled firstgid value. In non-Tiled data this should be considered the starting index value of the first tile in this set. */ this.firstgid = firstgid; @@ -42,12 +41,12 @@ Phaser.Tileset = function (name, firstgid, width, height, margin, spacing, prope this.tileHeight = height; /** - * @property {number} tileMargin - The margin around the tiles in the sheet. + * @property {number} tileMargin - The margin around the tiles in the tileset. */ this.tileMargin = margin; /** - * @property {number} tileSpacing - The margin around the tiles in the sheet. + * @property {number} tileSpacing - The spacing in pixels between each tile in the tileset. */ this.tileSpacing = spacing; @@ -56,11 +55,6 @@ Phaser.Tileset = function (name, firstgid, width, height, margin, spacing, prope */ this.properties = properties; - /** - * @property {object} tilePproperties - Tile specific properties (typically defined in the Tiled editor). - */ - // this.tileProperties = {}; - /** * @property {object} image - The image used for rendering. This is a reference to the image stored in Phaser.Cache. */ @@ -81,48 +75,81 @@ Phaser.Tileset = function (name, firstgid, width, height, margin, spacing, prope */ this.total = 0; + /** + * @property {array} draw - The tile drawImage look-up table + * @private + */ + this.drawCoords = []; + }; Phaser.Tileset.prototype = { /** - * Gets a Tile from this set. + * Draws a tile from this Tileset at the given coordinates on the context. * - * @method Phaser.Tileset#getTile - * @param {number} index - The index of the tile within the set. - * @return {object} The tile object. - getTile: function (index) { + * @method Phaser.Tileset#draw + * @param {HTMLCanvasContext} context - The context to draw the tile onto. + * @param {number} x - The x coordinate to draw to. + * @param {number} y - The y coordinate to draw to. + * @param {number} index - The index of the tile within the set to draw. + */ + draw: function (context, x, y, index) { - return this.tiles[index]; + if (!this.image || !this.drawCoords[index]) + { + return; + } + + context.drawImage( + this.image, + this.drawCoords[index][0], + this.drawCoords[index][1], + this.tileWidth, + this.tileHeight, + x, + y, + this.tileWidth, + this.tileHeight + ); }, - */ /** - * Gets a Tile from this set. + * Adds a reference from this Tileset to an Image stored in the Phaser.Cache. * - * @method Phaser.Tileset#getTileX - * @param {number} index - The index of the tile within the set. - * @return {object} The tile object. - getTileX: function (index) { + * @method Phaser.Tileset#setImage + * @param {Image} image - The image this tileset will use to draw with. + */ + setImage: function (image) { - return this.tiles[index][0]; + this.image = image; + + this.rows = Math.round((image.height - this.tileMargin) / (this.tileHeight + this.tileSpacing)); + this.columns = Math.round((image.width - this.tileMargin) / (this.tileWidth + this.tileSpacing)); + this.total = this.rows * this.columns; + + // Create the index look-up + this.drawCoords.length = 0; + + var tx = this.tileMargin; + var ty = this.tileMargin; + var i = this.firstgid; + + for (var y = 0; y < this.rows; y++) + { + for (var x = 0; x < this.columns; x++) + { + this.drawCoords[i] = [ tx, ty ]; + tx += this.tileWidth + this.tileSpacing; + i++; + } + + tx = this.tileMargin; + ty += this.tileHeight + this.tileSpacing; + } }, - */ - - /** - * Gets a Tile from this set. - * - * @method Phaser.Tileset#getTileY - * @param {number} index - The index of the tile within the set. - * @return {object} The tile object. - getTileY: function (index) { - - return this.tiles[index][1]; - - }, - */ /** * Sets tile spacing and margins. @@ -136,20 +163,9 @@ Phaser.Tileset.prototype = { this.tileMargin = margin; this.tileSpacing = spacing; - }, - - /** - * Checks if the tile at the given index exists. - * - * @method Phaser.Tileset#checkTileIndex - * @param {number} index - The index of the tile within the set. - * @return {boolean} True if a tile exists at the given index otherwise false. - checkTileIndex: function (index) { - - return (this.tiles[index]); + this.setImage(this.image); } - */ }; diff --git a/src/utils/Debug.js b/src/utils/Debug.js index cc04125a..949d2381 100644 --- a/src/utils/Debug.js +++ b/src/utils/Debug.js @@ -90,6 +90,11 @@ Phaser.Utils.Debug = function (game) { */ this.currentAlpha = 1; + /** + * @property {boolean} dirty - Does the canvas need re-rendering? + */ + this.dirty = false; + }; Phaser.Utils.Debug.prototype = { @@ -142,6 +147,11 @@ Phaser.Utils.Debug.prototype = { this.currentAlpha = this.context.globalAlpha; this.columnWidth = columnWidth; + if (this.sprite && this.dirty) + { + // this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); + } + this.context.save(); this.context.setTransform(1, 0, 0, 1, 0, 0); this.context.strokeStyle = color;