/** * @author Richard Davey * @author Felipe Alfonso <@bitnenfer> * @copyright 2018 Photon Storm Ltd. * @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} */ var ArrayUtils = require('../../utils/array'); var Class = require('../../utils/Class'); var Components = require('../components'); var GameObject = require('../GameObject'); var Render = require('./ContainerRender'); var Vector2 = require('../../math/Vector2'); /** * @classdesc * A Container Game Object. * * @class Container * @extends Phaser.GameObjects.GameObject * @memberOf Phaser.GameObjects * @constructor * @since 3.4.0 * * @extends Phaser.GameObjects.Components.Alpha * @extends Phaser.GameObjects.Components.BlendMode * @extends Phaser.GameObjects.Components.Depth * @extends Phaser.GameObjects.Components.Transform * @extends Phaser.GameObjects.Components.Visible * * @param {Phaser.Scene} scene - The Scene to which this Game Object belongs. A Game Object can only belong to one Scene at a time. * @param {number} [x=0] - The horizontal position of this Game Object in the world. * @param {number} [y=0] - The vertical position of this Game Object in the world. * @param {Phaser.GameObjects.GameObject[]} [children] - An optional array of Game Objects to add to this Container. */ var Container = new Class({ Extends: GameObject, Mixins: [ Components.Alpha, Components.BlendMode, Components.Depth, Components.Transform, Components.Visible, Render ], initialize: function Container (scene, x, y, children) { GameObject.call(this, scene, 'Container'); /** * An array holding the children of this Container. * * @name Phaser.GameObjects.Container#list * @type {Phaser.GameObjects.GameObject[]} * @since 3.4.0 */ this.list = []; /** * Does this Container exclusively manage its children? * * The default is `true` which means a child added to this Container cannot * belong in another Container, which includes the Scene display list. * * If you disable this then this Container will no longer exclusively manage its children. * This allows you to create all kinds of interesting graphical effects, such as replicating * Game Objects without reparenting them all over the Scene. * However, doing so will prevent children from receiving any kind of input event or have * their physics bodies work by default, as they're no longer a single entity on the * display list, but are being replicated where-ever this Container is. * * @name Phaser.GameObjects.Container#exclusive * @type {boolean} * @default true * @since 3.4.0 */ this.exclusive = true; /** * The cursor position. * * @name Phaser.GameObjects.Container#position * @type {integer} * @since 3.4.0 */ this.position = 0; /** * [description] * * @name Phaser.GameObjects.Container#localTransform * @type {Phaser.GameObjects.Components.TransformMatrix} * @since 3.4.0 */ this.localTransform = new Components.TransformMatrix(); /** * [description] * * @name Phaser.GameObjects.Container#tempTransformMatrix * @type {Phaser.GameObjects.Components.TransformMatrix} * @private * @since 3.4.0 */ this.tempTransformMatrix = new Components.TransformMatrix(); /** * A reference to the Scene Display List. * * @name Phaser.GameObjects.Container#_displayList * @type {Phaser.GameObjects.DisplayList} * @private * @since 3.4.0 */ this._displayList = scene.sys.displayList; /** * The property key to sort by. * * @name Phaser.GameObjects.Container#_sortKey * @type {string} * @since 3.4.0 */ this._sortKey = ''; this.setPosition(x, y); this.clearAlpha(); if (children) { this.add(children); } }, /** * Does this Container exclusively manage its children? * * The default is `true` which means a child added to this Container cannot * belong in another Container, which includes the Scene display list. * * If you disable this then this Container will no longer exclusively manage its children. * This allows you to create all kinds of interesting graphical effects, such as replicating * Game Objects without reparenting them all over the Scene. * However, doing so will prevent children from receiving any kind of input event or have * their physics bodies work by default, as they're no longer a single entity on the * display list, but are being replicated where-ever this Container is. * * @method Phaser.GameObjects.Container#setExclusive * @since 3.4.0 * * @param {boolean} [value=true] - The exclusive state of this Container. * * @return {Phaser.GameObjects.Container} This Container. */ setExclusive: function (value) { if (value === undefined) { value = true; } this.exclusive = value; return this; }, /** * Internal add handler. * * @method Phaser.GameObjects.Container#addHandler * @private * @since 3.4.0 * * @param {Phaser.GameObjects.GameObject} gameObject - The Game Object that was just added to this Container. */ addHandler: function (gameObject) { gameObject.on('destroy', this.remove, this); if (this.exclusive) { this._displayList.remove(gameObject); if (gameObject.parentContainer) { gameObject.parentContainer.remove(gameObject); } gameObject.parentContainer = this; } }, /** * Internal remove handler. * * @method Phaser.GameObjects.Container#removeHandler * @private * @since 3.4.0 * * @param {Phaser.GameObjects.GameObject} gameObject - The Game Object that was just removed from this Container. */ removeHandler: function (gameObject) { gameObject.off('destroy', this.remove, this); if (this.exclusive) { gameObject.parentContainer = null; } }, /** * Takes a Point-like object, such as a Vector2, Geom.Point or object with public x and y properties, * and transforms it into the space of this Container, then returns it in the output object. * * @method Phaser.GameObjects.Container#pointToContainer * @since 3.4.0 * * @param {(object|Phaser.Geom.Point|Phaser.Math.Vector2)} source - The Source Point to be transformed. * @param {(object|Phaser.Geom.Point|Phaser.Math.Vector2)} [output] - A destination object to store the transformed point in. If none given a Vector2 will be created and returned. * * @return {(object|Phaser.Geom.Point|Phaser.Math.Vector2)} The transformed point. */ pointToContainer: function (source, output) { if (output === undefined) { output = new Vector2(); } if (this.parentContainer) { return this.parentContainer.pointToContainer(source, output); } var tempMatrix = this.tempTransformMatrix; // No need to loadIdentity because applyITRS overwrites every value anyway tempMatrix.applyITRS(this.x, this.y, this.rotation, this.scaleX, this.scaleY); tempMatrix.invert(); tempMatrix.transformPoint(source.x, source.y, output); return output; }, /** * Adds the given Game Object, or array of Game Objects, to this Container. * * Each Game Object must be unique within the Container. * * @method Phaser.GameObjects.Container#add * @since 3.4.0 * * @genericUse {T} - [child,$return] * * @param {*|Array.<*>} child - The item, or array of items, to add to the array. Each item must be unique within the array. * * @return {Phaser.GameObjects.Container} This Container instance. */ add: function (child) { ArrayUtils.Add(this.list, child, 0, this.addHandler, this); return this; }, /** * [description] * * @method Phaser.GameObjects.Container#addAt * @since 3.4.0 * * @genericUse {T} - [child,$return] * * @param {*} child - [description] * @param {integer} [index=0] - [description] * * @return {Phaser.GameObjects.Container} This Container instance. */ addAt: function (child, index) { ArrayUtils.AddAt(this.list, child, index, 0, this.addHandler, this); return this; }, /** * [description] * * @method Phaser.GameObjects.Container#getAt * @since 3.4.0 * * @genericUse {T} - [$return] * * @param {integer} index - [description] * * @return {Phaser.GameObjects.GameObject|null} The Game Object at the specified index, or `null` if none found. */ getAt: function (index) { return this.list[index]; }, /** * [description] * * @method Phaser.GameObjects.Container#getIndex * @since 3.4.0 * * @genericUse {T} - [child] * * @param {Phaser.GameObjects.GameObject} child - The Game Object to search for in this Container. * * @return {integer} The index of the Game Object in this Container, or -1 if not found. */ getIndex: function (child) { // Return -1 if given child isn't a child of this display list return this.list.indexOf(child); }, /** * Sort the contents of this Container so the items are in order based on the given property. * For example: `sort('alpha')` would sort the elements based on the value of their `alpha` property. * * @method Phaser.GameObjects.Container#sort * @since 3.4.0 * * @genericUse {T[]} - [children,$return] * * @param {string} property - The property to lexically sort by. * * @return {Phaser.GameObjects.Container} This Container instance. */ sort: function (property) { if (property) { this._sortKey = property; ArrayUtils.StableSort.inplace(this.list, this.sortHandler); } return this; }, /** * Internal sort handler method. * * @method Phaser.GameObjects.Container#sortHandler * @private * @since 3.4.0 * * @genericUse {T} - [childA,childB] * * @param {Phaser.GameObjects.GameObject} childA - The first child to sort. * @param {Phaser.GameObjects.GameObject} childB - The second child to sort. * * @return {integer} The sort results. */ sortHandler: function (childA, childB) { return childA[this._sortKey] - childB[this._sortKey]; }, /** * Searches for the first instance of a child with its `name` property matching the given argument. * Should more than one child have the same name only the first is returned. * * @method Phaser.GameObjects.Container#getByName * @since 3.4.0 * * @genericUse {T | null} - [$return] * * @param {string} name - The name to search for. * * @return {?Phaser.GameObjects.GameObject} The first child with a matching name, or `null` if none were found. */ getByName: function (name) { return ArrayUtils.GetFirst(this.list, 'name', name); }, /** * Returns a random Game Object from this Container. * * @method Phaser.GameObjects.Container#getRandom * @since 3.4.0 * * @genericUse {T | null} - [$return] * * @param {integer} [startIndex=0] - An optional start index. * @param {integer} [length] - An optional length, the total number of elements (from the startIndex) to choose from. * * @return {?Phaser.GameObjects.GameObject} A random child from the Container, or `null` if the Container is empty. */ getRandom: function (startIndex, length) { return ArrayUtils.GetRandom(this.list, startIndex, length); }, /** * Gets the first Game Object in this Container. * * You can also specify a property and value to search for, in which case it will return the first * Game Object in this Container with a matching property and / or value. * * For example: `getFirst('visible', true)` would return the first Game Object that had its `visible` property set. * * You can limit the search to the `startIndex` - `endIndex` range. * * @method Phaser.GameObjects.Container#getFirst * @since 3.4.0 * * @param {string} [property] - The property to test on each Game Object in the Container. * @param {*} [value] - The value to test the property against. Must pass a strict (`===`) comparison check. * @param {integer} [startIndex=0] - An optional start index to search from. * @param {integer} [endIndex=Container.length] - An optional end index to search up to (but not included) * * @return {?Phaser.GameObjects.GameObject} The first matching Game Object, or `null` if none was found. */ getFirst: function (property, value, startIndex, endIndex) { return ArrayUtils.GetFirstElement(this.list, property, value, startIndex, endIndex); }, /** * Returns all Game Objects in this Container. * * You can optionally specify a matching criteria using the `property` and `value` arguments. * * For example: `getAll('body')` would return only Game Objects that have a body property. * * You can also specify a value to compare the property to: * * `getAll('visible', true)` would return only Game Objects that have their visible property set to `true`. * * Optionally you can specify a start and end index. For example if this Container had 100 Game Objects, * and you set `startIndex` to 0 and `endIndex` to 50, it would return matches from only * the first 50 Game Objects. * * @method Phaser.GameObjects.Container#getAll * @since 3.4.0 * * @param {string} [property] - The property to test on each Game Object in the Container. * @param {*} [value] - If property is set then the `property` must strictly equal this value to be included in the results. * @param {integer} [startIndex=0] - An optional start index to search from. * @param {integer} [endIndex=Container.length] - An optional end index to search up to (but not included) * * @return {Phaser.GameObjects.GameObject[]} An array of matching Game Objects from this Container. */ getAll: function (property, value, startIndex, endIndex) { return ArrayUtils.GetAll(this.list, property, value, startIndex, endIndex); }, /** * Returns the total number of Game Objects in this Container that have a property * matching the given value. * * For example: `count('visible', true)` would count all the elements that have their visible property set. * * You can optionally limit the operation to the `startIndex` - `endIndex` range. * * @method Phaser.GameObjects.Container#count * @since 3.4.0 * * @param {string} property - [description] * @param {*} value - [description] * @param {integer} [startIndex=0] - An optional start index to search from. * @param {integer} [endIndex=Container.length] - An optional end index to search up to (but not included) * * @return {integer} The total number of Game Objects in this Container with a property matching the given value. */ count: function (property, value, startIndex, endIndex) { return ArrayUtils.CountAllMatching(this.list, property, value, startIndex, endIndex); }, /** * Swaps the position of two Game Objects in this Container. * Both Game Objects must belong to this Container. * * @method Phaser.GameObjects.Container#swap * @since 3.4.0 * * @genericUse {T} - [child1,child2] * * @param {Phaser.GameObjects.GameObject} child1 - The first Game Object to swap. * @param {Phaser.GameObjects.GameObject} child2 - The second Game Object to swap. * * @return {Phaser.GameObjects.Container} This Container instance. */ swap: function (child1, child2) { ArrayUtils.Swap(this.list, child1, child2); return this; }, /** * Moves a Game Object to a new position within this Container. * * The Game Object must already be a child of this Container. * * The Game Object is removed from its old position and inserted into the new one. * Therefore the Container size does not change. Other children will change position accordingly. * * @method Phaser.GameObjects.Container#moveTo * @since 3.4.0 * * @genericUse {T} - [child,$return] * * @param {Phaser.GameObjects.GameObject} child - The Game Object to move. * @param {integer} index - The new position of the Game Object in this Container. * * @return {Phaser.GameObjects.Container} This Container instance. */ moveTo: function (child, index) { ArrayUtils.MoveTo(this.list, child, index); return this; }, /** * Removes the given Game Object, or array of Game Objects, from this Container. * * The Game Objects must already be children of this Container. * * You can also optionally call `destroy` on each Game Object that is removed from the Container. * * @method Phaser.GameObjects.Container#remove * @since 3.4.0 * * @genericUse {T} - [child,$return] * * @param {Phaser.GameObjects.GameObject|Phaser.GameObjects.GameObject[]} child - The Game Object, or array of Game Objects, to be removed from the Container. * @param {boolean} [destroyChild=false] - Optionally call `destroy` on each child successfully removed from this Container. * * @return {Phaser.GameObjects.Container} This Container instance. */ remove: function (child, destroyChild) { var removed = ArrayUtils.Remove(this.list, child, this.removeHandler, this); if (destroyChild && removed) { if (!Array.isArray(removed)) { removed = [ removed ]; } for (var i = 0; i < removed.length; i++) { removed[i].destroy(); } } return this; }, /** * Removes the Game Object at the given position in this Container. * * You can also optionally call `destroy` on the Game Object, if one is found. * * @method Phaser.GameObjects.Container#removeAt * @since 3.4.0 * * @genericUse {T} - [$return] * * @param {integer} index - The index of the Game Object to be removed. * @param {boolean} [destroyChild=false] - Optionally call `destroy` on the Game Object if successfully removed from this Container. * * @return {Phaser.GameObjects.Container} This Container instance. */ removeAt: function (index, destroyChild) { var removed = ArrayUtils.RemoveAt(this.list, index, this.removeHandler, this); if (destroyChild && removed) { removed.destroy(); } return this; }, /** * Removes the Game Objects between the given positions in this Container. * * You can also optionally call `destroy` on each Game Object that is removed from the Container. * * @method Phaser.GameObjects.Container#removeBetween * @since 3.4.0 * * @genericUse {T[]} - [$return] * * @param {integer} [startIndex=0] - An optional start index to search from. * @param {integer} [endIndex=Container.length] - An optional end index to search up to (but not included) * @param {boolean} [destroyChild=false] - Optionally call `destroy` on each Game Object successfully removed from this Container. * * @return {Phaser.GameObjects.Container} This Container instance. */ removeBetween: function (startIndex, endIndex, destroyChild) { var removed = ArrayUtils.RemoveBetween(this.list, startIndex, endIndex, this.removeHandler, this); if (destroyChild) { for (var i = 0; i < removed.length; i++) { removed[i].destroy(); } } return this; }, /** * Removes all Game Objects from this Container. * * You can also optionally call `destroy` on each Game Object that is removed from the Container. * * @method Phaser.GameObjects.Container#removeAll * @since 3.4.0 * * @genericUse {Phaser.GameObjects.Container.} - [$return] * * @param {boolean} [destroyChild=false] - Optionally call `destroy` on each Game Object successfully removed from this Container. * * @return {Phaser.GameObjects.Container} This Container instance. */ removeAll: function (destroyChild) { var removed = ArrayUtils.RemoveBetween(this.list, 0, this.list.length, this.removeHandler, this); if (destroyChild) { for (var i = 0; i < removed.length; i++) { removed[i].destroy(); } } return this; }, /** * Brings the given Game Object to the top of this Container. * This will cause it to render on-top of any other objects in the Container. * * @method Phaser.GameObjects.Container#bringToTop * @since 3.4.0 * * @genericUse {T} - [child,$return] * * @param {Phaser.GameObjects.GameObject} child - The Game Object to bring to the top of the Container. * * @return {Phaser.GameObjects.Container} This Container instance. */ bringToTop: function (child) { ArrayUtils.BringToTop(this.list, child); return this; }, /** * Sends the given Game Object to the bottom of this Container. * This will cause it to render below any other objects in the Container. * * @method Phaser.GameObjects.Container#sendToBack * @since 3.4.0 * * @genericUse {T} - [child,$return] * * @param {Phaser.GameObjects.GameObject} child - The Game Object to send to the bottom of the Container. * * @return {Phaser.GameObjects.Container} This Container instance. */ sendToBack: function (child) { ArrayUtils.SendToBack(this.list, child); return this; }, /** * Moves the given Game Object up one place in this Container, unless it's already at the top. * * @method Phaser.GameObjects.Container#moveUp * @since 3.4.0 * * @genericUse {T} - [child,$return] * * @param {Phaser.GameObjects.GameObject} child - The Game Object to be moved in the Container. * * @return {Phaser.GameObjects.Container} This Container instance. */ moveUp: function (child) { ArrayUtils.MoveUp(this.list, child); return this; }, /** * Moves the given Game Object down one place in this Container, unless it's already at the bottom. * * @method Phaser.GameObjects.Container#moveDown * @since 3.4.0 * * @genericUse {T} - [child,$return] * * @param {Phaser.GameObjects.GameObject} child - The Game Object to be moved in the Container. * * @return {Phaser.GameObjects.Container} This Container instance. */ moveDown: function (child) { ArrayUtils.MoveDown(this.list, child); return this; }, /** * Reverses the order of all Game Objects in this Container. * * @method Phaser.GameObjects.Container#reverse * @since 3.4.0 * * @genericUse {Phaser.GameObjects.Container.} - [$return] * * @return {Phaser.GameObjects.Container} This Container instance. */ reverse: function () { this.list.reverse(); return this; }, /** * Shuffles the all Game Objects in this Container using the Fisher-Yates implementation. * * @method Phaser.GameObjects.Container#shuffle * @since 3.4.0 * * @genericUse {Phaser.GameObjects.Container.} - [$return] * * @return {Phaser.GameObjects.Container} This Container instance. */ shuffle: function () { ArrayUtils.Shuffle(this.list); return this; }, /** * Replaces a Game Object in this Container with the new Game Object. * The new Game Object cannot already be a child of this Container. * * @method Phaser.GameObjects.Container#replace * @since 3.4.0 * * @genericUse {T} - [oldChild,newChild,$return] * * @param {Phaser.GameObjects.GameObject} oldChild - The Game Object in this Container that will be replaced. * @param {Phaser.GameObjects.GameObject} newChild - The Game Object to be added to this Container. * @param {boolean} [destroyChild=false] - Optionally call `destroy` on the Game Object if successfully removed from this Container. * * @return {Phaser.GameObjects.Container} This Container instance. */ replace: function (oldChild, newChild, destroyChild) { var moved = ArrayUtils.Replace(this.list, oldChild, newChild); if (moved) { this.addHandler(newChild); this.removeHandler(oldChild); if (destroyChild) { oldChild.destroy(); } } return this; }, /** * Returns `true` if the given Game Object is a direct child of this Container. * * This check does not scan nested Containers. * * @method Phaser.GameObjects.Container#exists * @since 3.4.0 * * @genericUse {T} - [child] * * @param {Phaser.GameObjects.GameObject} child - The Game Object to check for within this Container. * * @return {boolean} True if the Game Object is an immediate child of this Container, otherwise false. */ exists: function (child) { return (this.list.indexOf(child) > -1); }, /** * Sets the property to the given value on all Game Objects in this Container. * * Optionally you can specify a start and end index. For example if this Container had 100 Game Objects, * and you set `startIndex` to 0 and `endIndex` to 50, it would return matches from only * the first 50 Game Objects. * * @method Phaser.GameObjects.Container#setAll * @since 3.4.0 * * @genericUse {T} - [value] * * @param {string} property - The property that must exist on the Game Object. * @param {*} value - The value to get the property to. * @param {integer} [startIndex=0] - An optional start index to search from. * @param {integer} [endIndex=Container.length] - An optional end index to search up to (but not included) * * @return {Phaser.GameObjects.Container} This Container instance. */ setAll: function (property, value, startIndex, endIndex) { ArrayUtils.SetAll(this.list, property, value, startIndex, endIndex); return this; }, /** * Passes all Game Objects in this Container to the given callback. * * A copy of the Container is made before passing each entry to your callback. * This protects against the callback itself modifying the Container. * * If you know for sure that the callback will not change the size of this Container * then you can use the more performant `Container.iterate` method instead. * * @method Phaser.GameObjects.Container#each * @since 3.4.0 * * @genericUse {EachListCallback.} - [callback] * * @param {EachListCallback} callback - The function to call. * @param {*} [context] - Value to use as `this` when executing callback. * @param {...*} [args] - Additional arguments that will be passed to the callback, after the child. * * @return {Phaser.GameObjects.Container} This Container instance. */ each: function (callback, context) { var args = [ null ]; var i; var temp = this.list.slice(); var len = temp.length; for (i = 2; i < arguments.length; i++) { args.push(arguments[i]); } for (i = 0; i < len; i++) { args[0] = temp[i]; callback.apply(context, args); } return this; }, /** * Passes all Game Objects in this Container to the given callback. * * Only use this method when you absolutely know that the Container will not be modified during * the iteration, i.e. by removing or adding to its contents. * * @method Phaser.GameObjects.Container#iterate * @since 3.4.0 * * @genericUse {EachListCallback.} - [callback] * * @param {EachListCallback} callback - The function to call. * @param {*} [context] - Value to use as `this` when executing callback. * @param {...*} [args] - Additional arguments that will be passed to the callback, after the child. * * @return {Phaser.GameObjects.Container} This Container instance. */ iterate: function (callback, context) { var args = [ null ]; var i; for (i = 2; i < arguments.length; i++) { args.push(arguments[i]); } for (i = 0; i < this.list.length; i++) { args[0] = this.list[i]; callback.apply(context, args); } return this; }, /** * The number of Game Objects inside this Container. * * @name Phaser.GameObjects.Container#length * @type {integer} * @readOnly * @since 3.4.0 */ length: { get: function () { return this.list.length; } }, /** * Returns the first Game Object within the Container, or `null` if it is empty. * * You can move the cursor by calling `Container.next` and `Container.previous`. * * @name Phaser.GameObjects.Container#first * @type {?Phaser.GameObjects.GameObject} * @readOnly * @since 3.4.0 */ first: { get: function () { this.position = 0; if (this.list.length > 0) { return this.list[0]; } else { return null; } } }, /** * Returns the last Game Object within the Container, or `null` if it is empty. * * You can move the cursor by calling `Container.next` and `Container.previous`. * * @name Phaser.GameObjects.Container#last * @type {?Phaser.GameObjects.GameObject} * @readOnly * @since 3.4.0 */ last: { get: function () { if (this.list.length > 0) { this.position = this.list.length - 1; return this.list[this.position]; } else { return null; } } }, /** * Returns the next Game Object within the Container, or `null` if it is empty. * * You can move the cursor by calling `Container.next` and `Container.previous`. * * @name Phaser.GameObjects.Container#next * @type {?Phaser.GameObjects.GameObject} * @readOnly * @since 3.4.0 */ next: { get: function () { if (this.position < this.list.length) { this.position++; return this.list[this.position]; } else { return null; } } }, /** * Returns the previous Game Object within the Container, or `null` if it is empty. * * You can move the cursor by calling `Container.next` and `Container.previous`. * * @name Phaser.GameObjects.Container#previous * @type {?Phaser.GameObjects.GameObject} * @readOnly * @since 3.4.0 */ previous: { get: function () { if (this.position > 0) { this.position--; return this.list[this.position]; } else { return null; } } }, /** * Destroys this Container, removing it from the Display List. * * If `Container.exclusive` is `true` then it will also destroy all children. * * Use this to remove a Container from your game if you don't ever plan to use it again. * As long as no reference to it exists within your own code it should become free for * garbage collection. * * If you just want to temporarily disable an object then look at using the * Game Object Pool instead of destroying it, as destroyed objects cannot be resurrected. * * @method Phaser.GameObjects.Container#destroy * @since 3.4.0 */ destroy: function () { this.removeAll(this.exclusive); this.localTransform.destroy(); this.tempTransformMatrix.destroy(); this.list = []; this._displayList = null; GameObject.prototype.destroy.call(this); } }); module.exports = Container;