diff --git a/examples/tilemaps/wip1.php b/examples/tilemaps/wip1.php index 033eb950..536460ac 100644 --- a/examples/tilemaps/wip1.php +++ b/examples/tilemaps/wip1.php @@ -15,9 +15,12 @@ } + var map; + var tileset; var layer; var cursors; var sprite2; + var overlap; function create() { @@ -25,9 +28,19 @@ // A Tilemap object just holds the data needed to describe the map (i.e. the json exported from Tiled, or the CSV exported from elsewhere). // You can add your own data or manipulate the data (swap tiles around, etc) but in order to display it you need to create a TilemapLayer. - var map = new Phaser.Tilemap(game, 'level3'); + map = new Phaser.Tilemap(game, 'level3'); - var tileset = game.cache.getTileset('tiles'); + // A Tileset is a single image containing a strip of tiles. Each tile is broken down into its own Phaser.Tile object on import. + // You can set properties on the Tile objects, such as collision, n-way movement and meta data. + // A Tilemap uses a Tileset to render. The indexes in the map corresponding to the Tileset indexes. + // This way multiple levels can share the same single Tileset without requiring one each. + tileset = game.cache.getTileset('tiles'); + + // Basically this sets EVERY SINGLE tile to fully collide on all faces + tileset.setCollisionRange(0, tileset.total - 1, true, true, true, true); + + // And this turns off collision on the only tile we don't want collision on :) + tileset.setCollision(6, false, false, false, false); // A TilemapLayer consists of an x,y coordinate (position), a width and height, a Tileset and a Tilemap which it uses for map data. // The x/y coordinates are in World space and you can place the tilemap layer anywhere in the world. @@ -38,6 +51,11 @@ layer = new Phaser.TilemapLayer(game, 0, 0, 640, 400, tileset, map, 0); // To set tiles for collision you need to modify the Tileset, which is a property of the layer + + // Task now + // + // 1) Get tiles that will collide with sprites new position (hullX and hullY) + // 2) Separate x/y // Collision is based on the layer.x/y value @@ -95,12 +113,55 @@ layer.x += 4; } + // getTiles: function (x, y, width, height, collides, layer) { + overlap = layer.getTiles(layer.x, layer.y, 128, 128); + } function render() { layer.render(); + game.context.save(); + game.context.setTransform(1, 0, 0, 1, 0, 0); + game.context.fillStyle = 'rgba(255, 0, 0, 0.5)'; + + if (overlap.length > 1) + { + var x = 0; + var y = 0; + + for (var i = 1; i < overlap.length; i++) + { + game.context.drawImage( + overlap[i].tile.tileset.image, + overlap[i].tile.x, + overlap[i].tile.y, + overlap[i].tile.width, + overlap[i].tile.height, + 0 + (x * overlap[i].tile.width), + 420 + (y * overlap[i].tile.height), + overlap[i].tile.width, + overlap[i].tile.height + ); + + if (overlap[i].tile.collideNone == false) + { + game.context.fillRect(0 + (x * overlap[i].tile.width), 420 + (y * overlap[i].tile.height), overlap[i].tile.width, overlap[i].tile.height); + } + + x++; + + if (x == overlap[0].tw) + { + x = 0; + y++; + } + } + } + + game.context.restore(); + } diff --git a/examples/tilemaps/wip2.php b/examples/tilemaps/wip2.php new file mode 100644 index 00000000..6095d598 --- /dev/null +++ b/examples/tilemaps/wip2.php @@ -0,0 +1,190 @@ + + + + + \ No newline at end of file diff --git a/src/physics/arcade/ArcadePhysics.js b/src/physics/arcade/ArcadePhysics.js index 53a2724f..d59a8708 100644 --- a/src/physics/arcade/ArcadePhysics.js +++ b/src/physics/arcade/ArcadePhysics.js @@ -633,10 +633,21 @@ Phaser.Physics.Arcade.prototype = { * @param object2 The second GameObject to separate * @returns {boolean} Returns true if the objects were separated, otherwise false. */ - separateTile: function (object, x, y, width, height, mass, collideLeft, collideRight, collideUp, collideDown, separateX, separateY) { + separateTile: function (body, tile) { - var separatedX = this.separateTileX(object.body, x, y, width, height, mass, collideLeft, collideRight, separateX); - var separatedY = this.separateTileY(object.body, x, y, width, height, mass, collideUp, collideDown, separateY); + var separatedX = this.separateTileX(body, tile, true); + + if (separatedX) + { + console.log('x overlap', this._overlap); + } + + var separatedY = this.separateTileY(body, tile, true); + + if (separatedY) + { + console.log('y overlap', this._overlap); + } if (separatedX || separatedY) { @@ -653,58 +664,48 @@ Phaser.Physics.Arcade.prototype = { * @param tile The Tile to separate * @returns {boolean} Whether the objects in fact touched and were separated along the X axis. */ - OLDseparateTileX: function (object, x, y, width, height, mass, collideLeft, collideRight, separate) { + separateTileX: function (body, tile, separate) { // Can't separate two immovable objects (tiles are always immovable) - if (object.immovable) + if (body.immovable) { return false; } - // First, get the object delta this._overlap = 0; - // console.log('separatedX', x, y, object.deltaX()); - - if (object.deltaX() != 0) + // Phaser.Rectangle.intersectsRaw = function (a, left, right, top, bottom, tolerance) + if (body.deltaX() != 0 && Phaser.Rectangle.intersects(body.hullX, tile, 0)) { - this._bounds1.setTo(object.x, object.y, object.width, object.height); + // The hulls overlap, let's process it + this._maxOverlap = body.deltaAbsX() + this.OVERLAP_BIAS; - if ((this._bounds1.right > x) && (this._bounds1.x < x + width) && (this._bounds1.bottom > y) && (this._bounds1.y < y + height)) + if (body.deltaX() < 0) { - // The hulls overlap, let's process it - this._maxOverlap = object.deltaAbsX() + this.OVERLAP_BIAS; + // Moving left + this._overlap = tile.right - body.hullX.x; - // TODO - We need to check if we're already inside of the tile, i.e. jumping through an n-way tile - // in which case we didn't ought to separate because it'll look like tunneling - - if (object.deltaX() > 0) + if ((this._overlap > this._maxOverlap) || body.allowCollision.left == false || tile.tile.collideRight == false) { - // Going right ... - this._overlap = object.x + object.width - x; - - if ((this._overlap > this._maxOverlap) || !object.allowCollision.right || !collideLeft) - { - this._overlap = 0; - } - else - { - object.touching.right = true; - } + this._overlap = 0; } - else if (object.deltaX() < 0) + else { - // Going left ... - this._overlap = object.x - width - x; + body.touching.left = true; + } + } + else + { + // Moving right + this._overlap = body.hullX.right - tile.left; - if ((-this._overlap > this._maxOverlap) || !object.allowCollision.left || !collideRight) - { - this._overlap = 0; - } - else - { - object.touching.left = true; - } + if ((this._overlap > this._maxOverlap) || body.allowCollision.right == false || tile.tile.collideLeft == false) + { + this._overlap = 0; + } + else + { + body.touching.right = true; } } } @@ -714,16 +715,24 @@ Phaser.Physics.Arcade.prototype = { { if (separate) { - object.x = object.x - this._overlap; - - if (object.bounce.x == 0) + if (body.deltaX() < 0) { - object.velocity.x = 0; + body.x = body.x + this._overlap; } else { - object.velocity.x = -object.velocity.x * object.bounce.x; + body.x = body.x - this._overlap; } + + if (body.bounce.x == 0) + { + body.velocity.x = 0; + } + else + { + body.velocity.x = -body.velocity.x * body.bounce.x; + } + body.updateHulls(); } return true; } @@ -740,59 +749,47 @@ Phaser.Physics.Arcade.prototype = { * @param tile The Tile to separate * @returns {boolean} Whether the objects in fact touched and were separated along the X axis. */ - OLDseparateTileY: function (object, x, y, width, height, mass, collideUp, collideDown, separate) { + separateTileY: function (body, tile, separate) { // Can't separate two immovable objects (tiles are always immovable) - if (object.immovable) + if (body.immovable) { return false; } - // First, get the object delta this._overlap = 0; - if (object.deltaY() != 0) + if (body.deltaY() != 0 && Phaser.Rectangle.intersects(body.hullY, tile, 0)) { - this._bounds1.setTo(object.x, object.y, object.width, object.height); + // The hulls overlap, let's process it + this._maxOverlap = body.deltaAbsY() + this.OVERLAP_BIAS; - if ((this._bounds1.right > x) && (this._bounds1.x < x + width) && (this._bounds1.bottom > y) && (this._bounds1.y < y + height)) + if (body.deltaY() < 0) { - // The hulls overlap, let's process it + // Moving up + this._overlap = tile.bottom - body.hullY.y; - // Not currently used, may need it so keep for now - this._maxOverlap = object.deltaAbsY() + this.OVERLAP_BIAS; - - // TODO - We need to check if we're already inside of the tile, i.e. jumping through an n-way tile - // in which case we didn't ought to separate because it'll look like tunneling - - if (object.deltaY() > 0) + if ((this._overlap > this._maxOverlap) || body.allowCollision.up == false || tile.tile.collideDown == false) { - // Going down ... - this._overlap = object.bottom - y; - - // if (object.allowCollision.down && collideDown && this._overlap < this._maxOverlap) - if ((this._overlap > this._maxOverlap) || !object.allowCollision.down || !collideDown) - { - this._overlap = 0; - } - else - { - object.touching.down = true; - } + this._overlap = 0; } else { - // Going up ... - this._overlap = object.y - height - y; + body.touching.up = true; + } + } + else + { + // Moving down + this._overlap = body.hullY.bottom - tile.top; - if ((-this._overlap > this._maxOverlap) || !object.allowCollision.up || !collideUp) - { - this._overlap = 0; - } - else - { - object.touching.up = true; - } + if ((this._overlap > this._maxOverlap) || body.allowCollision.down == false || tile.tile.collideUp == false) + { + this._overlap = 0; + } + else + { + body.touching.down = true; } } } @@ -802,16 +799,24 @@ Phaser.Physics.Arcade.prototype = { { if (separate) { - object.y = object.y - this._overlap; - - if (object.bounce.y == 0) + if (body.deltaY() < 0) { - object.velocity.y = 0; + body.y = body.y + this._overlap; } else { - object.velocity.y = -object.velocity.y * object.bounce.y; + body.y = body.y - this._overlap; } + + if (body.bounce.y == 0) + { + body.velocity.y = 0; + } + else + { + body.velocity.y = -body.velocity.y * body.bounce.y; + } + body.updateHulls(); } return true; } @@ -820,167 +825,6 @@ Phaser.Physics.Arcade.prototype = { return false; } - }, - - /** - * Separates the two objects on their x axis - * @param object The GameObject to separate - * @param tile The Tile to separate - * @returns {boolean} Whether the objects in fact touched and were separated along the X axis. - */ - separateTileX: function (object, x, y, width, height, mass, collideLeft, collideRight, separate) { - - // Can't separate two immovable objects (tiles are always immovable) - if (object.immovable) - { - return false; - } - - this._overlap = 0; - - // Do we have any overlap at all? - if (Phaser.Rectangle.intersectsRaw(object, x, x + width, y, y + height)) - { - this._maxOverlap = object.deltaAbsX() + this.OVERLAP_BIAS; - - if (object.deltaX() == 0) - { - // Object is either stuck inside the tile or only overlapping on the Y axis - } - else if (object.deltaX() > 0) - { - // Going right ... - - - - this._overlap = object.x + object.width - x; - - if ((this._overlap > this._maxOverlap) || !object.allowCollision.right || !collideLeft) - { - this._overlap = 0; - } - else - { - object.touching.right = true; - } - } - else if (object.deltaX() < 0) - { - // Going left ... - this._overlap = object.x - width - x; - - if ((-this._overlap > this._maxOverlap) || !object.allowCollision.left || !collideRight) - { - this._overlap = 0; - } - else - { - object.touching.left = true; - } - } - - if (this._overlap != 0) - { - if (separate) - { - // console.log('x over', this._overlap); - object.x = object.x - this._overlap; - - if (object.bounce.x == 0) - { - object.velocity.x = 0; - } - else - { - object.velocity.x = -object.velocity.x * object.bounce.x; - } - } - return true; - } - - } - - return false; - - }, - - /** - * Separates the two objects on their x axis - * @param object The GameObject to separate - * @param tile The Tile to separate - * @returns {boolean} Whether the objects in fact touched and were separated along the X axis. - */ - separateTileY: function (object, x, y, width, height, mass, collideUp, collideDown, separate) { - - // Can't separate two immovable objects (tiles are always immovable) - if (object.immovable) - { - return false; - } - - this._overlap = 0; - - if (Phaser.Rectangle.intersectsRaw(object, x, x + width, y, y + height)) - { - this._maxOverlap = object.deltaAbsY() + this.OVERLAP_BIAS; - - if (object.deltaY() == 0) - { - // Object is stuck inside a tile and not moving - } - else if (object.deltaY() > 0) - { - // Going down ... - this._overlap = object.bottom - y; - - // if (object.allowCollision.down && collideDown && this._overlap < this._maxOverlap) - if ((this._overlap > this._maxOverlap) || !object.allowCollision.down || !collideDown) - { - this._overlap = 0; - } - else - { - object.touching.down = true; - } - } - else if (object.deltaY() < 0) - { - // Going up ... - this._overlap = object.y - height - y; - - if ((-this._overlap > this._maxOverlap) || !object.allowCollision.up || !collideUp) - { - this._overlap = 0; - } - else - { - object.touching.up = true; - } - } - - if (this._overlap != 0) - { - // console.log('y over', this._overlap); - - if (separate) - { - object.y = object.y - this._overlap; - - if (object.bounce.y == 0) - { - object.velocity.y = 0; - } - else - { - object.velocity.y = -object.velocity.y * object.bounce.y; - } - } - return true; - } - } - - return false; - }, /** diff --git a/src/tilemap/Tile.js b/src/tilemap/Tile.js index 4d3efab1..fbdb26d6 100644 --- a/src/tilemap/Tile.js +++ b/src/tilemap/Tile.js @@ -100,10 +100,36 @@ Phaser.Tile = function (tileset, index, x, y, width, height) { */ this.separateY = true; + /** + * @property {boolean} collisionCallback - Tilemap collision callback. + * @default + */ + this.collisionCallback = null; + + /** + * @property {boolean} collisionCallback - Tilemap collision callback. + * @default + */ + this.collisionCallbackContext = this; + }; Phaser.Tile.prototype = { + /** + * Set callback to be called when this tilemap collides. + * + * @method Phaser.Tilemap.prototype.setCollisionCallback + * @param {Function} callback - Callback function. + * @param {object} context - Callback will be called with this context. + */ + setCollisionCallback: function (callback, context) { + + this.collisionCallbackContext = context; + this.collisionCallback = callback; + + }, + /** * Clean up memory. * @method destroy diff --git a/src/tilemap/Tilemap.js b/src/tilemap/Tilemap.js index 3f8b9e72..27ef38fc 100644 --- a/src/tilemap/Tilemap.js +++ b/src/tilemap/Tilemap.js @@ -21,6 +21,8 @@ Phaser.Tilemap = function (game, key) { this.layers = []; } + console.log(this.layers); + this.currentLayer = 0; this.debugMap = []; @@ -79,6 +81,23 @@ Phaser.Tilemap.prototype = { }, + /** + * 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 {number} x - X position of the point in target tile. + * @param {number} y - Y position of the point in target tile. + * @param {number} [layer] - layer of this tile located. + * @return {Tile} The tile with specific properties. + */ + getTileFromWorldXY: function (x, y, layer) { + + if (typeof layer === "undefined") { layer = this.currentLayer; } + + + + // return this.tiles[this.layers[layer].getTileFromWorldXY(x, y)]; + + }, + /** * Set a specific tile with its x and y in tiles. * @method putTile @@ -95,6 +114,38 @@ Phaser.Tilemap.prototype = { }, + /** + * Set a specific tile with its x and y in tiles. + * @method putTileWorldXY + * @param {number} x - X position of this tile in world coordinates. + * @param {number} y - Y position of this tile in world coordinates. + * @param {number} index - The index of this tile type in the core map data. + */ + putTileWorldXY: function (x, y, index) { + + x = this.game.math.snapToFloor(x, this.tileWidth) / this.tileWidth; + y = this.game.math.snapToFloor(y, this.tileHeight) / this.tileHeight; + + if (x >= 0 && x < this.layers[this.currentLayer].width && y >= 0 && y < this.layers[this.currentLayer].height) + { + this.layers[this.currentLayer].data[y][x] = index; + } + + }, + + + // swapTile + // fillTile + // randomiseTiles + // replaceTiles + + removeAllLayers: function () { + + this.layers.length = 0; + this.currentLayer = 0; + + }, + dump: function () { var txt = ''; @@ -129,6 +180,13 @@ Phaser.Tilemap.prototype = { args[0] = txt; console.log.apply(console, args); + }, + + destroy: function () { + + this.removeAllLayers(); + this.game = null; + } }; diff --git a/src/tilemap/TilemapLayer.js b/src/tilemap/TilemapLayer.js index cc548192..466026dc 100644 --- a/src/tilemap/TilemapLayer.js +++ b/src/tilemap/TilemapLayer.js @@ -106,6 +106,11 @@ Phaser.TilemapLayer = function (game, x, y, renderWidth, renderHeight, tileset, * @private */ this._ty = 0; + + this._results = []; + + this._tw = 0; + this._th = 0; /** * @property {number} _tl - Local render loop var to help avoid gc spikes. @@ -144,6 +149,8 @@ Phaser.TilemapLayer = function (game, x, y, renderWidth, renderHeight, tileset, this._y = 0; this._prevX = 0; this._prevY = 0; + + this.dirty = true; if (tileset instanceof Phaser.Tileset || typeof tileset === 'string') @@ -168,7 +175,11 @@ Phaser.TilemapLayer.prototype = { } else if (typeof tileset === 'string') { - this.tileset = this.game.cache.getTileset('tiles');; + this.tileset = this.game.cache.getTileset('tiles'); + } + else + { + return; } this.tileWidth = this.tileset.tileWidth; @@ -184,9 +195,85 @@ Phaser.TilemapLayer.prototype = { layer = 0; } - this.tilemap = tilemap; - this.layer = this.tilemap.layers[layer]; - this.updateMax(); + if (tilemap instanceof Phaser.Tilemap) + { + this.tilemap = tilemap; + this.layer = this.tilemap.layers[layer]; + this.updateMax(); + } + + }, + + /** + * + * @method getTileOverlaps + * @param {GameObject} object - Tiles you want to get that overlaps this. + * @return {array} Array with tiles informations (each contains x, y, and the tile). + */ + getTiles: function (x, y, width, height, collides) { + + if (this.tilemap === null) + { + return; + } + + // Should we only get tiles that have at least one of their collision flags set? (true = yes, false = no just get them all) + if (typeof collides === 'undefined') { collides = false; } + + // Cap the values + + if (x < 0) + { + x = 0; + } + + if (y < 0) + { + y = 0; + } + + if (width > this.widthInPixels) + { + width = this.widthInPixels; + } + + if (height > this.heightInPixels) + { + height = this.heightInPixels; + } + + // Convert the pixel values into tile coordinates + this._tx = this.game.math.snapToFloor(x, this.tileWidth) / this.tileWidth; + this._ty = this.game.math.snapToFloor(y, this.tileHeight) / this.tileHeight; + this._tw = (this.game.math.snapToCeil(width, this.tileWidth) + this.tileWidth) / this.tileWidth; + this._th = (this.game.math.snapToCeil(height, this.tileHeight) + this.tileHeight) / this.tileHeight; + + this._results.length = 0; + + this._results.push( { x: x, y: y, width: width, height: height, tx: this._tx, ty: this._ty, tw: this._tw, th: this._th }); + + var _index = 0; + var _tile = null; + + for (var wy = this._ty; wy < this._ty + this._th; wy++) + { + for (var wx = this._tx; wx < this._tx + this._tw; wx++) + { + if (this.layer.data[wy] && this.layer.data[wy][wx]) + { + // Could combine + _index = this.layer.data[wy][wx] - 1; + _tile = this.tileset.getTile(_index); + + if (collides == false || (collides && _tile.collideNone == false)) + { + this._results.push({ left: wx * _tile.width, right: (wx * _tile.width) + _tile.width, top: wy * _tile.height, bottom: (wy * _tile.height) + _tile.height, width: _tile.width, height: _tile.height, tx: wx, ty: wy, tile: _tile }); + } + } + } + } + + return this._results; }, diff --git a/src/tilemap/Tileset.js b/src/tilemap/Tileset.js index 7b05868e..6a0f781a 100644 --- a/src/tilemap/Tileset.js +++ b/src/tilemap/Tileset.js @@ -36,11 +36,43 @@ Phaser.Tileset.prototype = { }, + canCollide: function (index) { + + if (this.tiles[index]) + { + return this.tiles[index].collideNone; + } + + return null; + + }, + checkTileIndex: function (index) { return (this.tiles[index]); - } + }, + + setCollisionRange: function (start, stop, left, right, up, down) { + + if (this.tiles[start] && this.tiles[stop] && start < stop) + { + for (var i = start; i <= stop; i++) + { + this.tiles[i].setCollision(left, right, up, down); + } + } + + }, + + setCollision: function (index, left, right, up, down) { + + if (this.tiles[index]) + { + this.tiles[index].setCollision(left, right, up, down); + } + + }, }