UNPKG

phaser-ce

Version:

Phaser CE (Community Edition) is a fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers.

1,743 lines (1,410 loc) 1.69 MB
/** * @author Richard Davey <rich@photonstorm.com> * @copyright 2016 Photon Storm Ltd. * @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} * * @overview * * Phaser - http://phaser.io * * v2.9.2 "2017-11-09" - Built: Thu Nov 09 2017 18:05:50 * * By Richard Davey http://www.photonstorm.com @photonstorm * * Phaser is a fun, free and fast 2D game framework for making HTML5 games * for desktop and mobile web browsers, supporting Canvas and WebGL rendering. * * Phaser uses Pixi.js for rendering, created by Mat Groves http://matgroves.com @Doormat23 * Phaser uses p2.js for full-body physics, created by Stefan Hedman https://github.com/schteppe/p2.js @schteppe * Phaser contains a port of N+ Physics, converted by Richard Davey, original by http://www.metanetsoftware.com * * Many thanks to Adam Saltsman (@ADAMATOMIC) for releasing Flixel, from which both Phaser and my love of framework development originate. * * Follow development at http://phaser.io and on our forum * * "If you want your children to be intelligent, read them fairy tales." * "If you want them to be more intelligent, read them more fairy tales." * -- Albert Einstein */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ (function(){ var root = this; /** * Namespace-class for [pixi.js](http://www.pixijs.com/). * * Contains assorted static properties and enumerations. * * @namespace PIXI * @author Mat Groves http://matgroves.com/ @Doormat23 */ var PIXI = PIXI || {}; /** * @author Mat Groves http://matgroves.com @Doormat23 * @author Richard Davey <rich@photonstorm.com> * @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} */ /** * The base class for all objects that are rendered. Contains properties for position, scaling, * rotation, masks and cache handling. * * This is an abstract class and should not be used on its own, rather it should be extended. * * It is used internally by the likes of PIXI.Sprite. * * @class PIXI.DisplayObject * @constructor */ PIXI.DisplayObject = function () { /** * The coordinates, in pixels, of this DisplayObject, relative to its parent container. * * The value of this property does not reflect any positioning happening further up the display list. * To obtain that value please see the `worldPosition` property. * * @property {PIXI.Point} position * @default */ this.position = new PIXI.Point(0, 0); /** * The scale of this DisplayObject. A scale of 1:1 represents the DisplayObject * at its default size. A value of 0.5 would scale this DisplayObject by half, and so on. * * The value of this property does not reflect any scaling happening further up the display list. * To obtain that value please see the `worldScale` property. * * @property {PIXI.Point} scale * @default */ this.scale = new PIXI.Point(1, 1); /** * The pivot point of this DisplayObject that it rotates around. The values are expressed * in pixel values. * @property {PIXI.Point} pivot * @default */ this.pivot = new PIXI.Point(0, 0); /** * The rotation of this DisplayObject. The value is given, and expressed, in radians, and is based on * a right-handed orientation. * * The value of this property does not reflect any rotation happening further up the display list. * To obtain that value please see the `worldRotation` property. * * @property {number} rotation * @default */ this.rotation = 0; /** * The alpha value of this DisplayObject. A value of 1 is fully opaque. A value of 0 is transparent. * Please note that an object with an alpha value of 0 is skipped during the render pass. * * The value of this property does not reflect any alpha values set further up the display list. * To obtain that value please see the `worldAlpha` property. * * @property {number} alpha * @default */ this.alpha = 1; /** * The visibility of this DisplayObject. A value of `false` makes the object invisible. * A value of `true` makes it visible. Please note that an object with a visible value of * `false` is skipped during the render pass. Equally a DisplayObject with visible false will * not render any of its children. * * The value of this property does not reflect any visible values set further up the display list. * To obtain that value please see the `worldVisible` property. * * @property {boolean} visible * @default */ this.visible = true; /** * This is the defined area that will pick up mouse / touch events. It is null by default. * Setting it is a neat way of optimising the hitTest function that the interactionManager will use (as it will not need to hit test all the children) * * @property hitArea * @type Rectangle|Circle|Ellipse|Polygon */ this.hitArea = null; /** * Should this DisplayObject be rendered by the renderer? An object with a renderable value of * `false` is skipped during the render pass. * * @property {boolean} renderable * @default */ this.renderable = false; /** * The parent DisplayObjectContainer that this DisplayObject is a child of. * All DisplayObjects must belong to a parent in order to be rendered. * The root parent is the Stage object. This property is set automatically when the * DisplayObject is added to, or removed from, a DisplayObjectContainer. * * @property {PIXI.DisplayObjectContainer} parent * @default * @readOnly */ this.parent = null; /** * The multiplied alpha value of this DisplayObject. A value of 1 is fully opaque. A value of 0 is transparent. * This value is the calculated total, based on the alpha values of all parents of this DisplayObjects * in the display list. * * To obtain, and set, the local alpha value, see the `alpha` property. * * Note: This property is only updated at the end of the `updateTransform` call, once per render. Until * that happens this property will contain values based on the previous frame. Be mindful of this if * accessing this property outside of the normal game flow, i.e. from an asynchronous event callback. * * @property {number} worldAlpha * @readOnly */ this.worldAlpha = 1; /** * The current transform of this DisplayObject. * * This property contains the calculated total, based on the transforms of all parents of this * DisplayObject in the display list. * * Note: This property is only updated at the end of the `updateTransform` call, once per render. Until * that happens this property will contain values based on the previous frame. Be mindful of this if * accessing this property outside of the normal game flow, i.e. from an asynchronous event callback. * * @property {Phaser.Matrix} worldTransform * @readOnly */ this.worldTransform = new Phaser.Matrix(); /** * The coordinates, in pixels, of this DisplayObject within the world. * * This property contains the calculated total, based on the positions of all parents of this * DisplayObject in the display list. * * Note: This property is only updated at the end of the `updateTransform` call, once per render. Until * that happens this property will contain values based on the previous frame. Be mindful of this if * accessing this property outside of the normal game flow, i.e. from an asynchronous event callback. * * @property {PIXI.Point} worldPosition * @readOnly */ this.worldPosition = new PIXI.Point(0, 0); /** * The global scale of this DisplayObject. * * This property contains the calculated total, based on the scales of all parents of this * DisplayObject in the display list. * * Note: This property is only updated at the end of the `updateTransform` call, once per render. Until * that happens this property will contain values based on the previous frame. Be mindful of this if * accessing this property outside of the normal game flow, i.e. from an asynchronous event callback. * * @property {PIXI.Point} worldScale * @readOnly */ this.worldScale = new PIXI.Point(1, 1); /** * The rotation, in radians, of this DisplayObject. * * This property contains the calculated total, based on the rotations of all parents of this * DisplayObject in the display list. * * Note: This property is only updated at the end of the `updateTransform` call, once per render. Until * that happens this property will contain values based on the previous frame. Be mindful of this if * accessing this property outside of the normal game flow, i.e. from an asynchronous event callback. * * @property {number} worldRotation * @readOnly */ this.worldRotation = 0; /** * The rectangular area used by filters when rendering a shader for this DisplayObject. * * @property {PIXI.Rectangle} filterArea * @type Rectangle * @default */ this.filterArea = null; /** * @property {number} _sr - Cached rotation value. * @private */ this._sr = 0; /** * @property {number} _cr - Cached rotation value. * @private */ this._cr = 1; /** * @property {PIXI.Rectangle} _bounds - The cached bounds of this object. * @private */ this._bounds = new PIXI.Rectangle(0, 0, 0, 0); /** * @property {PIXI.Rectangle} _currentBounds - The most recently calculated bounds of this object. * @private */ this._currentBounds = null; /** * @property {PIXI.Rectangle} _mask - The cached mask of this object. * @private */ this._mask = null; /** * @property {boolean} _cacheAsBitmap - Internal cache as bitmap flag. * @private */ this._cacheAsBitmap = false; /** * @property {boolean} _cacheIsDirty - Internal dirty cache flag. * @private */ this._cacheIsDirty = false; }; PIXI.DisplayObject.prototype = { constructor: PIXI.DisplayObject, /** * Destroy this DisplayObject. * * Removes any cached sprites, sets renderable flag to false, and nulls filters, bounds and mask. * * Also iteratively calls `destroy` on any children. * * @method PIXI.DisplayObject#destroy */ destroy: function () { if (this.children) { var i = this.children.length; while (i--) { this.children[i].destroy(); } this.children = []; } this.hitArea = null; this.parent = null; this.worldTransform = null; this.filterArea = null; this.renderable = false; this._bounds = null; this._currentBounds = null; this._mask = null; this._destroyCachedSprite(); }, /** * Updates the transform matrix this DisplayObject uses for rendering. * * If the object has no parent, and no parent parameter is provided, it will default to * Phaser.Game.World as the parent transform to use. If that is unavailable the transform fails to take place. * * The `parent` parameter has priority over the actual parent. Use it as a parent override. * Setting it does **not** change the actual parent of this DisplayObject. * * Calling this method updates the `worldTransform`, `worldAlpha`, `worldPosition`, `worldScale` * and `worldRotation` properties. * * If a `transformCallback` has been specified, it is called at the end of this method, and is passed * the new, updated, worldTransform property, along with the parent transform used. * * @method PIXI.DisplayObject#updateTransform * @param {PIXI.DisplayObjectContainer} [parent] - Optional parent to calculate this DisplayObjects transform from. * @return {PIXI.DisplayObject} - A reference to this DisplayObject. */ updateTransform: function (parent) { if (!parent && !this.parent && !this.game) { return this; } var p = this.parent; if (parent) { p = parent; } else if (!this.parent) { p = this.game.world; } // create some matrix refs for easy access var pt = p.worldTransform; var wt = this.worldTransform; // temporary matrix variables var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. if (this.rotation % Phaser.Math.PI2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes if (this.rotation !== this.rotationCache) { this.rotationCache = this.rotation; this._sr = Math.sin(this.rotation); this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. a = this._cr * this.scale.x; b = this._sr * this.scale.x; c = -this._sr * this.scale.y; d = this._cr * this.scale.y; tx = this.position.x; ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) { tx -= this.pivot.x * a + this.pivot.y * c; ty -= this.pivot.x * b + this.pivot.y * d; } // concat the parent matrix with the objects transform. wt.a = a * pt.a + b * pt.c; wt.b = a * pt.b + b * pt.d; wt.c = c * pt.a + d * pt.c; wt.d = c * pt.b + d * pt.d; wt.tx = tx * pt.a + ty * pt.c + pt.tx; wt.ty = tx * pt.b + ty * pt.d + pt.ty; } else { // lets do the fast version as we know there is no rotation.. a = this.scale.x; b = 0; c = 0; d = this.scale.y; tx = this.position.x - this.pivot.x * a; ty = this.position.y - this.pivot.y * d; wt.a = a * pt.a; wt.b = a * pt.b; wt.c = d * pt.c; wt.d = d * pt.d; wt.tx = tx * pt.a + ty * pt.c + pt.tx; wt.ty = tx * pt.b + ty * pt.d + pt.ty; } a = wt.a; b = wt.b; c = wt.c; d = wt.d; var determ = (a * d) - (b * c); if (a || b) { var r = Math.sqrt((a * a) + (b * b)); this.worldRotation = (b > 0) ? Math.acos(a / r) : -Math.acos(a / r); this.worldScale.x = r; this.worldScale.y = determ / r; } else if (c || d) { var s = Math.sqrt((c * c) + (d * d)); this.worldRotation = Phaser.Math.HALF_PI - ((d > 0) ? Math.acos(-c / s) : -Math.acos(c / s)); this.worldScale.x = determ / s; this.worldScale.y = s; } else { this.worldScale.x = 0; this.worldScale.y = 0; } // Set the World values this.worldAlpha = this.alpha * p.worldAlpha; this.worldPosition.x = wt.tx; this.worldPosition.y = wt.ty; // reset the bounds each time this is called! this._currentBounds = null; // Custom callback? if (this.transformCallback) { this.transformCallback.call(this.transformCallbackContext, wt, pt); } return this; }, /** * To be overridden by classes that require it. * * @method PIXI.DisplayObject#preUpdate */ preUpdate: function () { }, /** * Generates a RenderTexture based on this DisplayObject, which can they be used to texture other Sprites. * This can be useful if your DisplayObject is static, or complicated, and needs to be reused multiple times. * * Please note that no garbage collection takes place on old textures. It is up to you to destroy old textures, * and references to them, so they don't linger in memory. * * @method PIXI.DisplayObject#generateTexture * @param {number} [resolution=1] - The resolution of the texture being generated. * @param {number} [scaleMode=PIXI.scaleModes.DEFAULT] - See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values. * @param {PIXI.CanvasRenderer|PIXI.WebGLRenderer} renderer - The renderer used to generate the texture. * @return {Phaser.RenderTexture} - A RenderTexture containing an image of this DisplayObject at the time it was invoked. */ generateTexture: function (resolution, scaleMode, renderer) { var bounds = this.getLocalBounds(); var renderTexture = new Phaser.RenderTexture(this.game, bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution); PIXI.DisplayObject._tempMatrix.tx = -bounds.x; PIXI.DisplayObject._tempMatrix.ty = -bounds.y; renderTexture.render(this, PIXI.DisplayObject._tempMatrix); return renderTexture; }, /** * If this DisplayObject has a cached Sprite, this method generates and updates it. * * @method PIXI.DisplayObject#updateCache * @return {PIXI.DisplayObject} - A reference to this DisplayObject. */ updateCache: function () { this._generateCachedSprite(); return this; }, /** * Calculates the global position of this DisplayObject, based on the position given. * * @method PIXI.DisplayObject#toGlobal * @param {PIXI.Point} position - The global position to calculate from. * @return {PIXI.Point} - A point object representing the position of this DisplayObject based on the global position given. */ toGlobal: function (position) { this.updateTransform(); return this.worldTransform.apply(position); }, /** * Calculates the local position of this DisplayObject, relative to another point. * * @method PIXI.DisplayObject#toLocal * @param {PIXI.Point} position - The world origin to calculate from. * @param {PIXI.DisplayObject} [from] - An optional DisplayObject to calculate the global position from. * @return {PIXI.Point} - A point object representing the position of this DisplayObject based on the global position given. */ toLocal: function (position, from) { if (from) { position = from.toGlobal(position); } this.updateTransform(); return this.worldTransform.applyInverse(position); }, /** * Internal method. * * @method PIXI.DisplayObject#_renderCachedSprite * @private * @param {Object} renderSession - The render session */ _renderCachedSprite: function (renderSession) { this._cachedSprite.worldAlpha = this.worldAlpha; if (renderSession.gl) { PIXI.Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); } else { PIXI.Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession); } }, /** * Internal method. * * @method PIXI.DisplayObject#_generateCachedSprite * @private */ _generateCachedSprite: function () { this._cacheAsBitmap = false; var bounds = this.getLocalBounds(); // Round it off and force non-zero dimensions bounds.width = Math.max(1, Math.ceil(bounds.width)); bounds.height = Math.max(1, Math.ceil(bounds.height)); this.updateTransform(); if (!this._cachedSprite) { var textureUnit = 0; if (this.texture && this.texture.baseTexture && PIXI._enableMultiTextureToggle) { textureUnit = this.texture.baseTexture.textureIndex; } var renderTexture = new Phaser.RenderTexture(this.game, bounds.width, bounds.height, undefined, undefined, undefined, undefined, textureUnit); this._cachedSprite = new PIXI.Sprite(renderTexture); this._cachedSprite.worldTransform = this.worldTransform; } else { this._cachedSprite.texture.resize(bounds.width, bounds.height); } // Remove filters var tempFilters = this._filters; this._filters = null; this._cachedSprite.filters = tempFilters; PIXI.DisplayObject._tempMatrix.tx = -bounds.x; PIXI.DisplayObject._tempMatrix.ty = -bounds.y; this._cachedSprite.texture.render(this, PIXI.DisplayObject._tempMatrix, true); this._cachedSprite.anchor.x = -(bounds.x / bounds.width); this._cachedSprite.anchor.y = -(bounds.y / bounds.height); this._filters = tempFilters; this._cacheAsBitmap = true; }, /** * Destroys a cached Sprite. * * @method PIXI.DisplayObject#_destroyCachedSprite * @private */ _destroyCachedSprite: function () { if (!this._cachedSprite) { return; } this._cachedSprite.texture.destroy(true); this._cachedSprite = null; } }; // Alias for updateTransform. As used in DisplayObject container, etc. PIXI.DisplayObject.prototype.displayObjectUpdateTransform = PIXI.DisplayObject.prototype.updateTransform; Object.defineProperties(PIXI.DisplayObject.prototype, { /** * The horizontal position of the DisplayObject, in pixels, relative to its parent. * If you need the world position of the DisplayObject, use `DisplayObject.worldPosition` instead. * @name PIXI.DisplayObject#x * @property {number} x - The horizontal position of the DisplayObject, in pixels, relative to its parent. */ 'x': { get: function () { return this.position.x; }, set: function (value) { this.position.x = value; } }, /** * The vertical position of the DisplayObject, in pixels, relative to its parent. * If you need the world position of the DisplayObject, use `DisplayObject.worldPosition` instead. * @name PIXI.DisplayObject#y * @property {number} y - The vertical position of the DisplayObject, in pixels, relative to its parent. */ 'y': { get: function () { return this.position.y; }, set: function (value) { this.position.y = value; } }, /** * Indicates if this DisplayObject is visible, based on it, and all of its parents, `visible` property values. * @name PIXI.DisplayObject#worldVisible * @property {boolean} worldVisible - Indicates if this DisplayObject is visible, based on it, and all of its parents, `visible` property values. */ 'worldVisible': { get: function () { if (!this.visible) { return false; } else { var item = this.parent; if (!item) { return this.visible; } else { do { if (!item.visible) { return false; } item = item.parent; } while (item); } return true; } } }, /** * Sets a mask for this DisplayObject. A mask is an instance of a Graphics object. * When applied it limits the visible area of this DisplayObject to the shape of the mask. * Under a Canvas renderer it uses shape clipping. Under a WebGL renderer it uses a Stencil Buffer. * To remove a mask, set this property to `null`. * * @name PIXI.DisplayObject#mask * @property {Phaser.Graphics} mask - The mask applied to this DisplayObject. Set to `null` to remove an existing mask. */ 'mask': { get: function () { return this._mask; }, set: function (value) { if (this._mask) { this._mask.isMask = false; } this._mask = value; if (value) { this._mask.isMask = true; } } }, /** * Sets the filters for this DisplayObject. This is a WebGL only feature, and is ignored by the Canvas * Renderer. A filter is a shader applied to this DisplayObject. You can modify the placement of the filter * using `DisplayObject.filterArea`. * * To remove filters, set this property to `null`. * * Note: You cannot have a filter set, and a MULTIPLY Blend Mode active, at the same time. Setting a * filter will reset this DisplayObjects blend mode to NORMAL. * * @name PIXI.DisplayObject#filters * @property {Array} filters - An Array of Phaser.Filter objects, or objects that extend them. */ 'filters': { get: function () { return this._filters; }, set: function (value) { if (Array.isArray(value)) { // Put all the passes in one place. var passes = []; for (var i = 0; i < value.length; i++) { var filterPasses = value[i].passes; for (var j = 0; j < filterPasses.length; j++) { passes.push(filterPasses[j]); } } // Needed any more? this._filterBlock = { target: this, filterPasses: passes }; } this._filters = value; if (this.blendMode && this.blendMode === PIXI.blendModes.MULTIPLY) { this.blendMode = PIXI.blendModes.NORMAL; } } }, /** * Sets if this DisplayObject should be cached as a bitmap. * * When invoked it will take a snapshot of the DisplayObject, as it is at that moment, and store it * in a RenderTexture. This is then used whenever this DisplayObject is rendered. It can provide a * performance benefit for complex, but static, DisplayObjects. I.e. those with lots of children. * * Transparent areas adjoining the edges may be removed ({@link https://github.com/photonstorm/phaser-ce/issues/283 #283}). * * Cached Bitmaps do not track their parents. If you update a property of this DisplayObject, it will not * re-generate the cached bitmap automatically. To do that you need to call `DisplayObject.updateCache`. * * To remove a cached bitmap, set this property to `null`. * * @name PIXI.DisplayObject#cacheAsBitmap * @property {boolean} cacheAsBitmap - Cache this DisplayObject as a Bitmap. Set to `null` to remove an existing cached bitmap. */ 'cacheAsBitmap': { get: function () { return this._cacheAsBitmap; }, set: function (value) { if (this._cacheAsBitmap === value) { return; } if (value) { this._generateCachedSprite(); } else { this._destroyCachedSprite(); } this._cacheAsBitmap = value; } } }); /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * A DisplayObjectContainer represents a collection of display objects. * It is the base class of all display objects that act as a container for other objects. * * @class PIXI.DisplayObjectContainer * @extends PIXI.DisplayObject * @constructor */ PIXI.DisplayObjectContainer = function () { PIXI.DisplayObject.call(this); /** * [read-only] The array of children of this container. * * @property children * @type Array(DisplayObject) * @readOnly */ this.children = []; /** * If `ignoreChildInput` is `false` it will allow this objects _children_ to be considered as valid for Input events. * * If this property is `true` then the children will _not_ be considered as valid for Input events. * * Note that this property isn't recursive: only immediate children are influenced, it doesn't scan further down. * @property {boolean} ignoreChildInput * @default */ this.ignoreChildInput = false; }; PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); PIXI.DisplayObjectContainer.prototype.constructor = PIXI.DisplayObjectContainer; /** * Adds a child to the container. * * @method PIXI.DisplayObjectContainer#addChild * @param child {DisplayObject} The DisplayObject to add to the container * @return {DisplayObject} The child that was added. */ PIXI.DisplayObjectContainer.prototype.addChild = function (child) { return this.addChildAt(child, this.children.length); }; /** * Adds a child to the container at a specified index. If the index is out of bounds an error will be thrown * * @method PIXI.DisplayObjectContainer#addChildAt * @param child {DisplayObject} The child to add * @param index {Number} The index to place the child in * @return {DisplayObject} The child that was added. */ PIXI.DisplayObjectContainer.prototype.addChildAt = function (child, index) { if (index >= 0 && index <= this.children.length) { if (child.parent) { child.parent.removeChild(child); } child.parent = this; this.children.splice(index, 0, child); return child; } else { throw new Error(child + 'addChildAt: The index '+ index +' supplied is out of bounds ' + this.children.length); } }; /** * Swaps the position of 2 Display Objects within this container. * * @method PIXI.DisplayObjectContainer#swapChildren * @param child {DisplayObject} * @param child2 {DisplayObject} */ PIXI.DisplayObjectContainer.prototype.swapChildren = function (child, child2) { if (child === child2) { return; } var index1 = this.getChildIndex(child); var index2 = this.getChildIndex(child2); if (index1 < 0 || index2 < 0) { throw new Error('swapChildren: Both the supplied DisplayObjects must be a child of the caller.'); } this.children[index1] = child2; this.children[index2] = child; }; /** * Returns the index position of a child DisplayObject instance * * @method PIXI.DisplayObjectContainer#getChildIndex * @param child {DisplayObject} The DisplayObject instance to identify * @return {Number} The index position of the child display object to identify */ PIXI.DisplayObjectContainer.prototype.getChildIndex = function (child) { var index = this.children.indexOf(child); if (index === -1) { throw new Error('The supplied DisplayObject must be a child of the caller'); } return index; }; /** * Changes the position of an existing child in the display object container * * @method PIXI.DisplayObjectContainer#setChildIndex * @param child {DisplayObject} The child DisplayObject instance for which you want to change the index number * @param index {Number} The resulting index number for the child display object */ PIXI.DisplayObjectContainer.prototype.setChildIndex = function (child, index) { if (index < 0 || index >= this.children.length) { throw new Error('The supplied index is out of bounds'); } var currentIndex = this.getChildIndex(child); this.children.splice(currentIndex, 1); //remove from old position this.children.splice(index, 0, child); //add at new position }; /** * Returns the child at the specified index * * @method PIXI.DisplayObjectContainer#getChildAt * @param index {Number} The index to get the child from * @return {DisplayObject} The child at the given index, if any. */ PIXI.DisplayObjectContainer.prototype.getChildAt = function (index) { if (index < 0 || index >= this.children.length) { throw new Error('getChildAt: Supplied index '+ index +' does not exist in the child list, or the supplied DisplayObject must be a child of the caller'); } return this.children[index]; }; /** * Removes a child from the container. * * @method PIXI.DisplayObjectContainer#removeChild * @param child {DisplayObject} The DisplayObject to remove * @return {DisplayObject} The child that was removed. */ PIXI.DisplayObjectContainer.prototype.removeChild = function (child) { var index = this.children.indexOf(child); if (index === -1) { return; } return this.removeChildAt(index); }; /** * Removes a child from the specified index position. * * @method PIXI.DisplayObjectContainer#removeChildAt * @param index {Number} The index to get the child from * @return {DisplayObject} The child that was removed. */ PIXI.DisplayObjectContainer.prototype.removeChildAt = function (index) { var child = this.getChildAt(index); if (child) { child.parent = undefined; this.children.splice(index, 1); } return child; }; /** * Removes all children from this container that are within the begin and end indexes. * * @method PIXI.DisplayObjectContainer#removeChildren * @param beginIndex {Number} The beginning position. Default value is 0. * @param endIndex {Number} The ending position. Default value is size of the container. */ PIXI.DisplayObjectContainer.prototype.removeChildren = function (beginIndex, endIndex) { if (beginIndex === undefined) { beginIndex = 0; } if (endIndex === undefined) { endIndex = this.children.length; } var range = endIndex - beginIndex; if (range > 0 && range <= endIndex) { var removed = this.children.splice(beginIndex, range); for (var i = 0; i < removed.length; i++) { var child = removed[i]; child.parent = undefined; } return removed; } else if (range === 0 && this.children.length === 0) { return []; } else { throw new Error( 'removeChildren: Range Error, numeric values are outside the acceptable range' ); } }; /* * Updates the transform on all children of this container for rendering * * @method PIXI.DisplayObjectContainer#updateTransform * @private */ PIXI.DisplayObjectContainer.prototype.updateTransform = function () { if (!this.visible) { return; } this.displayObjectUpdateTransform(); if (this._cacheAsBitmap) { return; } for (var i = 0; i < this.children.length; i++) { this.children[i].updateTransform(); } }; // performance increase to avoid using call.. (10x faster) PIXI.DisplayObjectContainer.prototype.displayObjectContainerUpdateTransform = PIXI.DisplayObjectContainer.prototype.updateTransform; /** * Retrieves the global bounds of the displayObjectContainer as a rectangle. The bounds calculation takes all visible children into consideration. * * @method PIXI.DisplayObjectContainer#getBounds * @param {PIXI.DisplayObject|Phaser.Matrix} [targetCoordinateSpace] Returns a rectangle that defines the area of the display object relative to the coordinate system of the targetCoordinateSpace object. * @return {Rectangle} The rectangular bounding area */ PIXI.DisplayObjectContainer.prototype.getBounds = function (targetCoordinateSpace) { var isTargetCoordinateSpaceDisplayObject = (targetCoordinateSpace && targetCoordinateSpace instanceof PIXI.DisplayObject); var isTargetCoordinateSpaceThisOrParent = true; if (!isTargetCoordinateSpaceDisplayObject) { targetCoordinateSpace = this; } else if (targetCoordinateSpace instanceof PIXI.DisplayObjectContainer) { isTargetCoordinateSpaceThisOrParent = targetCoordinateSpace.contains(this); } else { isTargetCoordinateSpaceThisOrParent = false; } var i; if (isTargetCoordinateSpaceDisplayObject) { var matrixCache = targetCoordinateSpace.worldTransform; targetCoordinateSpace.worldTransform = Phaser.identityMatrix; for (i = 0; i < targetCoordinateSpace.children.length; i++) { targetCoordinateSpace.children[i].updateTransform(); } } var minX = Infinity; var minY = Infinity; var maxX = -Infinity; var maxY = -Infinity; var childBounds; var childMaxX; var childMaxY; var childVisible = false; for (i = 0; i < this.children.length; i++) { var child = this.children[i]; if (!child.visible) { continue; } childVisible = true; childBounds = this.children[i].getBounds(); minX = (minX < childBounds.x) ? minX : childBounds.x; minY = (minY < childBounds.y) ? minY : childBounds.y; childMaxX = childBounds.width + childBounds.x; childMaxY = childBounds.height + childBounds.y; maxX = (maxX > childMaxX) ? maxX : childMaxX; maxY = (maxY > childMaxY) ? maxY : childMaxY; } var bounds = this._bounds; if (!childVisible) { bounds = new PIXI.Rectangle(); var w0 = bounds.x; var w1 = bounds.width + bounds.x; var h0 = bounds.y; var h1 = bounds.height + bounds.y; var worldTransform = this.worldTransform; var a = worldTransform.a; var b = worldTransform.b; var c = worldTransform.c; var d = worldTransform.d; var tx = worldTransform.tx; var ty = worldTransform.ty; var x1 = a * w1 + c * h1 + tx; var y1 = d * h1 + b * w1 + ty; var x2 = a * w0 + c * h1 + tx; var y2 = d * h1 + b * w0 + ty; var x3 = a * w0 + c * h0 + tx; var y3 = d * h0 + b * w0 + ty; var x4 = a * w1 + c * h0 + tx; var y4 = d * h0 + b * w1 + ty; maxX = x1; maxY = y1; minX = x1; minY = y1; minX = x2 < minX ? x2 : minX; minX = x3 < minX ? x3 : minX; minX = x4 < minX ? x4 : minX; minY = y2 < minY ? y2 : minY; minY = y3 < minY ? y3 : minY; minY = y4 < minY ? y4 : minY; maxX = x2 > maxX ? x2 : maxX; maxX = x3 > maxX ? x3 : maxX; maxX = x4 > maxX ? x4 : maxX; maxY = y2 > maxY ? y2 : maxY; maxY = y3 > maxY ? y3 : maxY; maxY = y4 > maxY ? y4 : maxY; } bounds.x = minX; bounds.y = minY; bounds.width = maxX - minX; bounds.height = maxY - minY; if (isTargetCoordinateSpaceDisplayObject) { targetCoordinateSpace.worldTransform = matrixCache; for (i = 0; i < targetCoordinateSpace.children.length; i++) { targetCoordinateSpace.children[i].updateTransform(); } } if (!isTargetCoordinateSpaceThisOrParent) { var targetCoordinateSpaceBounds = targetCoordinateSpace.getBounds(); bounds.x -= targetCoordinateSpaceBounds.x; bounds.y -= targetCoordinateSpaceBounds.y; } return bounds; }; /** * Retrieves the non-global local bounds of the displayObjectContainer as a rectangle without any transformations. The calculation takes all visible children into consideration. * * @method PIXI.DisplayObjectContainer#getLocalBounds * @return {Rectangle} The rectangular bounding area */ PIXI.DisplayObjectContainer.prototype.getLocalBounds = function () { return this.getBounds(this); }; /** * Determines whether the specified display object is a child of the DisplayObjectContainer instance or the instance itself. * * @method PIXI.DisplayObjectContainer#contains * @param {DisplayObject} child * @returns {boolean} */ PIXI.DisplayObjectContainer.prototype.contains = function (child) { if (!child) { return false; } else if (child === this) { return true; } else { return this.contains(child.parent); } }; /** * Renders the object using the WebGL renderer * * @method PIXI.DisplayObjectContainer#_renderWebGL * @param renderSession {RenderSession} * @private */ PIXI.DisplayObjectContainer.prototype._renderWebGL = function (renderSession) { if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { this._renderCachedSprite(renderSession); return; } var i; if (this._mask || this._filters) { // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { renderSession.spriteBatch.flush(); renderSession.filterManager.pushFilter(this._filterBlock); } if (this._mask) { renderSession.spriteBatch.stop(); renderSession.maskManager.pushMask(this.mask, renderSession); renderSession.spriteBatch.start(); } // simple render children! for (i = 0; i < this.children.length; i++) { this.children[i]._renderWebGL(renderSession); } renderSession.spriteBatch.stop(); if (this._mask) renderSession.maskManager.popMask(this._mask, renderSession); if (this._filters) renderSession.filterManager.popFilter(); renderSession.spriteBatch.start(); } else { // simple render children! for (i = 0; i < this.children.length; i++) { this.children[i]._renderWebGL(renderSession); } } }; /** * Renders the object using the Canvas renderer * * @method PIXI.DisplayObjectContainer#_renderCanvas * @param renderSession {RenderSession} * @private */ PIXI.DisplayObjectContainer.prototype._renderCanvas = function (renderSession) { if (this.visible === false || this.alpha === 0) { return; } if (this._cacheAsBitmap) { this._renderCachedSprite(renderSession); return; } if (this._mask) { renderSession.maskManager.pushMask(this._mask, renderSession); } for (var i = 0; i < this.children.length; i++) { this.children[i]._renderCanvas(renderSession); } if (this._mask) { renderSession.maskManager.popMask(renderSession); } }; /** * The width of the displayObjectContainer, setting this will actually modify the scale to achieve the value set * * @property width * @type Number */ Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'width', { get: function() { return this.getLocalBounds().width * this.scale.x; }, set: function(value) { var width = this.getLocalBounds().width; if (width !== 0) { this.scale.x = value / width; } else { this.scale.x = 1; } this._width = value; } }); /** * The height of the displayObjectContainer, setting this will actually modify the scale to achieve the value set * * @property height * @type Number */ Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'height', { get: function() { return this.getLocalBounds().height * this.scale.y; }, set: function(value) { var height = this.getLocalBounds().height; if (height !== 0) { this.scale.y = value / height; } else { this.scale.y = 1; } this._height = value; } }); /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * The Sprite object is the base for all textured objects that are rendered to the screen * * @class PIXI.Sprite * @extends PIXI.DisplayObjectContainer * @constructor * @param texture {Texture} The texture for this sprite */ PIXI.Sprite = function (texture) { PIXI.DisplayObjectContainer.call(this); /** * The anchor sets the origin point of the texture. * The default (0, 0) is the top left. * (0.5, 0.5) is the center. * (1, 1) is the bottom right. * * You can modify the default values in PIXI.Sprite.defaultAnchor. * * @property anchor * @type Point */ this.anchor = new PIXI.Point(PIXI.Sprite.defaultAnchor.x, PIXI.Sprite.defaultAnchor.y); /** * The texture that the sprite is using * * @property texture * @type Texture */ this.texture = texture || PIXI.Texture.emptyTexture; /** * The width of the sprite (this is initially set by the texture) * * @property _width * @type Number * @private */ this._width = 0; /** * The height of the sprite (this is initially set by the texture) * * @property _height * @type Number * @private */ this._height = 0; /** * The tint applied to the sprite. This is a hex value. A value of 0xFFFFFF will remove any tint effect. * * @property tint * @type Number * @default 0xFFFFFF */ this.tint = 0xFFFFFF; /** * The tint applied to the sprite. This is a hex value. A value of 0xFFFFFF will remove any tint effect. * * @property cachedTint * @private * @type Number * @default -1 */ this.cachedTint = -1; /** * A canvas that contains the tinted version of the Sprite (in Canvas mode, WebGL doesn't populate this) * * @property tintedTexture * @type Canvas * @default null */ this.tintedTexture = null; /** * The blend mode to be applied to the sprite. Set to PIXI.blendModes.NORMAL to remove any blend mode. * * Warning: You cannot have a blend mode and a filter active on the same Sprite. Doing so will render the sprite invisible. * * @property blendMode * @type Number * @default PIXI.blendModes.NORMAL; */ this.blendMode = PIXI.blendModes.NORMAL; /** * The shader that will be used to render this Sprite. * Set to null to remove a current shader. * * @property shader * @type Phaser.Filter * @default null */ this.shader = null; /** * Controls if this Sprite is processed by the core Phaser game loops and Group loops (except {@link Phaser.Group#update}). * * @property exists * @type Boolean * @default true */ this.exists = true; if (this.texture.baseTexture.hasLoaded) { this.onTextureUpdate(); } this.renderable = true; }; /** * @property PIXI.Sprite.defaultAnchor - A Point-like object. * @type {{x: number, y: number}} * @default */ PIXI.Sprite.defaultAnchor = {x: 0, y: 0}; // constructor PIXI.Sprite.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); PIXI.Sprite.prototype.constructor = PIXI.Sprite; /** * The width of the sprite, setting this will actually modify the scale to achieve the value set * * @property width * @type Number */ Object.defineProperty(PIXI.Sprite.prototype, 'width', { get: function() { return this.scale.x * this.texture.frame.width; }, set: function(value) { this.scale.x = value / this.texture.frame.width; this._width = value; } }); /** * The height of the sprite, setting this will actually modify the scale to achieve the value set * * @property height * @type Number */ Object.defineProperty(PIXI.Sprite.prototype, 'height', { get: function() { return this.scale.y * this.texture.frame.height; }, set: function(value) { this.scale.y = value / this.texture.frame.height; this._height = value; } }); /** * Sets the texture of the sprite. Be warned that this doesn't remove or destroy the previous * texture this Sprite was using. * * @method PIXI.Sprite#setTexture * @param texture {Texture} The PIXI texture that is displayed by the sprite * @param [destroy=false] {boolean} Call Texture.destroy on the current texture before replacing it with the new one? */ PIXI.Sprite.prototype.setTexture = function(texture, destroyBase) { if (destroyBase) { this.texture.baseTexture.destroy(); } // Over-ridden by loadTexture as needed this.texture.baseTexture.skipRender = false; this.texture = texture; this.texture.valid = true; this.cachedTint = -1; }; /** * When the texture is updated, this event will fire to update the scale and frame * * @method PIXI.Sprite#onTextureUpdate * @param event * @private */ PIXI.Sprite.prototype.onTextureUpdate = function() { // so if _width is 0 then width was not set.. if (this._width) this.scale.x = this._width / this.texture.frame.width; if (this._height) this.scale.y = this._height / this.texture.frame.height; }; /** * Returns the bounds of the Sprite as a rectangle. * The bounds calculation takes the worldTransform into account. * * It is important to note that the transform is not updated when you call this method. * So if this Sprite is the child of a Display Object which has had its transform * updated since the last render pass, those changes will not yet have been applied * to this Sprites worldTransform. If you need to ensure that all parent transforms * are factored into this getBounds operation then you should call `updateTransform` * on the root most object in this Sprites display list first. * * @method PIXI.Sprite#getBounds * @param matrix {Matrix} the transformation matrix of the sprite * @return {Rectangle} the framing rectangle */ PIXI.Sprite.prototype.getBounds = function(matrix) { var width = this.text