diff --git a/README.md b/README.md index fa46b223..c4fb1d4c 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ Significant API changes: * Phaser.StageScaleMode has been renamed to ScaleManager and moved from the system folder to the core folder. It's still available under game.scale. * If your game references the old Phaser.StageScaleMode consts like SHOW_ALL you need to update them to Phaser.ScaleManager, i.e. Phaser.ScaleManager.SHOW_ALL. * Time.physicsElapsed is no longer bound or clamped, be wary of this if you use the value anywhere in your code. +* In Group.destroy the default for 'destroyChildren' was false. It's now `true` as this is a far more likely requirement when destroying a Group. New features: @@ -120,6 +121,7 @@ New features: * StateManager.start can now have as many parameters as you like. The order is: start(key, clearWorld, clearCache, ...) - they are passed to State.init() (NOT create!) * Loader.script now has callback (and callbackContext) parameters, so you can specify a function to run once the JS has been injected into the body. * Phaser.Timer.stop has a new parameter: clearEvents (default true), if true all the events in Timer will be cleared, otherwise they will remain (fixes #383) +* All GameObjects now have a 'destroyChildren' boolean as a parameter to their destroy method. It's default is true and the value propogates down its children. Updates: @@ -177,7 +179,10 @@ Bug Fixes: * Phaser.Timer will no longer resume if it was previously paused and the game loses focus and then resumes (fixes #383) * Tweens now resume correctly if the game pauses (focus loss) while they are paused. * Tweens don't double pause if they were already paused and the game pauses. - +* Buttons are now cleanly destroyed if part of a Group without leaving their InputHandler running. +* You can now safely destroy a Group and the 'destroyChildren' boolean will propogate fully down the display list. +* Calling destroy on an already destroyed object would throw a run-time error. Now checked for and aborted. +* Calling destroy while in an Input Event callback now works for either the parent Group or the calling object itself. TO DO: diff --git a/examples/wip/group destroy.js b/examples/wip/group destroy.js new file mode 100644 index 00000000..b75d1cfa --- /dev/null +++ b/examples/wip/group destroy.js @@ -0,0 +1,72 @@ + +var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', { preload: preload, create: create, update: update, render: render }); + +function preload() { + + game.load.image('atari1', 'assets/sprites/atari130xe.png'); + game.load.image('sonic', 'assets/sprites/sonic_havok_sanity.png'); + game.load.image('starfield', 'assets/misc/starfield.jpg'); + game.load.bitmapFont('carrier', 'assets/fonts/carrier_command.png', 'assets/fonts/carrier_command.xml', null, 0, 24); + game.load.spritesheet('button', 'assets/buttons/button_sprite_sheet.png', 193, 71); + +} + +var group; +var DaddyGroup; + +function create() { + + DaddyGroup = game.add.group(); + + group = game.add.group(); + + // Testing Group.destroy with different object types: + + var sprite = game.make.sprite(300, 100, 'atari1'); + + var graphics = game.make.graphics(0, 0); + graphics.beginFill(0xFF3300); + graphics.moveTo(0,50); + graphics.lineTo(250, 50); + graphics.lineTo(100, 100); + graphics.lineTo(250, 220); + graphics.lineTo(50, 220); + graphics.lineTo(0, 50); + graphics.endFill(); + + var tilesprite = game.make.tileSprite(600, 100, 200, 200, 'starfield'); + tilesprite.autoScroll(0, 100); + + var bitmaptext = game.make.bitmapText(100, 300, 'carrier', 'bitmap text', 32); + + var text = game.make.text(100, 350, "normal text"); + text.font = 'Arial Black'; + text.fontSize = 60; + text.fill = '#ff0044'; + + var button = game.make.button(100, 450, 'button', actionOnClick, this, 2, 1, 0); + + group.add(sprite); + group.add(graphics); + group.add(tilesprite); + group.add(bitmaptext); + group.add(text); + group.add(button); + + DaddyGroup.add(group); + + game.input.onDown.add(actionOnClick, this); + +} + +function actionOnClick() { + + DaddyGroup.destroy(); + +} + +function update() { +} + +function render() { +} diff --git a/src/core/Group.js b/src/core/Group.js index 4c2fc0e1..6050176f 100644 --- a/src/core/Group.js +++ b/src/core/Group.js @@ -1137,11 +1137,13 @@ Phaser.Group.prototype.removeBetween = function (startIndex, endIndex) { * Destroys this Group. Removes all children, then removes the container from the display list and nulls references. * * @method Phaser.Group#destroy -* @param {boolean} [destroyChildren=false] - Should every child of this Group have its destroy method called? +* @param {boolean} [destroyChildren=true] - Should every child of this Group have its destroy method called? */ Phaser.Group.prototype.destroy = function (destroyChildren) { - if (typeof destroyChildren === 'undefined') { destroyChildren = false; } + if (this.game === null) { return; } + + if (typeof destroyChildren === 'undefined') { destroyChildren = true; } if (destroyChildren) { @@ -1151,7 +1153,7 @@ Phaser.Group.prototype.destroy = function (destroyChildren) { { if (this.children[0].parent) { - this.children[0].destroy(); + this.children[0].destroy(destroyChildren); } } while (this.children.length > 0); diff --git a/src/gameobjects/BitmapText.js b/src/gameobjects/BitmapText.js index c2b86bde..5aadb45a 100644 --- a/src/gameobjects/BitmapText.js +++ b/src/gameobjects/BitmapText.js @@ -201,8 +201,13 @@ Phaser.BitmapText.prototype.postUpdate = function () { /** * Destroy this BitmapText instance. This will remove any filters and un-parent any children. * @method Phaser.BitmapText.prototype.destroy +* @param {boolean} [destroyChildren=true] - Should every child of this object have its destroy method called? */ -Phaser.BitmapText.prototype.destroy = function() { +Phaser.BitmapText.prototype.destroy = function(destroyChildren) { + + if (this.game === null) { return; } + + if (typeof destroyChildren === 'undefined') { destroyChildren = true; } if (this.parent) { @@ -211,9 +216,26 @@ Phaser.BitmapText.prototype.destroy = function() { var i = this.children.length; - while (i--) + if (destroyChildren) { - this.removeChild(this.children[i]); + while (i--) + { + if (this.children[i].destroy) + { + this.children[i].destroy(destroyChildren); + } + else + { + this.removeChild(this.children[i]); + } + } + } + else + { + while (i--) + { + this.removeChild(this.children[i]); + } } this.exists = false; diff --git a/src/gameobjects/Graphics.js b/src/gameobjects/Graphics.js index 97873dbd..0a06d5f3 100644 --- a/src/gameobjects/Graphics.js +++ b/src/gameobjects/Graphics.js @@ -138,8 +138,11 @@ Phaser.Graphics.prototype.postUpdate = function () { * Destroy this Graphics instance. * * @method Phaser.Graphics.prototype.destroy +* @param {boolean} [destroyChildren=true] - Should every child of this object have its destroy method called? */ -Phaser.Graphics.prototype.destroy = function() { +Phaser.Graphics.prototype.destroy = function(destroyChildren) { + + if (typeof destroyChildren === 'undefined') { destroyChildren = true; } this.clear(); @@ -148,6 +151,23 @@ Phaser.Graphics.prototype.destroy = function() { this.parent.remove(this); } + var i = this.children.length; + + if (destroyChildren) + { + while (i--) + { + this.children[i].destroy(destroyChildren); + } + } + else + { + while (i--) + { + this.removeChild(this.children[i]); + } + } + this.exists = false; this.visible = false; diff --git a/src/gameobjects/Image.js b/src/gameobjects/Image.js index 60ffe2f9..8f6b7a12 100644 --- a/src/gameobjects/Image.js +++ b/src/gameobjects/Image.js @@ -366,8 +366,13 @@ Phaser.Image.prototype.kill = function() { * * @method Phaser.Image#destroy * @memberof Phaser.Image +* @param {boolean} [destroyChildren=true] - Should every child of this object have its destroy method called? */ -Phaser.Image.prototype.destroy = function() { +Phaser.Image.prototype.destroy = function(destroyChildren) { + + if (this.game === null) { return; } + + if (typeof destroyChildren === 'undefined') { destroyChildren = true; } if (this.parent) { @@ -386,9 +391,19 @@ Phaser.Image.prototype.destroy = function() { var i = this.children.length; - while (i--) + if (destroyChildren) { - this.removeChild(this.children[i]); + while (i--) + { + this.children[i].destroy(destroyChildren); + } + } + else + { + while (i--) + { + this.removeChild(this.children[i]); + } } this.alive = false; diff --git a/src/gameobjects/Sprite.js b/src/gameobjects/Sprite.js index cd15794a..f14ab3fa 100644 --- a/src/gameobjects/Sprite.js +++ b/src/gameobjects/Sprite.js @@ -489,8 +489,13 @@ Phaser.Sprite.prototype.kill = function() { * * @method Phaser.Sprite#destroy * @memberof Phaser.Sprite +* @param {boolean} [destroyChildren=true] - Should every child of this object have its destroy method called? */ -Phaser.Sprite.prototype.destroy = function() { +Phaser.Sprite.prototype.destroy = function(destroyChildren) { + + if (this.game === null) { return; } + + if (typeof destroyChildren === 'undefined') { destroyChildren = true; } if (this.parent) { @@ -519,9 +524,19 @@ Phaser.Sprite.prototype.destroy = function() { var i = this.children.length; - while (i--) + if (destroyChildren) { - this.removeChild(this.children[i]); + while (i--) + { + this.children[i].destroy(destroyChildren); + } + } + else + { + while (i--) + { + this.removeChild(this.children[i]); + } } this.alive = false; diff --git a/src/gameobjects/Text.js b/src/gameobjects/Text.js index 2bca231f..9f5ce778 100644 --- a/src/gameobjects/Text.js +++ b/src/gameobjects/Text.js @@ -181,8 +181,13 @@ Phaser.Text.prototype.postUpdate = function () { /** * @method Phaser.Text.prototype.destroy +* @param {boolean} [destroyChildren=true] - Should every child of this object have its destroy method called? */ -Phaser.Text.prototype.destroy = function () { +Phaser.Text.prototype.destroy = function (destroyChildren) { + + if (this.game === null) { return; } + + if (typeof destroyChildren === 'undefined') { destroyChildren = true; } if (this.parent) { @@ -203,9 +208,19 @@ Phaser.Text.prototype.destroy = function () { var i = this.children.length; - while (i--) + if (destroyChildren) { - this.removeChild(this.children[i]); + while (i--) + { + this.children[i].destroy(destroyChildren); + } + } + else + { + while (i--) + { + this.removeChild(this.children[i]); + } } this.exists = false; diff --git a/src/gameobjects/TileSprite.js b/src/gameobjects/TileSprite.js index f13ac4ad..96b05dc1 100644 --- a/src/gameobjects/TileSprite.js +++ b/src/gameobjects/TileSprite.js @@ -276,8 +276,13 @@ Phaser.TileSprite.prototype.loadTexture = function (key, frame) { * * @method Phaser.TileSprite#destroy * @memberof Phaser.TileSprite +* @param {boolean} [destroyChildren=true] - Should every child of this object have its destroy method called? */ -Phaser.TileSprite.prototype.destroy = function() { +Phaser.TileSprite.prototype.destroy = function(destroyChildren) { + + if (this.game === null) { return; } + + if (typeof destroyChildren === 'undefined') { destroyChildren = true; } if (this.filters) { @@ -295,9 +300,19 @@ Phaser.TileSprite.prototype.destroy = function() { var i = this.children.length; - while (i--) + if (destroyChildren) { - this.removeChild(this.children[i]); + while (i--) + { + this.children[i].destroy(destroyChildren); + } + } + else + { + while (i--) + { + this.removeChild(this.children[i]); + } } this.exists = false;