From 2a5b6ef12afca182003e04c83020b565029454b1 Mon Sep 17 00:00:00 2001 From: Richard Davey Date: Thu, 2 May 2013 03:37:45 +0100 Subject: [PATCH] Large Tilemap collision overhaul. Proper callback support, optimised collision checks and lots more. --- Phaser/Collision.ts | 26 ++++-- Phaser/Group.ts | 34 +++---- Phaser/gameobjects/Tilemap.ts | 57 +++++++++--- Phaser/system/Tile.ts | 8 +- Phaser/system/TilemapLayer.ts | 25 ++--- README.md | 8 +- Tests/Tests.csproj | 4 + Tests/camera fx/mirror.js | 17 ++-- Tests/camera fx/mirror.ts | 10 +- Tests/phaser.js | 144 ++++++++++++++++++----------- Tests/tilemap/collide with tile.js | 64 +++++++++++++ Tests/tilemap/collide with tile.ts | 95 +++++++++++++++++++ Tests/tilemap/collision.js | 8 +- Tests/tilemap/collision.ts | 12 +-- build/phaser-fx.d.ts | 4 +- build/phaser-fx.js | 10 +- build/phaser.d.ts | 47 ++++++---- build/phaser.js | 144 ++++++++++++++++++----------- 18 files changed, 504 insertions(+), 213 deletions(-) create mode 100644 Tests/tilemap/collide with tile.js create mode 100644 Tests/tilemap/collide with tile.ts diff --git a/Phaser/Collision.ts b/Phaser/Collision.ts index 441b8bf7..81d0af44 100644 --- a/Phaser/Collision.ts +++ b/Phaser/Collision.ts @@ -645,10 +645,10 @@ module Phaser { * @param tile The Tile to separate * @returns {boolean} Whether the objects in fact touched and were separated */ - public static separateTile(object:GameObject, x: number, y: number, width: number, height: number, mass: number, collideLeft: bool, collideRight: bool, collideUp: bool, collideDown: bool): bool { + public static separateTile(object:GameObject, x: number, y: number, width: number, height: number, mass: number, collideLeft: bool, collideRight: bool, collideUp: bool, collideDown: bool, separateX: bool, separateY: bool): bool { - var separatedX: bool = Collision.separateTileX(object, x, y, width, height, mass, collideLeft, collideRight); - var separatedY: bool = Collision.separateTileY(object, x, y, width, height, mass, collideUp, collideDown); + var separatedX: bool = Collision.separateTileX(object, x, y, width, height, mass, collideLeft, collideRight, separateX); + var separatedY: bool = Collision.separateTileY(object, x, y, width, height, mass, collideUp, collideDown, separateY); return separatedX || separatedY; @@ -660,7 +660,7 @@ module Phaser { * @param tile The Tile to separate * @returns {boolean} Whether the objects in fact touched and were separated along the X axis. */ - public static separateTileX(object:GameObject, x: number, y: number, width: number, height: number, mass: number, collideLeft: bool, collideRight: bool): bool { + public static separateTileX(object:GameObject, x: number, y: number, width: number, height: number, mass: number, collideLeft: bool, collideRight: bool, separate: bool): bool { // Can't separate two immovable objects (tiles are always immovable) if (object.immovable) @@ -717,8 +717,12 @@ module Phaser { // Then adjust their positions and velocities accordingly (if there was any overlap) if (overlap != 0) { - object.x = object.x - overlap; - object.velocity.x = -(object.velocity.x * object.elasticity); + if (separate == true) + { + object.x = object.x - overlap; + object.velocity.x = -(object.velocity.x * object.elasticity); + } + Collision.TILE_OVERLAP = true; return true; } @@ -735,7 +739,7 @@ module Phaser { * @param tile The second GameObject to separate * @returns {boolean} Whether the objects in fact touched and were separated along the Y axis. */ - public static separateTileY(object: GameObject, x: number, y: number, width: number, height: number, mass: number, collideUp: bool, collideDown: bool): bool { + public static separateTileY(object: GameObject, x: number, y: number, width: number, height: number, mass: number, collideUp: bool, collideDown: bool, separate: bool): bool { // Can't separate two immovable objects (tiles are always immovable) if (object.immovable) @@ -792,8 +796,12 @@ module Phaser { // Then adjust their positions and velocities accordingly (if there was any overlap) if (overlap != 0) { - object.y = object.y - overlap; - object.velocity.y = -(object.velocity.y * object.elasticity); + if (separate == true) + { + object.y = object.y - overlap; + object.velocity.y = -(object.velocity.y * object.elasticity); + } + Collision.TILE_OVERLAP = true; return true; } diff --git a/Phaser/Group.ts b/Phaser/Group.ts index 4ad0dd7f..1b6ea0bd 100644 --- a/Phaser/Group.ts +++ b/Phaser/Group.ts @@ -321,21 +321,21 @@ module Phaser { /** * Removes an object from the group. * - * @param Object The Basic you want to remove. - * @param Splice Whether the object should be cut from the array entirely or not. + * @param object The Basic you want to remove. + * @param splice Whether the object should be cut from the array entirely or not. * * @return The removed object. */ - public remove(Object: Basic, Splice: bool = false): Basic { + public remove(object: Basic, splice: bool = false): Basic { - var index: number = this.members.indexOf(Object); + var index: number = this.members.indexOf(object); if ((index < 0) || (index >= this.members.length)) { return null; } - if (Splice) + if (splice) { this.members.splice(index, 1); this.length--; @@ -345,30 +345,30 @@ module Phaser { this.members[index] = null; } - return Object; + return object; } /** * Replaces an existing Basic with a new one. * - * @param OldObject The object you want to replace. - * @param NewObject The new object you want to use instead. + * @param oldObject The object you want to replace. + * @param newObject The new object you want to use instead. * * @return The new object. */ - public replace(OldObject: Basic, NewObject: Basic): Basic { + public replace(oldObject: Basic, newObject: Basic): Basic { - var index: number = this.members.indexOf(OldObject); + var index: number = this.members.indexOf(oldObject); if ((index < 0) || (index >= this.members.length)) { return null; } - this.members[index] = NewObject; + this.members[index] = newObject; - return NewObject; + return newObject; } @@ -379,13 +379,13 @@ module Phaser { * State.update() override. To sort all existing objects after * a big explosion or bomb attack, you might call myGroup.sort("exists",Group.DESCENDING). * - * @param Index The string name of the member variable you want to sort on. Default value is "y". - * @param Order A Group constant that defines the sort order. Possible values are Group.ASCENDING and Group.DESCENDING. Default value is Group.ASCENDING. + * @param index The string name of the member variable you want to sort on. Default value is "y". + * @param order A Group constant that defines the sort order. Possible values are Group.ASCENDING and Group.DESCENDING. Default value is Group.ASCENDING. */ - public sort(Index: string = "y", Order: number = Group.ASCENDING) { + public sort(index: string = "y", order: number = Group.ASCENDING) { - this._sortIndex = Index; - this._sortOrder = Order; + this._sortIndex = index; + this._sortOrder = order; this.members.sort(this.sortHandler); } diff --git a/Phaser/gameobjects/Tilemap.ts b/Phaser/gameobjects/Tilemap.ts index 78d31d0e..9e24c3c5 100644 --- a/Phaser/gameobjects/Tilemap.ts +++ b/Phaser/gameobjects/Tilemap.ts @@ -43,6 +43,8 @@ module Phaser { } + private _tempCollisionData; + public static FORMAT_CSV: number = 0; public static FORMAT_TILED_JSON: number = 1; @@ -50,6 +52,8 @@ module Phaser { public layers : TilemapLayer[]; public currentLayer: TilemapLayer; public collisionLayer: TilemapLayer; + public collisionCallback = null; + public collisionCallbackContext; public mapFormat: number; public update() { @@ -170,26 +174,44 @@ module Phaser { // Tile Collision - public setCollisionRange(start: number, end: number, collision?:number = Collision.ANY, resetCollisions: bool = false) { + public setCollisionCallback(context, callback) { + + this.collisionCallbackContext = context; + this.collisionCallback = callback; + + } + + public setCollisionRange(start: number, end: number, collision?:number = Collision.ANY, resetCollisions?: bool = false, separateX?: bool = true, separateY?: bool = true) { for (var i = start; i < end; i++) { - this.tiles[i].setCollision(collision, resetCollisions); + this.tiles[i].setCollision(collision, resetCollisions, separateX, separateY); } } - public setCollisionByIndex(values:number[], collision?:number = Collision.ANY, resetCollisions: bool = false) { + public setCollisionByIndex(values:number[], collision?:number = Collision.ANY, resetCollisions?: bool = false, separateX?: bool = true, separateY?: bool = true) { for (var i = 0; i < values.length; i++) { - this.tiles[values[i]].setCollision(collision, resetCollisions); + this.tiles[values[i]].setCollision(collision, resetCollisions, separateX, separateY); } } // Tile Management + public getTileByIndex(value: number):Tile { + + if (this.tiles[value]) + { + return this.tiles[value]; + } + + return null; + + } + public getTile(x: number, y: number, layer?: number = 0):Tile { return this.tiles[this.layers[layer].getTileIndex(x, y)]; @@ -215,7 +237,13 @@ module Phaser { } // COLLIDE - public collide(objectOrGroup = null, callback = null): bool { + public collide(objectOrGroup = null, callback = null, context = null) { + + if (callback !== null && context !== null) + { + this.collisionCallback = callback; + this.collisionCallbackContext = context; + } if (objectOrGroup == null) { @@ -225,24 +253,27 @@ module Phaser { // Group? if (objectOrGroup.isGroup == false) { - return this.collideGameObject(objectOrGroup); + this.collideGameObject(objectOrGroup); } else { objectOrGroup.forEachAlive(this, this.collideGameObject, true); } - return true; - } public collideGameObject(object: GameObject): bool { - if (object == this) { return false; } - - if (object.immovable == false && object.exists == true && object.allowCollisions != Collision.NONE) + if (object !== this && object.immovable == false && object.exists == true && object.allowCollisions != Collision.NONE) { - return this.collisionLayer.getTileOverlaps(object); + 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 { @@ -251,7 +282,6 @@ module Phaser { } - // Set current layer // Set layer order? // Get block of tiles @@ -259,7 +289,6 @@ module Phaser { // Delete tiles of certain type // Erase tiles - } } \ No newline at end of file diff --git a/Phaser/system/Tile.ts b/Phaser/system/Tile.ts index 748e7cfe..426bf03b 100644 --- a/Phaser/system/Tile.ts +++ b/Phaser/system/Tile.ts @@ -38,6 +38,9 @@ module Phaser { public collideUp: bool = false; public collideDown: bool = false; + public separateX: bool = true; + public separateY: bool = true; + /** * A reference to the tilemap this tile object belongs to. */ @@ -59,13 +62,16 @@ module Phaser { } - public setCollision(collision: number, resetCollisions: bool) { + public setCollision(collision: number, resetCollisions: bool, separateX: bool, separateY: bool) { if (resetCollisions) { this.resetCollision(); } + this.separateX = separateX; + this.separateY = separateY; + this.allowCollisions = collision; if (collision & Collision.ANY) diff --git a/Phaser/system/TilemapLayer.ts b/Phaser/system/TilemapLayer.ts index f334469e..95657e81 100644 --- a/Phaser/system/TilemapLayer.ts +++ b/Phaser/system/TilemapLayer.ts @@ -23,6 +23,7 @@ module Phaser { //this.scrollFactor = new MicroPoint(1, 1); this.mapData = []; + this._tempTileBlock = []; this._texture = this._game.cache.getImage(key); } @@ -47,6 +48,8 @@ module Phaser { private _tempTileY: number; private _tempTileW: number; private _tempTileH: number; + private _tempTileBlock; + private _tempBlockResults; public name: string; public alpha: number = 1; @@ -95,20 +98,20 @@ module Phaser { this._tempTileW = (this._game.math.snapToCeil(object.bounds.width, this.tileWidth) + this.tileWidth) / this.tileWidth; this._tempTileH = (this._game.math.snapToCeil(object.bounds.height, this.tileHeight) + this.tileHeight) / this.tileHeight; - // Loop through the tiles we've got and check overlaps accordingly - var tiles = this.getTileBlock(this._tempTileX, this._tempTileY, this._tempTileW, this._tempTileH); + // Loop through the tiles we've got and check overlaps accordingly (the results are stored in this._tempTileBlock) + this.getTileBlock(this._tempTileX, this._tempTileY, this._tempTileW, this._tempTileH); Collision.TILE_OVERLAP = false; - for (var r = 0; r < tiles.length; r++) + for (var r = 0; r < this._tempTileBlock.length; r++) { - if (tiles[r].tile.allowCollisions != Collision.NONE) + if (Collision.separateTile(object, this._tempTileBlock[r].x * this.tileWidth, this._tempTileBlock[r].y * this.tileHeight, this.tileWidth, this.tileHeight, this._tempTileBlock[r].tile.mass, this._tempTileBlock[r].tile.collideLeft, this._tempTileBlock[r].tile.collideRight, this._tempTileBlock[r].tile.collideUp, this._tempTileBlock[r].tile.collideDown, this._tempTileBlock[r].tile.separateX, this._tempTileBlock[r].tile.separateY) == true) { - Collision.separateTile(object, tiles[r].x * this.tileWidth, tiles[r].y * this.tileHeight, this.tileWidth, this.tileHeight, tiles[r].tile.mass, tiles[r].tile.collideLeft, tiles[r].tile.collideRight, tiles[r].tile.collideUp, tiles[r].tile.collideDown); + this._tempBlockResults.push({ x: this._tempTileBlock[r].x, y: this._tempTileBlock[r].y, tile: this._tempTileBlock[r].tile }); } } - return Collision.TILE_OVERLAP; + return this._tempBlockResults; } @@ -134,21 +137,21 @@ module Phaser { height = this.heightInTiles; } - var output = []; + this._tempTileBlock = []; + this._tempBlockResults = []; for (var ty = y; ty < y + height; ty++) { for (var tx = x; tx < x + width; tx++) { - if (this.mapData[ty] && this.mapData[ty][tx]) + // We only want to consider the tile for checking if you can actually collide with it + if (this.mapData[ty] && this.mapData[ty][tx] && this._parent.tiles[this.mapData[ty][tx]].allowCollisions != Collision.NONE) { - output.push({ x: tx, y: ty, tile: this._parent.tiles[this.mapData[ty][tx]] }); + this._tempTileBlock.push({ x: tx, y: ty, tile: this._parent.tiles[this.mapData[ty][tx]] }); } } } - return output; - } public getTileIndex(x: number, y: number): number { diff --git a/README.md b/README.md index 69fc97dd..c25b7c06 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,13 @@ V0.9.5 * Fixed offset values being ignored in GeomSprite.renderPoint (thanks bapuna). * Added new Mirror Camera FX. Can mirror the camera image horizontally, vertically or both with an optional fill color overlay. * Added Camera.disableClipping for when you don't care about things being drawn outside the edge (usful for some FX). - +* Updated TilemapLayer so that collision data is now stored in _tempTileBlock to avoid constant array creation during game loop. +* TilemapLayer.getTileOverlaps() now returns all tiles the object overlapped with rather than just a boolean. +* Tilemap.collide now optionally takes callback and context parameters which are used if collision occurs. +* Added Tilemap.collisionCallback and Tilemap.collisionCallbackContext so you can set them once and not re-set them on every call to collide. +* Collision.separateTile now has 2 extra parameters: separateX and separateY. If true the object will be separated on overlap, otherwise just the overlap boolean result is returned. +* Added Tile.separateX and Tile.separateY (both true by default), if true an object will be separated from the tile on overlap. Set to false if you don't want a tile to stop an object from moving, you just want it to return collision data to your callback. +* Added Tilemap.getTileByIndex(value) to access a specific type of tile, rather than by its map index. diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index a33830a4..c68b56e8 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -152,6 +152,10 @@ + + + collide with tile.ts + collision.ts diff --git a/Tests/camera fx/mirror.js b/Tests/camera fx/mirror.js index f32b4105..f5adab9e 100644 --- a/Tests/camera fx/mirror.js +++ b/Tests/camera fx/mirror.js @@ -3,26 +3,27 @@ (function () { var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); function init() { + // Just set the world to be the size of the image we're loading in myGame.world.setSize(1216, 896); myGame.loader.addImageFile('backdrop', 'assets/pics/ninja-masters2.png'); myGame.loader.load(); } var mirror; - var cam; function create() { - // What we need is a camera 800x400 pixels in size, the mirror effect will sit below it. + // What we need is a camera 800x400 pixels in size as the mirror effect will be 200px tall and sit below it. // So we resize our default camera to 400px myGame.camera.height = 400; - // Because it's our default camera we need to tell it to disable clipping, otherwise we'll never see the mirror effect + // Because it's our default camera we need to tell it to disable clipping, otherwise we'll never see the mirror effect render. myGame.camera.disableClipping = true; // Add our effect to the camera mirror = myGame.camera.fx.add(Phaser.FX.Camera.Mirror); - // The first 2 parameters are the x and y coordinates we're going to display the mirror effect in STAGE coordinates - // The next is the rectangular region of the Camera that we'll create the effect from (in this case the whole camera) - // Finally we set the fill color that is put over the top of the mirror effect + // The first 2 parameters are the x and y coordinates of where to display the effect. They are in STAGE coordinates, not World. + // The next is a Quad making up the rectangular region of the Camera that we'll create the effect from (in this case the whole camera). + // Finally we set the fill color that is put over the top of the mirror effect. mirror.start(0, 400, new Phaser.Quad(0, 0, 800, 400), 'rgba(0, 0, 100, 0.7)'); - mirror.flipX = true; - mirror.flipY = true; + // Experiment with variations on these to see the different mirror effects that can be achieved. + //mirror.flipX = true; + //mirror.flipY = true; myGame.createSprite(0, 0, 'backdrop'); } function update() { diff --git a/Tests/camera fx/mirror.ts b/Tests/camera fx/mirror.ts index 95158467..be6e0adb 100644 --- a/Tests/camera fx/mirror.ts +++ b/Tests/camera fx/mirror.ts @@ -20,19 +20,19 @@ function create() { - // What we need is a camera 800x400 pixels in size, the mirror effect will sit below it. + // What we need is a camera 800x400 pixels in size as the mirror effect will be 200px tall and sit below it. // So we resize our default camera to 400px myGame.camera.height = 400; - // Because it's our default camera we need to tell it to disable clipping, otherwise we'll never see the mirror effect + // Because it's our default camera we need to tell it to disable clipping, otherwise we'll never see the mirror effect render. myGame.camera.disableClipping = true; // Add our effect to the camera mirror = myGame.camera.fx.add(Phaser.FX.Camera.Mirror); - // The first 2 parameters are the x and y coordinates we're going to display the mirror effect in STAGE coordinates - // The next is the rectangular region of the Camera that we'll create the effect from (in this case the whole camera) - // Finally we set the fill color that is put over the top of the mirror effect + // The first 2 parameters are the x and y coordinates of where to display the effect. They are in STAGE coordinates, not World. + // The next is a Quad making up the rectangular region of the Camera that we'll create the effect from (in this case the whole camera). + // Finally we set the fill color that is put over the top of the mirror effect. mirror.start(0, 400, new Phaser.Quad(0, 0, 800, 400), 'rgba(0, 0, 100, 0.7)'); // Experiment with variations on these to see the different mirror effects that can be achieved. diff --git a/Tests/phaser.js b/Tests/phaser.js index 5ac07e62..4f038bfb 100644 --- a/Tests/phaser.js +++ b/Tests/phaser.js @@ -4827,9 +4827,9 @@ var Phaser; * @param tile The Tile to separate * @returns {boolean} Whether the objects in fact touched and were separated */ - function separateTile(object, x, y, width, height, mass, collideLeft, collideRight, collideUp, collideDown) { - var separatedX = Collision.separateTileX(object, x, y, width, height, mass, collideLeft, collideRight); - var separatedY = Collision.separateTileY(object, x, y, width, height, mass, collideUp, collideDown); + function separateTile(object, x, y, width, height, mass, collideLeft, collideRight, collideUp, collideDown, separateX, separateY) { + var separatedX = Collision.separateTileX(object, x, y, width, height, mass, collideLeft, collideRight, separateX); + var separatedY = Collision.separateTileY(object, x, y, width, height, mass, collideUp, collideDown, separateY); return separatedX || separatedY; }; Collision.separateTileX = /** @@ -4838,7 +4838,7 @@ var Phaser; * @param tile The Tile to separate * @returns {boolean} Whether the objects in fact touched and were separated along the X axis. */ - function separateTileX(object, x, y, width, height, mass, collideLeft, collideRight) { + function separateTileX(object, x, y, width, height, mass, collideLeft, collideRight, separate) { // Can't separate two immovable objects (tiles are always immovable) if(object.immovable) { return false; @@ -4872,8 +4872,10 @@ var Phaser; } // Then adjust their positions and velocities accordingly (if there was any overlap) if(overlap != 0) { - object.x = object.x - overlap; - object.velocity.x = -(object.velocity.x * object.elasticity); + if(separate == true) { + object.x = object.x - overlap; + object.velocity.x = -(object.velocity.x * object.elasticity); + } Collision.TILE_OVERLAP = true; return true; } else { @@ -4886,7 +4888,7 @@ var Phaser; * @param tile The second GameObject to separate * @returns {boolean} Whether the objects in fact touched and were separated along the Y axis. */ - function separateTileY(object, x, y, width, height, mass, collideUp, collideDown) { + function separateTileY(object, x, y, width, height, mass, collideUp, collideDown, separate) { // Can't separate two immovable objects (tiles are always immovable) if(object.immovable) { return false; @@ -4921,8 +4923,10 @@ var Phaser; // TODO - with super low velocities you get lots of stuttering, set some kind of base minimum here // Then adjust their positions and velocities accordingly (if there was any overlap) if(overlap != 0) { - object.y = object.y - overlap; - object.velocity.y = -(object.velocity.y * object.elasticity); + if(separate == true) { + object.y = object.y - overlap; + object.velocity.y = -(object.velocity.y * object.elasticity); + } Collision.TILE_OVERLAP = true; return true; } else { @@ -6341,40 +6345,40 @@ var Phaser; Group.prototype.remove = /** * Removes an object from the group. * - * @param Object The Basic you want to remove. - * @param Splice Whether the object should be cut from the array entirely or not. + * @param object The Basic you want to remove. + * @param splice Whether the object should be cut from the array entirely or not. * * @return The removed object. */ - function (Object, Splice) { - if (typeof Splice === "undefined") { Splice = false; } - var index = this.members.indexOf(Object); + function (object, splice) { + if (typeof splice === "undefined") { splice = false; } + var index = this.members.indexOf(object); if((index < 0) || (index >= this.members.length)) { return null; } - if(Splice) { + if(splice) { this.members.splice(index, 1); this.length--; } else { this.members[index] = null; } - return Object; + return object; }; Group.prototype.replace = /** * Replaces an existing Basic with a new one. * - * @param OldObject The object you want to replace. - * @param NewObject The new object you want to use instead. + * @param oldObject The object you want to replace. + * @param newObject The new object you want to use instead. * * @return The new object. */ - function (OldObject, NewObject) { - var index = this.members.indexOf(OldObject); + function (oldObject, newObject) { + var index = this.members.indexOf(oldObject); if((index < 0) || (index >= this.members.length)) { return null; } - this.members[index] = NewObject; - return NewObject; + this.members[index] = newObject; + return newObject; }; Group.prototype.sort = /** * Call this function to sort the group according to a particular value and order. @@ -6383,14 +6387,14 @@ var Phaser; * State.update() override. To sort all existing objects after * a big explosion or bomb attack, you might call myGroup.sort("exists",Group.DESCENDING). * - * @param Index The string name of the member variable you want to sort on. Default value is "y". - * @param Order A Group constant that defines the sort order. Possible values are Group.ASCENDING and Group.DESCENDING. Default value is Group.ASCENDING. + * @param index The string name of the member variable you want to sort on. Default value is "y". + * @param order A Group constant that defines the sort order. Possible values are Group.ASCENDING and Group.DESCENDING. Default value is Group.ASCENDING. */ - function (Index, Order) { - if (typeof Index === "undefined") { Index = "y"; } - if (typeof Order === "undefined") { Order = Group.ASCENDING; } - this._sortIndex = Index; - this._sortOrder = Order; + function (index, order) { + if (typeof index === "undefined") { index = "y"; } + if (typeof order === "undefined") { order = Group.ASCENDING; } + this._sortIndex = index; + this._sortOrder = order; this.members.sort(this.sortHandler); }; Group.prototype.setAll = /** @@ -11151,6 +11155,7 @@ var Phaser; this.boundsInTiles = new Phaser.Rectangle(); //this.scrollFactor = new MicroPoint(1, 1); this.mapData = []; + this._tempTileBlock = []; this._texture = this._game.cache.getImage(key); } TilemapLayer.prototype.getTileFromWorldXY = function (x, y) { @@ -11168,15 +11173,19 @@ var Phaser; this._tempTileY = this._game.math.snapToFloor(object.bounds.y, this.tileHeight) / this.tileHeight; this._tempTileW = (this._game.math.snapToCeil(object.bounds.width, this.tileWidth) + this.tileWidth) / this.tileWidth; this._tempTileH = (this._game.math.snapToCeil(object.bounds.height, this.tileHeight) + this.tileHeight) / this.tileHeight; - // Loop through the tiles we've got and check overlaps accordingly - var tiles = this.getTileBlock(this._tempTileX, this._tempTileY, this._tempTileW, this._tempTileH); + // Loop through the tiles we've got and check overlaps accordingly (the results are stored in this._tempTileBlock) + this.getTileBlock(this._tempTileX, this._tempTileY, this._tempTileW, this._tempTileH); Phaser.Collision.TILE_OVERLAP = false; - for(var r = 0; r < tiles.length; r++) { - if(tiles[r].tile.allowCollisions != Phaser.Collision.NONE) { - Phaser.Collision.separateTile(object, tiles[r].x * this.tileWidth, tiles[r].y * this.tileHeight, this.tileWidth, this.tileHeight, tiles[r].tile.mass, tiles[r].tile.collideLeft, tiles[r].tile.collideRight, tiles[r].tile.collideUp, tiles[r].tile.collideDown); + for(var r = 0; r < this._tempTileBlock.length; r++) { + if(Phaser.Collision.separateTile(object, this._tempTileBlock[r].x * this.tileWidth, this._tempTileBlock[r].y * this.tileHeight, this.tileWidth, this.tileHeight, this._tempTileBlock[r].tile.mass, this._tempTileBlock[r].tile.collideLeft, this._tempTileBlock[r].tile.collideRight, this._tempTileBlock[r].tile.collideUp, this._tempTileBlock[r].tile.collideDown, this._tempTileBlock[r].tile.separateX, this._tempTileBlock[r].tile.separateY) == true) { + this._tempBlockResults.push({ + x: this._tempTileBlock[r].x, + y: this._tempTileBlock[r].y, + tile: this._tempTileBlock[r].tile + }); } } - return Phaser.Collision.TILE_OVERLAP; + return this._tempBlockResults; }; TilemapLayer.prototype.getTileBlock = function (x, y, width, height) { if(x < 0) { @@ -11191,11 +11200,13 @@ var Phaser; if(height > this.heightInTiles) { height = this.heightInTiles; } - var output = []; + this._tempTileBlock = []; + this._tempBlockResults = []; for(var ty = y; ty < y + height; ty++) { for(var tx = x; tx < x + width; tx++) { - if(this.mapData[ty] && this.mapData[ty][tx]) { - output.push({ + // We only want to consider the tile for checking if you can actually collide with it + if(this.mapData[ty] && this.mapData[ty][tx] && this._parent.tiles[this.mapData[ty][tx]].allowCollisions != Phaser.Collision.NONE) { + this._tempTileBlock.push({ x: tx, y: ty, tile: this._parent.tiles[this.mapData[ty][tx]] @@ -11203,7 +11214,6 @@ var Phaser; } } } - return output; }; TilemapLayer.prototype.getTileIndex = function (x, y) { if(y >= 0 && y < this.mapData.length) { @@ -11350,6 +11360,8 @@ var Phaser; this.collideRight = false; this.collideUp = false; this.collideDown = false; + this.separateX = true; + this.separateY = true; this._game = game; this.tilemap = tilemap; this.index = index; @@ -11363,10 +11375,12 @@ var Phaser; function () { this.tilemap = null; }; - Tile.prototype.setCollision = function (collision, resetCollisions) { + Tile.prototype.setCollision = function (collision, resetCollisions, separateX, separateY) { if(resetCollisions) { this.resetCollision(); } + this.separateX = separateX; + this.separateY = separateY; this.allowCollisions = collision; if(collision & Phaser.Collision.ANY) { this.collideLeft = true; @@ -11426,6 +11440,7 @@ var Phaser; if (typeof tileWidth === "undefined") { tileWidth = 0; } if (typeof tileHeight === "undefined") { tileHeight = 0; } _super.call(this, game); + this.collisionCallback = null; this.isGroup = false; this.tiles = []; this.layers = []; @@ -11522,23 +11537,37 @@ var Phaser; enumerable: true, configurable: true }); - Tilemap.prototype.setCollisionRange = // Tile Collision - function (start, end, collision, resetCollisions) { + Tilemap.prototype.setCollisionCallback = // Tile Collision + function (context, callback) { + this.collisionCallbackContext = context; + this.collisionCallback = callback; + }; + Tilemap.prototype.setCollisionRange = function (start, end, collision, resetCollisions, separateX, separateY) { if (typeof collision === "undefined") { collision = Phaser.Collision.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); + this.tiles[i].setCollision(collision, resetCollisions, separateX, separateY); } }; - Tilemap.prototype.setCollisionByIndex = function (values, collision, resetCollisions) { + Tilemap.prototype.setCollisionByIndex = function (values, collision, resetCollisions, separateX, separateY) { if (typeof collision === "undefined") { collision = Phaser.Collision.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); + this.tiles[values[i]].setCollision(collision, resetCollisions, separateX, separateY); } }; - Tilemap.prototype.getTile = // Tile Management - function (x, y, layer) { + Tilemap.prototype.getTileByIndex = // Tile Management + function (value) { + if(this.tiles[value]) { + return this.tiles[value]; + } + return null; + }; + Tilemap.prototype.getTile = function (x, y, layer) { if (typeof layer === "undefined") { layer = 0; } return this.tiles[this.layers[layer].getTileIndex(x, y)]; }; @@ -11554,26 +11583,31 @@ var Phaser; return this.currentLayer.getTileOverlaps(object); }; Tilemap.prototype.collide = // COLLIDE - function (objectOrGroup, callback) { + 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) { - return this.collideGameObject(objectOrGroup); + this.collideGameObject(objectOrGroup); } else { objectOrGroup.forEachAlive(this, this.collideGameObject, true); } - return true; }; Tilemap.prototype.collideGameObject = function (object) { - if(object == this) { - return false; - } - if(object.immovable == false && object.exists == true && object.allowCollisions != Phaser.Collision.NONE) { - return this.collisionLayer.getTileOverlaps(object); + if(object !== this && object.immovable == false && object.exists == true && object.allowCollisions != Phaser.Collision.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; } diff --git a/Tests/tilemap/collide with tile.js b/Tests/tilemap/collide with tile.js new file mode 100644 index 00000000..0c2a3e9d --- /dev/null +++ b/Tests/tilemap/collide with tile.js @@ -0,0 +1,64 @@ +/// +/// +/// +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.loader.addTextFile('desert', 'assets/maps/desert.json'); + myGame.loader.addImageFile('tiles', 'assets/tiles/tmw_desert_spacing.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + myGame.loader.load(); + } + var map; + var car; + var tile; + var flash; + function create() { + map = myGame.createTilemap('tiles', 'desert', Phaser.Tilemap.FORMAT_TILED_JSON); + // When the car collides with the cactus tile we'll flash the screen red briefly, + // but it won't stop the car (the separateX/Y values are set to false) + map.setCollisionByIndex([ + 31 + ], Phaser.Collision.ANY, true, false, false); + // When the car collides with the sign post tile we'll stop the car moving (separation is set to true) + map.setCollisionByIndex([ + 46 + ], Phaser.Collision.ANY, true, true, true); + // This is the callback that will be called every time map.collide() returns true + map.collisionCallback = collide; + // This is the context in which the callback is called (usually 'this' if you want to be able to access local vars) + map.collisionCallbackContext = this; + car = myGame.createSprite(250, 200, 'car'); + car.setBounds(0, 0, map.widthInPixels - 32, map.heightInPixels - 32); + myGame.camera.follow(car); + flash = myGame.camera.fx.add(Phaser.FX.Camera.Flash); + } + function update() { + // Collide the car object with the tilemap + // It's important to do this BEFORE you adjust the object velocity (below) otherwise it can jitter around a lot + map.collide(car); + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + car.angularVelocity = -200; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + car.angularVelocity = 200; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 300)); + } + } + function collide(object, collisionData) { + // collisionData is an array containing all of the tiles the object overlapped with (can be more than 1) + for(var i = 0; i < collisionData.length; i++) { + if(collisionData[i].tile.index == 31) { + console.log('you hit a cactus!'); + flash.start(0xff0000, 1); + } else if(collisionData[i].tile.index == 31) { + console.log('you hit a sign post!'); + } + } + } +})(); diff --git a/Tests/tilemap/collide with tile.ts b/Tests/tilemap/collide with tile.ts new file mode 100644 index 00000000..396ed9e3 --- /dev/null +++ b/Tests/tilemap/collide with tile.ts @@ -0,0 +1,95 @@ +/// +/// +/// +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.loader.addTextFile('desert', 'assets/maps/desert.json'); + myGame.loader.addImageFile('tiles', 'assets/tiles/tmw_desert_spacing.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + + myGame.loader.load(); + + } + + var map: Phaser.Tilemap; + var car: Phaser.Sprite; + var tile: Phaser.Tile; + var flash: Phaser.FX.Camera.Flash; + + function create() { + + map = myGame.createTilemap('tiles', 'desert', Phaser.Tilemap.FORMAT_TILED_JSON); + + // When the car collides with the cactus tile we'll flash the screen red briefly, + // but it won't stop the car (the separateX/Y values are set to false) + map.setCollisionByIndex([31], Phaser.Collision.ANY, true, false, false); + + // When the car collides with the sign post tile we'll stop the car moving (separation is set to true) + map.setCollisionByIndex([46], Phaser.Collision.ANY, true, true, true); + + // This is the callback that will be called every time map.collide() returns true + map.collisionCallback = collide; + + // This is the context in which the callback is called (usually 'this' if you want to be able to access local vars) + map.collisionCallbackContext = this; + + car = myGame.createSprite(250, 200, 'car'); + car.setBounds(0, 0, map.widthInPixels - 32, map.heightInPixels - 32); + + myGame.camera.follow(car); + + flash = myGame.camera.fx.add(Phaser.FX.Camera.Flash); + + } + + function update() { + + // Collide the car object with the tilemap + // It's important to do this BEFORE you adjust the object velocity (below) otherwise it can jitter around a lot + map.collide(car); + + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + car.angularVelocity = -200; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + car.angularVelocity = 200; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 300)); + } + + } + + function collide(object, collisionData) { + + // collisionData is an array containing all of the tiles the object overlapped with (can be more than 1) + for (var i = 0; i < collisionData.length; i++) + { + if (collisionData[i].tile.index == 31) + { + console.log('you hit a cactus!'); + flash.start(0xff0000, 1); + } + else if (collisionData[i].tile.index == 31) + { + console.log('you hit a sign post!'); + } + } + + } + +})(); diff --git a/Tests/tilemap/collision.js b/Tests/tilemap/collision.js index ddde5001..45eddaa9 100644 --- a/Tests/tilemap/collision.js +++ b/Tests/tilemap/collision.js @@ -41,6 +41,10 @@ car.setBounds(0, 0, map.widthInPixels - 32, map.heightInPixels - 32); } function update() { + // Collide everything with the map + map.collide(); + // And collide everything in the game :) + myGame.collide(); car.velocity.x = 0; car.velocity.y = 0; if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { @@ -53,9 +57,5 @@ } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.DOWN)) { car.velocity.y = 200; } - // Collide everything with the map - map.collide(); - // And collide everything in the game :) - myGame.collide(); } })(); diff --git a/Tests/tilemap/collision.ts b/Tests/tilemap/collision.ts index 2b719f38..744c7c08 100644 --- a/Tests/tilemap/collision.ts +++ b/Tests/tilemap/collision.ts @@ -54,6 +54,12 @@ function update() { + // Collide everything with the map + map.collide(); + + // And collide everything in the game :) + myGame.collide(); + car.velocity.x = 0; car.velocity.y = 0; @@ -75,12 +81,6 @@ car.velocity.y = 200; } - // Collide everything with the map - map.collide(); - - // And collide everything in the game :) - myGame.collide(); - } })(); diff --git a/build/phaser-fx.d.ts b/build/phaser-fx.d.ts index da160062..87feeb0f 100644 --- a/build/phaser-fx.d.ts +++ b/build/phaser-fx.d.ts @@ -66,8 +66,8 @@ module Phaser.FX.Camera { /** * Phaser - FX - Camera - Mirror * -* A Template FX file you can use to create your own Camera FX. -* If you don't use any of the methods below (i.e. preUpdate, render, etc) then DELETE THEM to avoid un-necessary calls by the FXManager. +* Creates a mirror effect for a camera. +* Can mirror the camera image horizontally, vertically or both with an optional fill color overlay. */ module Phaser.FX.Camera { class Mirror { diff --git a/build/phaser-fx.js b/build/phaser-fx.js index bde85275..c784aa72 100644 --- a/build/phaser-fx.js +++ b/build/phaser-fx.js @@ -137,8 +137,8 @@ var Phaser; /** * Phaser - FX - Camera - Mirror * - * A Template FX file you can use to create your own Camera FX. - * If you don't use any of the methods below (i.e. preUpdate, render, etc) then DELETE THEM to avoid un-necessary calls by the FXManager. + * Creates a mirror effect for a camera. + * Can mirror the camera image horizontally, vertically or both with an optional fill color overlay. */ (function (Camera) { var Mirror = (function () { @@ -159,7 +159,7 @@ var Phaser; * It is rendered to the Stage at Mirror.x/y (note the use of Stage coordinates, not World coordinates) */ function (x, y, region, fillColor) { - if (typeof fillColor === "undefined") { fillColor = 'rgba(0,0,100,0.5)'; } + if (typeof fillColor === "undefined") { fillColor = 'rgba(0, 0, 100, 0.5)'; } this.x = x; this.y = y; this._mirrorX = region.x; @@ -199,7 +199,6 @@ var Phaser; if(this._mirrorColor) { this._context.fillRect(0, 0, this._mirrorWidth, this._mirrorHeight); } - //this._game.stage.context.save(); if(this.flipX && this.flipY) { this._game.stage.context.transform(-1, 0, 0, -1, this._mirrorWidth, this._mirrorHeight); this._game.stage.context.drawImage(this._canvas, -this.x, -this.y); @@ -210,8 +209,7 @@ var Phaser; this._game.stage.context.transform(1, 0, 0, -1, 0, this._mirrorHeight); this._game.stage.context.drawImage(this._canvas, this.x, -this.y); } - //this._game.stage.context.restore(); - }; + }; return Mirror; })(); Camera.Mirror = Mirror; diff --git a/build/phaser.d.ts b/build/phaser.d.ts index b088bca4..ff270381 100644 --- a/build/phaser.d.ts +++ b/build/phaser.d.ts @@ -2476,21 +2476,21 @@ module Phaser { * @param tile The Tile to separate * @returns {boolean} Whether the objects in fact touched and were separated */ - static separateTile(object: GameObject, x: number, y: number, width: number, height: number, mass: number, collideLeft: bool, collideRight: bool, collideUp: bool, collideDown: bool): bool; + static separateTile(object: GameObject, x: number, y: number, width: number, height: number, mass: number, collideLeft: bool, collideRight: bool, collideUp: bool, collideDown: bool, separateX: bool, separateY: bool): bool; /** * 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. */ - static separateTileX(object: GameObject, x: number, y: number, width: number, height: number, mass: number, collideLeft: bool, collideRight: bool): bool; + static separateTileX(object: GameObject, x: number, y: number, width: number, height: number, mass: number, collideLeft: bool, collideRight: bool, separate: bool): bool; /** * Separates the two objects on their y axis * @param object The first GameObject to separate * @param tile The second GameObject to separate * @returns {boolean} Whether the objects in fact touched and were separated along the Y axis. */ - static separateTileY(object: GameObject, x: number, y: number, width: number, height: number, mass: number, collideUp: bool, collideDown: bool): bool; + static separateTileY(object: GameObject, x: number, y: number, width: number, height: number, mass: number, collideUp: bool, collideDown: bool, separate: bool): bool; /** * Separates the two objects on their x axis * @param object1 The first GameObject to separate @@ -3180,21 +3180,21 @@ module Phaser { /** * Removes an object from the group. * - * @param Object The Basic you want to remove. - * @param Splice Whether the object should be cut from the array entirely or not. + * @param object The Basic you want to remove. + * @param splice Whether the object should be cut from the array entirely or not. * * @return The removed object. */ - public remove(Object: Basic, Splice?: bool): Basic; + public remove(object: Basic, splice?: bool): Basic; /** * Replaces an existing Basic with a new one. * - * @param OldObject The object you want to replace. - * @param NewObject The new object you want to use instead. + * @param oldObject The object you want to replace. + * @param newObject The new object you want to use instead. * * @return The new object. */ - public replace(OldObject: Basic, NewObject: Basic): Basic; + public replace(oldObject: Basic, newObject: Basic): Basic; /** * Call this function to sort the group according to a particular value and order. * For example, to sort game objects for Zelda-style overlaps you might call @@ -3202,10 +3202,10 @@ module Phaser { * State.update() override. To sort all existing objects after * a big explosion or bomb attack, you might call myGroup.sort("exists",Group.DESCENDING). * - * @param Index The string name of the member variable you want to sort on. Default value is "y". - * @param Order A Group constant that defines the sort order. Possible values are Group.ASCENDING and Group.DESCENDING. Default value is Group.ASCENDING. + * @param index The string name of the member variable you want to sort on. Default value is "y". + * @param order A Group constant that defines the sort order. Possible values are Group.ASCENDING and Group.DESCENDING. Default value is Group.ASCENDING. */ - public sort(Index?: string, Order?: number): void; + public sort(index?: string, order?: number): void; /** * Go through and set the specified variable to the specified value on all members of the group. * @@ -5344,6 +5344,8 @@ module Phaser { private _tempTileY; private _tempTileW; private _tempTileH; + private _tempTileBlock; + private _tempBlockResults; public name: string; public alpha: number; public exists: bool; @@ -5362,8 +5364,8 @@ module Phaser { public tileMargin: number; public tileSpacing: number; public getTileFromWorldXY(x: number, y: number): number; - public getTileOverlaps(object: GameObject): bool; - public getTileBlock(x: number, y: number, width: number, height: number): any[]; + public getTileOverlaps(object: GameObject); + public getTileBlock(x: number, y: number, width: number, height: number): void; public getTileIndex(x: number, y: number): number; public addColumn(column): void; public updateBounds(): void; @@ -5390,6 +5392,8 @@ module Phaser { public collideRight: bool; public collideUp: bool; public collideDown: bool; + public separateX: bool; + public separateY: bool; /** * A reference to the tilemap this tile object belongs to. */ @@ -5404,7 +5408,7 @@ module Phaser { * Clean up memory. */ public destroy(): void; - public setCollision(collision: number, resetCollisions: bool): void; + public setCollision(collision: number, resetCollisions: bool, separateX: bool, separateY: bool): void; public resetCollision(): void; /** * Returns a string representation of this object. @@ -5423,12 +5427,15 @@ module Phaser { module Phaser { class Tilemap extends GameObject { constructor(game: Game, key: string, mapData: string, format: number, resizeWorld?: bool, tileWidth?: number, tileHeight?: number); + private _tempCollisionData; static FORMAT_CSV: number; static FORMAT_TILED_JSON: number; public tiles: Tile[]; public layers: TilemapLayer[]; public currentLayer: TilemapLayer; public collisionLayer: TilemapLayer; + public collisionCallback; + public collisionCallbackContext; public mapFormat: number; public update(): void; public render(camera: Camera, cameraOffsetX: number, cameraOffsetY: number): void; @@ -5437,13 +5444,15 @@ module Phaser { private generateTiles(qty); public widthInPixels : number; public heightInPixels : number; - public setCollisionRange(start: number, end: number, collision?: number, resetCollisions?: bool): void; - public setCollisionByIndex(values: number[], collision?: number, resetCollisions?: bool): void; + public setCollisionCallback(context, callback): void; + public setCollisionRange(start: number, end: number, collision?: number, resetCollisions?: bool, separateX?: bool, separateY?: bool): void; + public setCollisionByIndex(values: number[], collision?: number, resetCollisions?: bool, separateX?: bool, separateY?: bool): void; + public getTileByIndex(value: number): Tile; public getTile(x: number, y: number, layer?: number): Tile; public getTileFromWorldXY(x: number, y: number, layer?: number): Tile; public getTileFromInputXY(layer?: number): Tile; - public getTileOverlaps(object: GameObject): bool; - public collide(objectOrGroup?, callback?): bool; + public getTileOverlaps(object: GameObject); + public collide(objectOrGroup?, callback?, context?): void; public collideGameObject(object: GameObject): bool; } } diff --git a/build/phaser.js b/build/phaser.js index 5ac07e62..4f038bfb 100644 --- a/build/phaser.js +++ b/build/phaser.js @@ -4827,9 +4827,9 @@ var Phaser; * @param tile The Tile to separate * @returns {boolean} Whether the objects in fact touched and were separated */ - function separateTile(object, x, y, width, height, mass, collideLeft, collideRight, collideUp, collideDown) { - var separatedX = Collision.separateTileX(object, x, y, width, height, mass, collideLeft, collideRight); - var separatedY = Collision.separateTileY(object, x, y, width, height, mass, collideUp, collideDown); + function separateTile(object, x, y, width, height, mass, collideLeft, collideRight, collideUp, collideDown, separateX, separateY) { + var separatedX = Collision.separateTileX(object, x, y, width, height, mass, collideLeft, collideRight, separateX); + var separatedY = Collision.separateTileY(object, x, y, width, height, mass, collideUp, collideDown, separateY); return separatedX || separatedY; }; Collision.separateTileX = /** @@ -4838,7 +4838,7 @@ var Phaser; * @param tile The Tile to separate * @returns {boolean} Whether the objects in fact touched and were separated along the X axis. */ - function separateTileX(object, x, y, width, height, mass, collideLeft, collideRight) { + function separateTileX(object, x, y, width, height, mass, collideLeft, collideRight, separate) { // Can't separate two immovable objects (tiles are always immovable) if(object.immovable) { return false; @@ -4872,8 +4872,10 @@ var Phaser; } // Then adjust their positions and velocities accordingly (if there was any overlap) if(overlap != 0) { - object.x = object.x - overlap; - object.velocity.x = -(object.velocity.x * object.elasticity); + if(separate == true) { + object.x = object.x - overlap; + object.velocity.x = -(object.velocity.x * object.elasticity); + } Collision.TILE_OVERLAP = true; return true; } else { @@ -4886,7 +4888,7 @@ var Phaser; * @param tile The second GameObject to separate * @returns {boolean} Whether the objects in fact touched and were separated along the Y axis. */ - function separateTileY(object, x, y, width, height, mass, collideUp, collideDown) { + function separateTileY(object, x, y, width, height, mass, collideUp, collideDown, separate) { // Can't separate two immovable objects (tiles are always immovable) if(object.immovable) { return false; @@ -4921,8 +4923,10 @@ var Phaser; // TODO - with super low velocities you get lots of stuttering, set some kind of base minimum here // Then adjust their positions and velocities accordingly (if there was any overlap) if(overlap != 0) { - object.y = object.y - overlap; - object.velocity.y = -(object.velocity.y * object.elasticity); + if(separate == true) { + object.y = object.y - overlap; + object.velocity.y = -(object.velocity.y * object.elasticity); + } Collision.TILE_OVERLAP = true; return true; } else { @@ -6341,40 +6345,40 @@ var Phaser; Group.prototype.remove = /** * Removes an object from the group. * - * @param Object The Basic you want to remove. - * @param Splice Whether the object should be cut from the array entirely or not. + * @param object The Basic you want to remove. + * @param splice Whether the object should be cut from the array entirely or not. * * @return The removed object. */ - function (Object, Splice) { - if (typeof Splice === "undefined") { Splice = false; } - var index = this.members.indexOf(Object); + function (object, splice) { + if (typeof splice === "undefined") { splice = false; } + var index = this.members.indexOf(object); if((index < 0) || (index >= this.members.length)) { return null; } - if(Splice) { + if(splice) { this.members.splice(index, 1); this.length--; } else { this.members[index] = null; } - return Object; + return object; }; Group.prototype.replace = /** * Replaces an existing Basic with a new one. * - * @param OldObject The object you want to replace. - * @param NewObject The new object you want to use instead. + * @param oldObject The object you want to replace. + * @param newObject The new object you want to use instead. * * @return The new object. */ - function (OldObject, NewObject) { - var index = this.members.indexOf(OldObject); + function (oldObject, newObject) { + var index = this.members.indexOf(oldObject); if((index < 0) || (index >= this.members.length)) { return null; } - this.members[index] = NewObject; - return NewObject; + this.members[index] = newObject; + return newObject; }; Group.prototype.sort = /** * Call this function to sort the group according to a particular value and order. @@ -6383,14 +6387,14 @@ var Phaser; * State.update() override. To sort all existing objects after * a big explosion or bomb attack, you might call myGroup.sort("exists",Group.DESCENDING). * - * @param Index The string name of the member variable you want to sort on. Default value is "y". - * @param Order A Group constant that defines the sort order. Possible values are Group.ASCENDING and Group.DESCENDING. Default value is Group.ASCENDING. + * @param index The string name of the member variable you want to sort on. Default value is "y". + * @param order A Group constant that defines the sort order. Possible values are Group.ASCENDING and Group.DESCENDING. Default value is Group.ASCENDING. */ - function (Index, Order) { - if (typeof Index === "undefined") { Index = "y"; } - if (typeof Order === "undefined") { Order = Group.ASCENDING; } - this._sortIndex = Index; - this._sortOrder = Order; + function (index, order) { + if (typeof index === "undefined") { index = "y"; } + if (typeof order === "undefined") { order = Group.ASCENDING; } + this._sortIndex = index; + this._sortOrder = order; this.members.sort(this.sortHandler); }; Group.prototype.setAll = /** @@ -11151,6 +11155,7 @@ var Phaser; this.boundsInTiles = new Phaser.Rectangle(); //this.scrollFactor = new MicroPoint(1, 1); this.mapData = []; + this._tempTileBlock = []; this._texture = this._game.cache.getImage(key); } TilemapLayer.prototype.getTileFromWorldXY = function (x, y) { @@ -11168,15 +11173,19 @@ var Phaser; this._tempTileY = this._game.math.snapToFloor(object.bounds.y, this.tileHeight) / this.tileHeight; this._tempTileW = (this._game.math.snapToCeil(object.bounds.width, this.tileWidth) + this.tileWidth) / this.tileWidth; this._tempTileH = (this._game.math.snapToCeil(object.bounds.height, this.tileHeight) + this.tileHeight) / this.tileHeight; - // Loop through the tiles we've got and check overlaps accordingly - var tiles = this.getTileBlock(this._tempTileX, this._tempTileY, this._tempTileW, this._tempTileH); + // Loop through the tiles we've got and check overlaps accordingly (the results are stored in this._tempTileBlock) + this.getTileBlock(this._tempTileX, this._tempTileY, this._tempTileW, this._tempTileH); Phaser.Collision.TILE_OVERLAP = false; - for(var r = 0; r < tiles.length; r++) { - if(tiles[r].tile.allowCollisions != Phaser.Collision.NONE) { - Phaser.Collision.separateTile(object, tiles[r].x * this.tileWidth, tiles[r].y * this.tileHeight, this.tileWidth, this.tileHeight, tiles[r].tile.mass, tiles[r].tile.collideLeft, tiles[r].tile.collideRight, tiles[r].tile.collideUp, tiles[r].tile.collideDown); + for(var r = 0; r < this._tempTileBlock.length; r++) { + if(Phaser.Collision.separateTile(object, this._tempTileBlock[r].x * this.tileWidth, this._tempTileBlock[r].y * this.tileHeight, this.tileWidth, this.tileHeight, this._tempTileBlock[r].tile.mass, this._tempTileBlock[r].tile.collideLeft, this._tempTileBlock[r].tile.collideRight, this._tempTileBlock[r].tile.collideUp, this._tempTileBlock[r].tile.collideDown, this._tempTileBlock[r].tile.separateX, this._tempTileBlock[r].tile.separateY) == true) { + this._tempBlockResults.push({ + x: this._tempTileBlock[r].x, + y: this._tempTileBlock[r].y, + tile: this._tempTileBlock[r].tile + }); } } - return Phaser.Collision.TILE_OVERLAP; + return this._tempBlockResults; }; TilemapLayer.prototype.getTileBlock = function (x, y, width, height) { if(x < 0) { @@ -11191,11 +11200,13 @@ var Phaser; if(height > this.heightInTiles) { height = this.heightInTiles; } - var output = []; + this._tempTileBlock = []; + this._tempBlockResults = []; for(var ty = y; ty < y + height; ty++) { for(var tx = x; tx < x + width; tx++) { - if(this.mapData[ty] && this.mapData[ty][tx]) { - output.push({ + // We only want to consider the tile for checking if you can actually collide with it + if(this.mapData[ty] && this.mapData[ty][tx] && this._parent.tiles[this.mapData[ty][tx]].allowCollisions != Phaser.Collision.NONE) { + this._tempTileBlock.push({ x: tx, y: ty, tile: this._parent.tiles[this.mapData[ty][tx]] @@ -11203,7 +11214,6 @@ var Phaser; } } } - return output; }; TilemapLayer.prototype.getTileIndex = function (x, y) { if(y >= 0 && y < this.mapData.length) { @@ -11350,6 +11360,8 @@ var Phaser; this.collideRight = false; this.collideUp = false; this.collideDown = false; + this.separateX = true; + this.separateY = true; this._game = game; this.tilemap = tilemap; this.index = index; @@ -11363,10 +11375,12 @@ var Phaser; function () { this.tilemap = null; }; - Tile.prototype.setCollision = function (collision, resetCollisions) { + Tile.prototype.setCollision = function (collision, resetCollisions, separateX, separateY) { if(resetCollisions) { this.resetCollision(); } + this.separateX = separateX; + this.separateY = separateY; this.allowCollisions = collision; if(collision & Phaser.Collision.ANY) { this.collideLeft = true; @@ -11426,6 +11440,7 @@ var Phaser; if (typeof tileWidth === "undefined") { tileWidth = 0; } if (typeof tileHeight === "undefined") { tileHeight = 0; } _super.call(this, game); + this.collisionCallback = null; this.isGroup = false; this.tiles = []; this.layers = []; @@ -11522,23 +11537,37 @@ var Phaser; enumerable: true, configurable: true }); - Tilemap.prototype.setCollisionRange = // Tile Collision - function (start, end, collision, resetCollisions) { + Tilemap.prototype.setCollisionCallback = // Tile Collision + function (context, callback) { + this.collisionCallbackContext = context; + this.collisionCallback = callback; + }; + Tilemap.prototype.setCollisionRange = function (start, end, collision, resetCollisions, separateX, separateY) { if (typeof collision === "undefined") { collision = Phaser.Collision.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); + this.tiles[i].setCollision(collision, resetCollisions, separateX, separateY); } }; - Tilemap.prototype.setCollisionByIndex = function (values, collision, resetCollisions) { + Tilemap.prototype.setCollisionByIndex = function (values, collision, resetCollisions, separateX, separateY) { if (typeof collision === "undefined") { collision = Phaser.Collision.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); + this.tiles[values[i]].setCollision(collision, resetCollisions, separateX, separateY); } }; - Tilemap.prototype.getTile = // Tile Management - function (x, y, layer) { + Tilemap.prototype.getTileByIndex = // Tile Management + function (value) { + if(this.tiles[value]) { + return this.tiles[value]; + } + return null; + }; + Tilemap.prototype.getTile = function (x, y, layer) { if (typeof layer === "undefined") { layer = 0; } return this.tiles[this.layers[layer].getTileIndex(x, y)]; }; @@ -11554,26 +11583,31 @@ var Phaser; return this.currentLayer.getTileOverlaps(object); }; Tilemap.prototype.collide = // COLLIDE - function (objectOrGroup, callback) { + 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) { - return this.collideGameObject(objectOrGroup); + this.collideGameObject(objectOrGroup); } else { objectOrGroup.forEachAlive(this, this.collideGameObject, true); } - return true; }; Tilemap.prototype.collideGameObject = function (object) { - if(object == this) { - return false; - } - if(object.immovable == false && object.exists == true && object.allowCollisions != Phaser.Collision.NONE) { - return this.collisionLayer.getTileOverlaps(object); + if(object !== this && object.immovable == false && object.exists == true && object.allowCollisions != Phaser.Collision.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; }