From 7e12075be142e91193a0bd00d7cc0f33869d7e93 Mon Sep 17 00:00:00 2001 From: photonstorm Date: Thu, 27 Feb 2014 20:05:16 +0000 Subject: [PATCH] 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. 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. 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. --- README.md | 7 +++- examples/wip/group destroy.js | 72 +++++++++++++++++++++++++++++++++++ src/core/Group.js | 8 ++-- src/gameobjects/BitmapText.js | 28 ++++++++++++-- src/gameobjects/Graphics.js | 22 ++++++++++- src/gameobjects/Image.js | 21 ++++++++-- src/gameobjects/Sprite.js | 21 ++++++++-- src/gameobjects/Text.js | 21 ++++++++-- src/gameobjects/TileSprite.js | 21 ++++++++-- 9 files changed, 201 insertions(+), 20 deletions(-) create mode 100644 examples/wip/group destroy.js 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;