diff --git a/examples/js.php b/examples/js.php index 7181313f..279877a8 100644 --- a/examples/js.php +++ b/examples/js.php @@ -68,6 +68,7 @@ + diff --git a/examples/quadtree.php b/examples/quadtree.php new file mode 100644 index 00000000..642a9a3c --- /dev/null +++ b/examples/quadtree.php @@ -0,0 +1,72 @@ + + +
+object and next to null.
-*/
-Phaser.LinkedList = function () {
-
- this.object = null;
- this.next = null;
-
-};
-
-Phaser.LinkedList.prototype = {
-
- destroy: function () {
-
- this.object = null;
-
- if (this.next != null)
- {
- this.next.destroy();
- }
-
- this.next = null;
- }
-
-};
diff --git a/src/math/QuadTree.js b/src/math/QuadTree.js
index 48ca23fa..a4626e71 100644
--- a/src/math/QuadTree.js
+++ b/src/math/QuadTree.js
@@ -1,34 +1,229 @@
-/**
-* Phaser - QuadTree
-*
-* A fairly generic quad tree structure for rapid overlap checks. QuadTree is also configured for single or dual list operation.
-* You can add items either to its A list or its B list. When you do an overlap check, you can compare the A list to itself,
-* or the A list against the B list. Handy for different things!
+/*
+ * Javascript QuadTree
+ * @version 1.0
+ * @author Timo Hausmann
+ *
+ * Optimised to reduce temp. var creation and increase performance by Richard Davey
+ *
+ * Original version at https://github.com/timohausmann/quadtree-js/
+ */
+
+/*
+ Copyright © 2012 Timo Hausmann
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-/**
-* Instantiate a new Quad Tree node.
-
-* @extends Rectangle
-*
-* @param {Number} x The X-coordinate of the point in space.
-* @param {Number} y The Y-coordinate of the point in space.
-* @param {Number} width Desired width of this node.
-* @param {Number} height Desired height of this node.
-* @param {Number} parent The parent branch or node. Pass null to create a root.
+/*
+* QuadTree Constructor
+* @param Integer maxObjects (optional) max objects a node can hold before splitting into 4 subnodes (default: 10)
+* @param Integer maxLevels (optional) total max levels inside root QuadTree (default: 4)
+* @param Integer level (optional) deepth level, required for subnodes
*/
-Phaser.QuadTree = function (manager, x, y, width, height, parent) {
-
- Phaser.Rectangle.call(this);
+Phaser.QuadTree = function (x, y, width, height, maxObjects, maxLevels, level) {
+
+ this.maxObjects = maxObjects || 10;
+ this.maxLevels = maxLevels || 4;
+ this.level = level || 0;
+
+ this.bounds = {
+ x: x,
+ y: y,
+ width: width,
+ height: height,
+ subWidth: Math.floor(width / 2 ),
+ subHeight: Math.floor(height / 2 ),
+ right: x + Math.floor(width / 2 ),
+ bottom: y + Math.floor(height / 2 )
+ };
+ this.objects = [];
+ this.nodes = [];
};
-// constructor
-Phaser.QuadTree.prototype = Object.create(Phaser.Rectangle.prototype);
-Phaser.QuadTree.prototype.constructor = Phaser.Rectangle;
-
Phaser.QuadTree.prototype = {
+ /*
+ * Split the node into 4 subnodes
+ */
+ split: function() {
+
+ // top right node
+ this.nodes[0] = new Phaser.QuadTree(this.bounds.right, this.bounds.y, this.bounds.subWidth, this.bounds.subHeight, this.maxObjects, this.maxLevels, this.level + 1);
+
+ // top left node
+ this.nodes[1] = new Phaser.QuadTree(this.bounds.x, this.bounds.y, this.bounds.subWidth, this.bounds.subHeight, this.maxObjects, this.maxLevels, this.level + 1);
+
+ // bottom left node
+ this.nodes[2] = new Phaser.QuadTree(this.bounds.x, this.bounds.bottom, this.bounds.subWidth, this.bounds.subHeight, this.maxObjects, this.maxLevels, this.level + 1);
+
+ // bottom right node
+ this.nodes[3] = new Phaser.QuadTree(this.bounds.right, this.bounds.bottom, this.bounds.subWidth, this.bounds.subHeight, this.maxObjects, this.maxLevels, this.level + 1);
-};
\ No newline at end of file
+ },
+
+ /*
+ * Determine which node the object belongs to
+ * @param Object pRect bounds of the area to be checked, with x, y, width, height
+ * @return Integer index of the subnode (0-3), or -1 if pRect cannot completely fit within a subnode and is part of the parent node
+ */
+ getIndex: function (rect) {
+
+ var index = -1;
+
+ // Duplicated comparisons, but they only get checked once in the flow so it saves creating temp. vars
+
+ // rect can completely fit within the left quadrants
+ if (rect.x < this.bounds.bottom && rect.right < this.bounds.bottom)
+ {
+ if ((rect.y < this.bounds.right && rect.bottom < this.bounds.right))
+ {
+ // rect can completely fit within the top quadrants
+ index = 1;
+ }
+ else if ((rect.y > this.bounds.right))
+ {
+ // rect can completely fit within the bottom quadrants
+ index = 2;
+ }
+ }
+ else if (rect.x > this.bounds.bottom)
+ {
+ // rect can completely fit within the right quadrants
+ if ((rect.y < this.bounds.right && rect.bottom < this.bounds.right))
+ {
+ index = 0;
+ }
+ else if ((rect.y > this.bounds.right))
+ {
+ index = 3;
+ }
+ }
+
+ return index;
+
+ },
+
+ /*
+ * Insert the object into the node. If the node
+ * exceeds the capacity, it will split and add all
+ * objects to their corresponding subnodes.
+ * @param Object pRect bounds of the object to be added, with x, y, width, height
+ */
+ insert: function (body) {
+
+ var i = 0;
+ var index;
+
+ // if we have subnodes ...
+ // if (typeof this.nodes[0] !== 'undefined')
+ if (this.nodes[0])
+ {
+ index = this.getIndex(body.bounds);
+
+ if (index !== -1)
+ {
+ this.nodes[index].insert(body);
+ return;
+ }
+ }
+
+ this.objects.push(body);
+
+ if (this.objects.length > this.maxObjects && this.level < this.maxLevels )
+ {
+ // Split if we don't already have subnodes
+ // if (typeof this.nodes[0] === 'undefined')
+ if (!this.nodes[0])
+ {
+ this.split();
+ }
+
+ // Add objects to subnodes
+ while (i < this.objects.length)
+ {
+ index = this.getIndex(this.objects[i].bounds);
+
+ if (index !== -1 )
+ {
+ // this is expensive - see what we can do about it
+ this.nodes[index].insert(this.objects.splice(i, 1)[0]);
+ }
+ else
+ {
+ i++;
+ }
+ }
+ }
+ },
+
+ /*
+ * Return all objects that could collide with the given object
+ * @param Object pRect bounds of the object to be checked, with x, y, width, height
+ * @Return Array array with all detected objects
+ */
+ retrieve: function (rect) {
+
+ var index = this.getIndex(rect);
+ var returnObjects = this.objects;
+
+ // if we have subnodes ...
+ // if (typeof this.nodes[0] !== 'undefined')
+ if (this.nodes[0])
+ {
+ // if rect fits into a subnode ..
+ if (index !== -1)
+ {
+ returnObjects = returnObjects.concat(this.nodes[index].retrieve(rect));
+ }
+ else
+ {
+ // if rect does not fit into a subnode, check it against all subnodes
+ for (var i = 0, len = this.nodes.length; i < len; i++)
+ {
+ returnObjects = returnObjects.concat(this.nodes[i].retrieve(rect));
+ }
+ }
+ }
+
+ return returnObjects;
+
+ },
+
+ /*
+ * Clear the quadtree
+ */
+ clear: function () {
+
+ this.objects = [];
+
+ for (var i = 0, len = this.nodes.length; i < len; i++)
+ {
+ // if (typeof this.nodes[i] !== 'undefined')
+ if (this.nodes[i])
+ {
+ this.nodes[i].clear();
+ delete this.nodes[i];
+ }
+ }
+ }
+
+};
diff --git a/src/physics/arcade/ArcadePhysics.js b/src/physics/arcade/ArcadePhysics.js
index 40a1388d..47ddc246 100644
--- a/src/physics/arcade/ArcadePhysics.js
+++ b/src/physics/arcade/ArcadePhysics.js
@@ -8,9 +8,16 @@ Phaser.Physics.Arcade = function (game) {
this.bounds = new Phaser.Rectangle(0, 0, game.world.width, game.world.height);
/**
+ * Used by the QuadTree to set the maximum number of objects
* @type {number}
*/
- this.worldDivisions = 6;
+ this.maxObjects = 10;
+
+ /**
+ * Used by the QuadTree to set the maximum number of levels
+ * @type {number}
+ */
+ this.maxLevels = 4;
this.LEFT = 0x0001;
this.RIGHT = 0x0010;
@@ -25,6 +32,8 @@ Phaser.Physics.Arcade = function (game) {
this.OVERLAP_BIAS = 4;
this.TILE_OVERLAP = false;
+ this.quadTree = null;
+
// avoid gc spikes by caching these values for re-use
this._obj1Bounds = new Phaser.Rectangle;
this._obj2Bounds = new Phaser.Rectangle;
@@ -126,6 +135,20 @@ Phaser.Physics.Arcade.prototype = {
return velocity;
+ },
+
+ preUpdate: function () {
+
+ // Create our tree which all of the Physics bodies will add themselves to
+ this.quadTree = new Phaser.QuadTree(this.game.world.bounds.x, this.game.world.bounds.y, this.game.world.bounds.width, this.game.world.bounds.height, this.maxObjects, this.maxLevels);
+
+ },
+
+ postUpdate: function () {
+
+ // Clear the tree ready for the next update
+ this.quadTree.clear();
+
},
/**
@@ -139,28 +162,6 @@ Phaser.Physics.Arcade.prototype = {
*/
overlap: function (object1, object2, notifyCallback, processCallback) {
- if (object1 == null)
- {
- object1 = this._game.world.group;
- }
-
- if (object2 == object1)
- {
- object2 = null;
- }
-
- Phaser.QuadTree.divisions = this.worldDivisions;
-
- var quadTree = new Phaser.QuadTree(this._game.world.bounds.x, this._game.world.bounds.y, this._game.world.bounds.width, this._game.world.bounds.height);
-
- quadTree.load(object1, object2, notifyCallback, processCallback);
-
- var result = quadTree.execute();
-
- quadTree.destroy();
-
- quadTree = null;
-
return result;
},
diff --git a/src/physics/arcade/Body.js b/src/physics/arcade/Body.js
index 5dcfe577..5c0a8a11 100644
--- a/src/physics/arcade/Body.js
+++ b/src/physics/arcade/Body.js
@@ -58,6 +58,8 @@ Phaser.Physics.Arcade.Body = function (sprite) {
this.allowRotation = false;
this.allowGravity = true;
+ this.collideWorldBounds = false;
+
this.lastX = sprite.x;
this.lastY = sprite.y;
@@ -91,10 +93,50 @@ Phaser.Physics.Arcade.Body.prototype = {
this.bounds.x = this.x;
this.bounds.y = this.y;
+ if (this.allowCollisions & this.ANY)
+ {
+ this.game.physics.quadTree.insert(this);
+ }
+
+ },
+
+ checkWorldBounds: function () {
+
+ if (this.bounds.x < this.game.world.bounds.x)
+ {
+ this.x = this.game.world.bounds.x;
+ this.velocity.x *= -1;
+ this.velocity.x *= this.bounce.x;
+ }
+ else if (this.bounds.right > this.game.world.bounds.right)
+ {
+ this.x = this.game.world.bounds.right - this.width;
+ this.velocity.x *= -1;
+ this.velocity.x *= this.bounce.x;
+ }
+
+ if (this.bounds.y < this.game.world.bounds.y)
+ {
+ this.y = this.game.world.bounds.y;
+ this.velocity.y *= -1;
+ this.velocity.y *= this.bounce.y;
+ }
+ else if (this.bounds.bottom > this.game.world.bounds.bottom)
+ {
+ this.y = this.game.world.bounds.bottom - this.height;
+ this.velocity.y *= -1;
+ this.velocity.y *= this.bounce.y;
+ }
+
},
postUpdate: function () {
+ if (this.collideWorldBounds)
+ {
+ this.checkWorldBounds();
+ }
+
this.sprite.x = this.x - this.offset.x + (this.sprite.anchor.x * this.width);
this.sprite.y = this.y - this.offset.y + (this.sprite.anchor.y * this.height);
@@ -122,6 +164,7 @@ Phaser.Physics.Arcade.Body.prototype = {
},
+ /*
hullWidth: function () {
if (this.deltaX() > 0)
@@ -173,6 +216,7 @@ Phaser.Physics.Arcade.Body.prototype = {
}
},
+ */
deltaAbsX: function () {
return (this.deltaX() > 0 ? this.deltaX() : -this.deltaX());