diff --git a/README.md b/README.md index 06f53253..3d420394 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ Version 1.1.3 - in build * New: StageScaleMode.forceOrientation allows you to lock your game to one orientation and display a Sprite (i.e. a "please rotate" screen) when incorrect. * New: World.visible boolean added, toggles rendering of the world on/off entirely. * New: Polygon class & drawPolygon method added to Graphics (thanks rjimenezda) +* New: Added Group.iterate, a powerful way to count or return child that match a certain criteria. Refactored Group to use iterate, lots of repeated code cut. * Fixed: Mouse.stop now uses the true useCapture, which means the event listeners stop listening correctly (thanks beeglebug) * Fixed: Input Keyboard example fix (thanks Atrodilla) * Updated: ArcadePhysics.updateMotion applies the dt to the velocity calculations as well as position now (thanks jcs) diff --git a/examples/wip/sort.js b/examples/wip/sort.js new file mode 100644 index 00000000..9b39682c --- /dev/null +++ b/examples/wip/sort.js @@ -0,0 +1,82 @@ +var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', { preload: preload, create: create, update: update, render: render }); + +function preload() { + + game.load.image('phaser', 'assets/sprites/phaser-dude.png'); + game.load.spritesheet('veggies', 'assets/sprites/fruitnveg32wh37.png', 32, 32); + +} + +var sprite; +var group; +var oldY = 0; + +function create() { + + game.stage.backgroundColor = '#2d2d2d'; + + // sprite = game.add.sprite(32, 200, 'phaser'); + // sprite.name = 'phaser-dude'; + + group = game.add.group(); + + sprite = group.create(300, 200, 'phaser'); + sprite.name = 'phaser-dude'; + + for (var i = 0; i < 10; i++) + { + var c = group.create(100 + Math.random() * 700, game.world.randomY, 'veggies', game.rnd.integerInRange(0, 36)); + c.name = 'veg' + i; + } + + game.input.onUp.add(sortGroup, this); + game.input.keyboard.addKeyCapture([ Phaser.Keyboard.LEFT, Phaser.Keyboard.RIGHT, Phaser.Keyboard.UP, Phaser.Keyboard.DOWN ]); + +} + +function sortGroup () { + + console.log('%c ', 'background: #efefef'); + group.sort(); + group.dump(false); + +} + +function update() { + + sprite.body.velocity.x = 0; + sprite.body.velocity.y = 0; + + if (game.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + sprite.body.velocity.x = -200; + } + else if (game.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + sprite.body.velocity.x = 200; + } + + if (game.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + sprite.body.velocity.y = -200; + } + else if (game.input.keyboard.isDown(Phaser.Keyboard.DOWN)) + { + sprite.body.velocity.y = 200; + } + + if (sprite.y !== oldY) + { + // console.log('sorted'); + // group.sort(); + // oldY = sprite.y; + } + +} + +function render() { + + // game.debug.renderText(group.cursor.name, 32, 32); + // game.debug.renderInputInfo(32, 32); + +} \ No newline at end of file diff --git a/examples/wip/swap.js b/examples/wip/swap.js new file mode 100644 index 00000000..ab0de747 --- /dev/null +++ b/examples/wip/swap.js @@ -0,0 +1,89 @@ +var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', { preload: preload, create: create, update: update, render: render }); + +function preload() { + + game.load.image('phaser', 'assets/sprites/phaser-dude.png'); + game.load.spritesheet('veggies', 'assets/sprites/fruitnveg32wh37.png', 32, 32); + +} + +var group; +var start = false; +var swapCount = 0; +var time = 0; +var test = 0; + +function create() { + + game.stage.backgroundColor = '#2d2d2d'; + + group = game.add.group(); + + for (var i = 0; i < 10; i++) + { + var c = group.create(100 + Math.random() * 700, game.world.randomY, 'veggies', game.rnd.integerInRange(0, 36)); + c.name = 'veg' + i; + } + + test = group.length; + + game.input.onUp.add(toggleSwap, this); + +} + +function toggleSwap () { + + if (start) + { + start = false; + } + else + { + start = true; + } + +} + +function update() { + + if (start && game.time.now > time) + { + var a = group.getRandom(); + var b = group.getRandom(); + + if (a.name !== b.name) + { + console.log('************************ NEW ROUND *********************'); + group.dump(true); + console.log('Group Size: ' + group.length); + group.swap(a, b); + swapCount++; + + if (group.length !== test) + { + start = false; + console.log('************************ SHIT *********************'); + group.dump(true); + console.log('************************ SHIT *********************'); + } + + if (group.validate() == false) + { + start = false; + console.log('************************ VALIDATE FAIL *********************'); + group.dump(true); + console.log('************************ VALIDATE FAIL *********************'); + } + + } + + time = game.time.now + 100; + } + +} + +function render() { + + game.debug.renderText('Swap: ' + swapCount, 32, 32); + +} \ No newline at end of file diff --git a/resources/Project Templates/Basic/MainMenu.js b/resources/Project Templates/Basic/MainMenu.js index 968bca66..fe2a1ae1 100644 --- a/resources/Project Templates/Basic/MainMenu.js +++ b/resources/Project Templates/Basic/MainMenu.js @@ -10,7 +10,7 @@ BasicGame.MainMenu.prototype = { create: function () { - // We've already preloaded our assets, so let's kick right into the Main Menu itself + // We've already preloaded our assets, so let's kick right into the Main Menu itself. // Here all we're doing is playing some music and adding a picture and button // Naturally I expect you to do something significantly better :) diff --git a/resources/Project Templates/Basic/Preloader.js b/resources/Project Templates/Basic/Preloader.js index 1b7e2c81..1eefba51 100644 --- a/resources/Project Templates/Basic/Preloader.js +++ b/resources/Project Templates/Basic/Preloader.js @@ -17,12 +17,13 @@ BasicGame.Preloader.prototype = { this.background = this.add.sprite(0, 0, 'preloaderBackground'); this.preloadBar = this.add.sprite(300, 400, 'preloaderBar'); - // This sets the preloadBar sprite as a loader sprite, basically - // what that does is automatically crop the sprite from 0 to full-width + // This sets the preloadBar sprite as a loader sprite. + // What that does is automatically crop the sprite from 0 to full-width // as the files below are loaded in. this.load.setPreloadSprite(this.preloadBar); - // Here we load most of the assets our game needs + // Here we load the rest of the assets our game needs. + // As this is just a Project Template I've not provided these assets, swap them for your own. this.load.image('titlepage', 'images/title.jpg'); this.load.atlas('playButton', 'images/play_button.png', 'images/play_button.json'); this.load.audio('titleMusic', ['audio/main_menu.mp3']); @@ -33,7 +34,7 @@ BasicGame.Preloader.prototype = { create: function () { - // Once the load has finished we disable the crop because we're going to sit in the update loop for a short while + // Once the load has finished we disable the crop because we're going to sit in the update loop for a short while as the music decodes this.preloadBar.cropEnabled = false; }, @@ -51,7 +52,7 @@ BasicGame.Preloader.prototype = { if (this.cache.isSoundDecoded('titleMusic') && this.ready == false) { - this.ready = false; + this.ready = true; this.game.state.start('MainMenu'); } diff --git a/resources/Project Templates/Basic/index.html b/resources/Project Templates/Basic/index.html index 0c85cf8e..76d93862 100644 --- a/resources/Project Templates/Basic/index.html +++ b/resources/Project Templates/Basic/index.html @@ -3,6 +3,7 @@
myGroup.sort("y",Group.ASCENDING) at the bottom of your
+ * State.update() override. To sort all existing objects after
+ * a big explosion or bomb attack, you might call myGroup.sort("exists",Group.DESCENDING).
+ *
+ * @param {string} index The string name of the member variable you want to sort on. Default value is "z".
+ * @param {number} order A Group constant that defines the sort order. Possible values are Group.ASCENDING and Group.DESCENDING. Default value is Group.ASCENDING.
+ */
+ sort: function (index, order) {
+
+ if (typeof index === 'undefined') { index = 'y'; }
+ if (typeof order === 'undefined') { order = Phaser.Group.SORT_ASCENDING; }
+
+ this._sortIndex = index;
+ this._sortOrder = order;
+ this._sortCache = this._container.children.slice();
+
+ console.log('-vvv--------------------------------------------------------------------------------');
+
+ for (var i = 0; i < this._sortCache.length; i++)
+ {
+ console.log(i + ' = ' + this._sortCache[i].name + ' at y: ' + this._sortCache[i].y);
+ }
+
+ console.log('---------------------------------------------------------------------------------');
+
+ this._sortCache.sort(this.sortHandler.bind(this));
+
+ for (var i = 0; i < this._sortCache.length; i++)
+ {
+ console.log(i + ' = ' + this._sortCache[i].name + ' at y: ' + this._sortCache[i].y);
+ }
+
+ for (var i = 0; i < this._sortCache.length; i++)
+ {
+ if (this._container.children[i] !== this._sortCache[i])
+ {
+ console.log('swapped:', this._container.children[i].name,'with',this._sortCache[i].name);
+ this.swap(this._container.children[i], this._sortCache[i]);
+ }
+ }
+
+ // Now put it back again
+ this._container.children = this._sortCache.slice();
+
+ this._container.updateTransform();
+
+ console.log('-^^^--------------------------------------------------------------------------------');
+
+ },
+
+ /**
+ * Helper function for the sort process.
+ *
+ * @param {Basic} Obj1 The first object being sorted.
+ * @param {Basic} Obj2 The second object being sorted.
+ *
+ * @return {number} An integer value: -1 (Obj1 before Obj2), 0 (same), or 1 (Obj1 after Obj2).
+ */
+ sortHandler: function (obj1, obj2) {
+
+ if (!obj1 || !obj2)
+ {
+ // console.log('null objects in sort', obj1, obj2);
+ return 0;
+ }
+
+ // number only test
+ // return obj1[this._sortIndex] - obj2[this._sortIndex];
+
+ if (obj1[this._sortIndex] < obj2[this._sortIndex])
+ {
+ // console.log('1 < 2');
+ return this._sortOrder;
+ }
+ else if (obj1[this._sortIndex] > obj2[this._sortIndex])
+ {
+ // console.log('1 > 2');
+ return -this._sortOrder;
+ }
+
+ return 0;
+
+ },
+
+ /**
+ * Iterates over the children of the Group. When a child has a property matching key that equals the given value, it is considered as a match.
+ * Matched children can be sent to the optional callback, or simply returned or counted.
+ * You can add as many callback parameters as you like, which will all be passed to the callback along with the child, after the callbackContext parameter.
+ *
+ * @method Phaser.Group#iterate
+ * @param {string} key - The child property to check, i.e. 'exists', 'alive', 'health'
+ * @param {any} value - If child.key === this value it will be considered a match. Note that a strict comparison is used.
+ * @param {number} returnType - How to return the data from this method. Either Phaser.Group.RETURN_NONE, Phaser.Group.RETURN_TOTAL or Phaser.Group.RETURN_CHILD.
+ * @param {function} [callback=null] - Optional function that will be called on each matching child. Each child of the Group will be passed to it as its first parameter.
+ * @param {Object} [callbackContext] - The context in which the function should be called (usually 'this').
+ */
+ iterate: function (key, value, returnType, callback, callbackContext, args) {
+
+ if (returnType == Phaser.Group.RETURN_TOTAL && this._container.children.length == 0)
+ {
+ return -1;
+ }
+
+ if (typeof callback === 'undefined')
+ {
+ callback = false;
+ }
+
+ var total = 0;
+
if (this._container.children.length > 0 && this._container.first._iNext)
{
var currentNode = this._container.first._iNext;
do
{
- if (currentNode.alive == false)
+ if (currentNode[key] === value)
{
- args[0] = currentNode;
- callback.apply(callbackContext, args);
+ total++;
+
+ if (callback)
+ {
+ args[0] = currentNode;
+ callback.apply(callbackContext, args);
+ }
+
+ if (returnType == Phaser.Group.RETURN_CHILD)
+ {
+ return currentNode;
+ }
}
currentNode = currentNode._iNext;
}
while (currentNode != this._container.last._iNext);
-
}
+
+ if (returnType == Phaser.Group.RETURN_TOTAL)
+ {
+ return total;
+ }
+ else if (returnType == Phaser.Group.RETURN_CHILD)
+ {
+ return null;
+ }
+
},
/**
@@ -941,23 +1176,7 @@ Phaser.Group.prototype = {
state = true;
}
- if (this._container.children.length > 0 && this._container.first._iNext)
- {
- var currentNode = this._container.first._iNext;
-
- do
- {
- if (currentNode.exists === state)
- {
- return currentNode;
- }
-
- currentNode = currentNode._iNext;
- }
- while (currentNode != this._container.last._iNext);
- }
-
- return null;
+ return this.iterate('exists', state, Phaser.Group.RETURN_CHILD);
},
@@ -970,23 +1189,7 @@ Phaser.Group.prototype = {
*/
getFirstAlive: function () {
- if (this._container.children.length > 0 && this._container.first._iNext)
- {
- var currentNode = this._container.first._iNext;
-
- do
- {
- if (currentNode.alive)
- {
- return currentNode;
- }
-
- currentNode = currentNode._iNext;
- }
- while (currentNode != this._container.last._iNext);
- }
-
- return null;
+ return this.iterate('alive', true, Phaser.Group.RETURN_CHILD);
},
@@ -999,23 +1202,7 @@ Phaser.Group.prototype = {
*/
getFirstDead: function () {
- if (this._container.children.length > 0 && this._container.first._iNext)
- {
- var currentNode = this._container.first._iNext;
-
- do
- {
- if (!currentNode.alive)
- {
- return currentNode;
- }
-
- currentNode = currentNode._iNext;
- }
- while (currentNode != this._container.last._iNext);
- }
-
- return null;
+ return this.iterate('alive', false, Phaser.Group.RETURN_CHILD);
},
@@ -1027,29 +1214,7 @@ Phaser.Group.prototype = {
*/
countLiving: function () {
- var total = 0;
-
- if (this._container.children.length > 0 && this._container.first._iNext)
- {
- var currentNode = this._container.first._iNext;
-
- do
- {
- if (currentNode.alive)
- {
- total++;
- }
-
- currentNode = currentNode._iNext;
- }
- while (currentNode != this._container.last._iNext);
- }
- else
- {
- total = -1;
- }
-
- return total;
+ return this.iterate('alive', true, Phaser.Group.RETURN_TOTAL);
},
@@ -1061,29 +1226,7 @@ Phaser.Group.prototype = {
*/
countDead: function () {
- var total = 0;
-
- if (this._container.children.length > 0 && this._container.first._iNext)
- {
- var currentNode = this._container.first._iNext;
-
- do
- {
- if (!currentNode.alive)
- {
- total++;
- }
-
- currentNode = currentNode._iNext;
- }
- while (currentNode != this._container.last._iNext);
- }
- else
- {
- total = -1;
- }
-
- return total;
+ return this.iterate('alive', false, Phaser.Group.RETURN_TOTAL);
},
@@ -1228,6 +1371,48 @@ Phaser.Group.prototype = {
},
+ validate: function () {
+
+ var testObject = this.game.stage._stage.last._iNext;
+ var displayObject = this.game.stage._stage;
+ var nextObject = null;
+ var prevObject = null;
+ var count = 0;
+
+ do
+ {
+ if (count > 0)
+ {
+ // check next
+ if (displayObject !== nextObject)
+ {
+ console.log('check next fail');
+ return false;
+ }
+
+ // check previous
+ if (displayObject._iPrev !== prevObject)
+ {
+ console.log('check previous fail');
+ return false;
+ }
+ }
+
+ // Set the next object
+ nextObject = displayObject._iNext;
+ prevObject = displayObject;
+
+ displayObject = displayObject._iNext;
+
+ count++;
+
+ }
+ while(displayObject != testObject)
+
+ return true;
+
+ },
+
/**
* Dumps out a list of Group children and their index positions to the browser console. Useful for group debugging.
*
@@ -1252,11 +1437,13 @@ Phaser.Group.prototype = {
if (full)
{
var testObject = this.game.stage._stage.last._iNext;
+ // var testObject = this.game.stage._stage.last;
var displayObject = this.game.stage._stage;
}
else
{
var testObject = this._container.last._iNext;
+ // var testObject = this._container.last;
var displayObject = this._container;
}
@@ -1334,7 +1521,8 @@ Phaser.Group.prototype = {
Object.defineProperty(Phaser.Group.prototype, "total", {
get: function () {
- return this._container.children.length;
+ return this.iterate('exists', true, Phaser.Group.RETURN_TOTAL);
+ // return this._container.children.length;
}
});
@@ -1347,7 +1535,8 @@ Object.defineProperty(Phaser.Group.prototype, "total", {
Object.defineProperty(Phaser.Group.prototype, "length", {
get: function () {
- return this._container.children.length;
+ return this.iterate('exists', true, Phaser.Group.RETURN_TOTAL);
+ // return this._container.children.length;
}
});