No longer extends List, but implements its own methods directly.

This commit is contained in:
Richard Davey 2018-04-10 15:21:46 +01:00
parent 82e99bf130
commit be7b52b1b9

View file

@ -5,10 +5,10 @@
* @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 List = require('../../structs/List');
var Render = require('./ContainerRender');
var Vector2 = require('../../math/Vector2');
@ -22,9 +22,11 @@ var Vector2 = require('../../math/Vector2');
* @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.Structs.List
* @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.
@ -41,8 +43,7 @@ var Container = new Class({
Components.Depth,
Components.Transform,
Components.Visible,
Render,
List
Render
],
initialize:
@ -51,16 +52,6 @@ var Container = new Class({
{
GameObject.call(this, scene, 'Container');
/**
* The parent property as used by the List methods.
*
* @name Phaser.GameObjects.Container#parent
* @type {Phaser.GameObjects.Container}
* @private
* @since 3.4.0
*/
this.parent = this;
/**
* An array holding the children of this Container.
*
@ -117,27 +108,7 @@ var Container = new Class({
* @since 3.4.0
*/
this.tempTransformMatrix = new Components.TransformMatrix();
/**
* A callback that is invoked every time a child is added to this list.
*
* @name Phaser.GameObjects.Container#addCallback
* @type {function}
* @private
* @since 3.4.0
*/
this.addCallback = this.addHandler.bind(this);
/**
* A callback that is invoked every time a child is removed from this list.
*
* @name Phaser.GameObjects.Container#removeCallback
* @type {function}
* @private
* @since 3.4.0
*/
this.removeCallback = this.removeHandler.bind(this);
/**
* A reference to the Scene Display List.
*
@ -148,7 +119,17 @@ var Container = new Class({
*/
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)
@ -193,7 +174,6 @@ var Container = new Class({
* @private
* @since 3.4.0
*
* @param {Phaser.GameObjects.Container} list - The List the Game Object was added to (also this Container)
* @param {Phaser.GameObjects.GameObject} gameObject - The Game Object that was just added to this Container.
*/
addHandler: function (gameObject)
@ -220,7 +200,6 @@ var Container = new Class({
* @private
* @since 3.4.0
*
* @param {Phaser.GameObjects.Container} list - The List the Game Object was removed from (also this Container)
* @param {Phaser.GameObjects.GameObject} gameObject - The Game Object that was just removed from this Container.
*/
removeHandler: function (gameObject)
@ -267,12 +246,815 @@ var Container = new Class({
},
/**
* Destroys this Container removing it and all children from the Display List and
* severing all ties to parent resources.
* 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;
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)
{
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.<T>} - [$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.<T>} - [$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.<T>} - [$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.<T>} - [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.<T>} - [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 by the browser.
* 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.
@ -282,46 +1064,15 @@ var Container = new Class({
*/
destroy: function ()
{
// This Game Object had already been destroyed
if (!this.scene)
{
return;
}
this.removeAll(this.exclusive);
if (this.preDestroy)
{
this.preDestroy.call(this);
}
this.emit('destroy', this);
var sys = this.scene.sys;
sys.displayList.remove(this);
if (this.data)
{
this.data.destroy();
this.data = undefined;
}
// Tell the Scene to re-sort the children
sys.queueDepthSort();
this.active = false;
this.visible = false;
this.scene = undefined;
this.removeAllListeners();
this.removeAll();
this.localTransform.destroy();
this.tempTransformMatrix.destroy();
this.list = [];
this._displayList = null;
this.parent = null;
this.parentContainer = null;
GameObject.prototype.destroy.call(this);
}
});