UNPKG

phaser

Version:

A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers.

1,825 lines (1,549 loc) 2.57 MB
/** * @author Richard Davey <rich@photonstorm.com> * @copyright 2014 Photon Storm Ltd. * @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} * * @overview * * Phaser - http://phaser.io * * v2.1.2 "Whitebridge" - Built: Thu Oct 09 2014 16:16:06 * * 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; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * @module PIXI */ var PIXI = PIXI || {}; /* * * This file contains a lot of pixi consts which are used across the rendering engine * @class Consts */ PIXI.WEBGL_RENDERER = 0; PIXI.CANVAS_RENDERER = 1; // useful for testing against if your lib is using pixi. PIXI.VERSION = "v1.6.1"; // the various blend modes supported by pixi PIXI.blendModes = { NORMAL:0, ADD:1, MULTIPLY:2, SCREEN:3, OVERLAY:4, DARKEN:5, LIGHTEN:6, COLOR_DODGE:7, COLOR_BURN:8, HARD_LIGHT:9, SOFT_LIGHT:10, DIFFERENCE:11, EXCLUSION:12, HUE:13, SATURATION:14, COLOR:15, LUMINOSITY:16 }; // the scale modes PIXI.scaleModes = { DEFAULT:0, LINEAR:0, NEAREST:1 }; // used to create uids for various pixi objects.. PIXI._UID = 0; if(typeof(Float32Array) != 'undefined') { PIXI.Float32Array = Float32Array; PIXI.Uint16Array = Uint16Array; } else { PIXI.Float32Array = Array; PIXI.Uint16Array = Array; } // interaction frequency PIXI.INTERACTION_FREQUENCY = 30; PIXI.AUTO_PREVENT_DEFAULT = true; PIXI.RAD_TO_DEG = 180 / Math.PI; PIXI.DEG_TO_RAD = Math.PI / 180; PIXI.dontSayHello = false; PIXI.sayHello = function (type) { if(PIXI.dontSayHello)return; if ( navigator.userAgent.toLowerCase().indexOf('chrome') > -1 ) { var args = [ '%c %c %c Pixi.js ' + PIXI.VERSION + ' - ' + type + ' %c ' + ' %c ' + ' http://www.pixijs.com/ %c %c ♥%c♥%c♥ ', 'background: #ff66a5', 'background: #ff66a5', 'color: #ff66a5; background: #030307;', 'background: #ff66a5', 'background: #ffc3dc', 'background: #ff66a5', 'color: #ff2424; background: #fff', 'color: #ff2424; background: #fff', 'color: #ff2424; background: #fff' ]; console.log.apply(console, args); } else if (window['console']) { console.log('Pixi.js ' + PIXI.VERSION + ' - http://www.pixijs.com/'); } PIXI.dontSayHello = true; }; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * The Matrix class is now an object, which makes it a lot faster, * here is a representation of it : * | a | b | tx| * | c | d | ty| * | 0 | 0 | 1 | * * @class Matrix * @constructor */ PIXI.Matrix = function() { this.a = 1; this.b = 0; this.c = 0; this.d = 1; this.tx = 0; this.ty = 0; }; /** * Creates a pixi matrix object based on the array given as a parameter * * @method fromArray * @param array {Array} The array that the matrix will be filled with */ PIXI.Matrix.prototype.fromArray = function(array) { this.a = array[0]; this.b = array[1]; this.c = array[3]; this.d = array[4]; this.tx = array[2]; this.ty = array[5]; }; /** * Creates an array from the current Matrix object * * @method toArray * @param transpose {Boolean} Whether we need to transpose the matrix or not * @return {Array} the newly created array which contains the matrix */ PIXI.Matrix.prototype.toArray = function(transpose) { if(!this.array) this.array = new Float32Array(9); var array = this.array; if(transpose) { array[0] = this.a; array[1] = this.c; array[2] = 0; array[3] = this.b; array[4] = this.d; array[5] = 0; array[6] = this.tx; array[7] = this.ty; array[8] = 1; } else { array[0] = this.a; array[1] = this.b; array[2] = this.tx; array[3] = this.c; array[4] = this.d; array[5] = this.ty; array[6] = 0; array[7] = 0; array[8] = 1; } return array; }; /** * Get a new position with the current transormation applied. * Can be used to go from a child's coordinate space to the world coordinate space. (e.g. rendering) * * @method apply * @param pos {Point} The origin * @param [newPos] {Point} The point that the new position is assigned to (allowed to be same as input) * @return {Point} The new point, transformed trough this matrix */ PIXI.Matrix.prototype.apply = function(pos, newPos) { newPos = newPos || new PIXI.Point(); newPos.x = this.a * pos.x + this.b * pos.y + this.tx; newPos.y = this.c * pos.x + this.d * pos.y + this.ty; return newPos; }; /** * Get a new position with the inverse of the current transormation applied. * Can be used to go from the world coordinate space to a child's coordinate space. (e.g. input) * * @method apply * @param pos {Point} The origin * @param [newPos] {Point} The point that the new position is assigned to (allowed to be same as input) * @return {Point} The new point, inverse-transformed trough this matrix */ PIXI.Matrix.prototype.applyInverse = function(pos, newPos) { newPos = newPos || new PIXI.Point(); var id = 1 / (this.a * this.d + this.b * -this.c); newPos.x = this.d * id * pos.x - this.b * id * pos.y + (this.ty * this.b - this.tx * this.d) * id; newPos.y = this.a * id * pos.y - this.c * id * pos.x + (this.tx * this.c - this.ty * this.a) * id; return newPos; }; PIXI.identityMatrix = new PIXI.Matrix(); PIXI.determineMatrixArrayType = function() { return (typeof Float32Array !== 'undefined') ? Float32Array : Array; }; /** * The Matrix2 class will choose the best type of array to use between * a regular javascript Array and a Float32Array if the latter is available * * @class Matrix2 * @constructor */ PIXI.Matrix2 = PIXI.determineMatrixArrayType(); /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * The base class for all objects that are rendered on the screen. * This is an abstract class and should not be used on its own rather it should be extended. * * @class DisplayObject * @constructor */ PIXI.DisplayObject = function() { /** * The coordinate of the object relative to the local coordinates of the parent. * * @property position * @type Point */ this.position = new PIXI.Point(); /** * The scale factor of the object. * * @property scale * @type Point */ this.scale = new PIXI.Point(1,1);//{x:1, y:1}; /** * The pivot point of the displayObject that it rotates around * * @property pivot * @type Point */ this.pivot = new PIXI.Point(0,0); /** * The rotation of the object in radians. * * @property rotation * @type Number */ this.rotation = 0; /** * The opacity of the object. * * @property alpha * @type Number */ this.alpha = 1; /** * The visibility of the object. * * @property visible * @type Boolean */ 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; /** * This is used to indicate if the displayObject should display a mouse hand cursor on rollover * * @property buttonMode * @type Boolean */ this.buttonMode = false; /** * Can this object be rendered * * @property renderable * @type Boolean */ this.renderable = false; /** * [read-only] The display object container that contains this display object. * * @property parent * @type DisplayObjectContainer * @readOnly */ this.parent = null; /** * [read-only] The stage the display object is connected to, or undefined if it is not connected to the stage. * * @property stage * @type Stage * @readOnly */ this.stage = null; /** * [read-only] The multiplied alpha of the displayObject * * @property worldAlpha * @type Number * @readOnly */ this.worldAlpha = 1; /** * [read-only] Whether or not the object is interactive, do not toggle directly! use the `interactive` property * * @property _interactive * @type Boolean * @readOnly * @private */ this._interactive = false; /** * This is the cursor that will be used when the mouse is over this object. To enable this the element must have interaction = true and buttonMode = true * * @property defaultCursor * @type String * */ this.defaultCursor = 'pointer'; /** * [read-only] Current transform of the object based on world (parent) factors * * @property worldTransform * @type Mat3 * @readOnly * @private */ this.worldTransform = new PIXI.Matrix(); /** * [NYI] Unknown * * @property color * @type Array<> * @private */ this.color = []; /** * [NYI] Holds whether or not this object is dynamic, for rendering optimization * * @property dynamic * @type Boolean * @private */ this.dynamic = true; // cached sin rotation and cos rotation this._sr = 0; this._cr = 1; /** * The area the filter is applied to like the hitArea this is used as more of an optimisation * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle * * @property filterArea * @type Rectangle */ this.filterArea = null;//new PIXI.Rectangle(0,0,1,1); /** * The original, cached bounds of the object * * @property _bounds * @type Rectangle * @private */ this._bounds = new PIXI.Rectangle(0, 0, 1, 1); /** * The most up-to-date bounds of the object * * @property _currentBounds * @type Rectangle * @private */ this._currentBounds = null; /** * The original, cached mask of the object * * @property _currentBounds * @type Rectangle * @private */ this._mask = null; this._cacheAsBitmap = false; this._cacheIsDirty = false; /* * MOUSE Callbacks */ /** * A callback that is used when the users mouse rolls over the displayObject * @method mouseover * @param interactionData {InteractionData} */ /** * A callback that is used when the users mouse leaves the displayObject * @method mouseout * @param interactionData {InteractionData} */ //Left button /** * A callback that is used when the users clicks on the displayObject with their mouse's left button * @method click * @param interactionData {InteractionData} */ /** * A callback that is used when the user clicks the mouse's left button down over the sprite * @method mousedown * @param interactionData {InteractionData} */ /** * A callback that is used when the user releases the mouse's left button that was over the displayObject * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject * @method mouseup * @param interactionData {InteractionData} */ /** * A callback that is used when the user releases the mouse's left button that was over the displayObject but is no longer over the displayObject * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject * @method mouseupoutside * @param interactionData {InteractionData} */ //Right button /** * A callback that is used when the users clicks on the displayObject with their mouse's right button * @method rightclick * @param interactionData {InteractionData} */ /** * A callback that is used when the user clicks the mouse's right button down over the sprite * @method rightdown * @param interactionData {InteractionData} */ /** * A callback that is used when the user releases the mouse's right button that was over the displayObject * for this callback to be fired the mouse's right button must have been pressed down over the displayObject * @method rightup * @param interactionData {InteractionData} */ /** * A callback that is used when the user releases the mouse's right button that was over the displayObject but is no longer over the displayObject * for this callback to be fired, the mouse's right button must have been pressed down over the displayObject * @method rightupoutside * @param interactionData {InteractionData} */ /* * TOUCH Callbacks */ /** * A callback that is used when the users taps on the sprite with their finger * basically a touch version of click * @method tap * @param interactionData {InteractionData} */ /** * A callback that is used when the user touches over the displayObject * @method touchstart * @param interactionData {InteractionData} */ /** * A callback that is used when the user releases a touch over the displayObject * @method touchend * @param interactionData {InteractionData} */ /** * A callback that is used when the user releases the touch that was over the displayObject * for this callback to be fired, The touch must have started over the sprite * @method touchendoutside * @param interactionData {InteractionData} */ }; // constructor PIXI.DisplayObject.prototype.constructor = PIXI.DisplayObject; /** * [Deprecated] Indicates if the sprite will have touch and mouse interactivity. It is false by default * Instead of using this function you can now simply set the interactive property to true or false * * @method setInteractive * @param interactive {Boolean} * @deprecated Simply set the `interactive` property directly */ PIXI.DisplayObject.prototype.setInteractive = function(interactive) { this.interactive = interactive; }; /** * Indicates if the sprite will have touch and mouse interactivity. It is false by default * * @property interactive * @type Boolean * @default false */ Object.defineProperty(PIXI.DisplayObject.prototype, 'interactive', { get: function() { return this._interactive; }, set: function(value) { this._interactive = value; // TODO more to be done here.. // need to sort out a re-crawl! if(this.stage)this.stage.dirty = true; } }); /** * [read-only] Indicates if the sprite is globaly visible. * * @property worldVisible * @type Boolean */ Object.defineProperty(PIXI.DisplayObject.prototype, 'worldVisible', { get: function() { var item = this; do { if(!item.visible)return false; item = item.parent; } while(item); return true; } }); /** * Sets a mask for the displayObject. A mask is an object that limits the visibility of an object to the shape of the mask applied to it. * In PIXI a regular mask must be a PIXI.Graphics object. This allows for much faster masking in canvas as it utilises shape clipping. * To remove a mask, set this property to null. * * @property mask * @type Graphics */ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { get: function() { return this._mask; }, set: function(value) { if(this._mask)this._mask.isMask = false; this._mask = value; if(this._mask)this._mask.isMask = true; } }); /** * Sets the filters for the displayObject. * * IMPORTANT: This is a webGL only feature and will be ignored by the canvas renderer. * To remove filters simply set this property to 'null' * @property filters * @type Array An array of filters */ Object.defineProperty(PIXI.DisplayObject.prototype, 'filters', { get: function() { return this._filters; }, set: function(value) { if(value) { // now 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]); } } // TODO change this as it is legacy this._filterBlock = {target:this, filterPasses:passes}; } this._filters = value; } }); /** * Set weather or not a the display objects is cached as a bitmap. * This basically takes a snap shot of the display object as it is at that moment. It can provide a performance benefit for complex static displayObjects * To remove filters simply set this property to 'null' * @property cacheAsBitmap * @type Boolean */ Object.defineProperty(PIXI.DisplayObject.prototype, 'cacheAsBitmap', { get: function() { return this._cacheAsBitmap; }, set: function(value) { if(this._cacheAsBitmap === value)return; if(value) { //this._cacheIsDirty = true; this._generateCachedSprite(); } else { this._destroyCachedSprite(); } this._cacheAsBitmap = value; } }); /* * Updates the object transform for rendering * * @method updateTransform * @private */ PIXI.DisplayObject.prototype.updateTransform = function() { // TODO OPTIMIZE THIS!! with dirty if(this.rotation !== this.rotationCache) { this.rotationCache = this.rotation; this._sr = Math.sin(this.rotation); this._cr = Math.cos(this.rotation); } // var localTransform = this.localTransform//.toArray(); var parentTransform = this.parent.worldTransform;//.toArray(); var worldTransform = this.worldTransform;//.toArray(); var px = this.pivot.x; var py = this.pivot.y; var a00 = this._cr * this.scale.x, a01 = -this._sr * this.scale.y, a10 = this._sr * this.scale.x, a11 = this._cr * this.scale.y, a02 = this.position.x - a00 * px - py * a01, a12 = this.position.y - a11 * py - px * a10, b00 = parentTransform.a, b01 = parentTransform.b, b10 = parentTransform.c, b11 = parentTransform.d; worldTransform.a = b00 * a00 + b01 * a10; worldTransform.b = b00 * a01 + b01 * a11; worldTransform.tx = b00 * a02 + b01 * a12 + parentTransform.tx; worldTransform.c = b10 * a00 + b11 * a10; worldTransform.d = b10 * a01 + b11 * a11; worldTransform.ty = b10 * a02 + b11 * a12 + parentTransform.ty; this.worldAlpha = this.alpha * this.parent.worldAlpha; }; /** * Retrieves the bounds of the displayObject as a rectangle object * * @method getBounds * @return {Rectangle} the rectangular bounding area */ PIXI.DisplayObject.prototype.getBounds = function( matrix ) { matrix = matrix;//just to get passed js hinting (and preserve inheritance) return PIXI.EmptyRectangle; }; /** * Retrieves the local bounds of the displayObject as a rectangle object * * @method getLocalBounds * @return {Rectangle} the rectangular bounding area */ PIXI.DisplayObject.prototype.getLocalBounds = function() { return this.getBounds(PIXI.identityMatrix);///PIXI.EmptyRectangle(); }; /** * Sets the object's stage reference, the stage this object is connected to * * @method setStageReference * @param stage {Stage} the stage that the object will have as its current stage reference */ PIXI.DisplayObject.prototype.setStageReference = function(stage) { this.stage = stage; if(this._interactive)this.stage.dirty = true; }; PIXI.DisplayObject.prototype.generateTexture = function(renderer) { var bounds = this.getLocalBounds(); var renderTexture = new PIXI.RenderTexture(bounds.width | 0, bounds.height | 0, renderer); renderTexture.render(this, new PIXI.Point(-bounds.x, -bounds.y) ); return renderTexture; }; PIXI.DisplayObject.prototype.updateCache = function() { this._generateCachedSprite(); }; /** * Calculates the global position of the display object * * @method toGlobal * @param position {Point} The world origin to calculate from * @return {Point} A point object representing the position of this object */ PIXI.DisplayObject.prototype.toGlobal = function(pos) { this.updateTransform(); return this.worldTransform.apply(pos); }; /** * Calculates the local position of the display object relative to another point * * @method toGlobal * @param position {Point} The world origin to calculate from * @param [from] {DisplayObject} The DisplayObject to calculate the global position from * @return {Point} A point object representing the position of this object */ PIXI.DisplayObject.prototype.toLocal = function(pos, from) { if (from) { pos = from.toGlobal(pos); } this.updateTransform(); return this.worldTransform.applyInverse(pos); }; PIXI.DisplayObject.prototype._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); } }; PIXI.DisplayObject.prototype._generateCachedSprite = function()//renderSession) { this._cacheAsBitmap = false; var bounds = this.getLocalBounds(); if(!this._cachedSprite) { var renderTexture = new PIXI.RenderTexture(bounds.width | 0, bounds.height | 0);//, renderSession.renderer); this._cachedSprite = new PIXI.Sprite(renderTexture); this._cachedSprite.worldTransform = this.worldTransform; } else { this._cachedSprite.texture.resize(bounds.width | 0, bounds.height | 0); } //REMOVE filter! var tempFilters = this._filters; this._filters = null; this._cachedSprite.filters = tempFilters; this._cachedSprite.texture.render(this, new PIXI.Point(-bounds.x, -bounds.y) ); this._cachedSprite.anchor.x = -( bounds.x / bounds.width ); this._cachedSprite.anchor.y = -( bounds.y / bounds.height ); this._filters = tempFilters; this._cacheAsBitmap = true; }; /** * Renders the object using the WebGL renderer * * @method _renderWebGL * @param renderSession {RenderSession} * @private */ PIXI.DisplayObject.prototype._destroyCachedSprite = function() { if(!this._cachedSprite)return; this._cachedSprite.texture.destroy(true); // console.log("DESTROY") // let the gc collect the unused sprite // TODO could be object pooled! this._cachedSprite = null; }; PIXI.DisplayObject.prototype._renderWebGL = function(renderSession) { // OVERWRITE; // this line is just here to pass jshinting :) renderSession = renderSession; }; /** * Renders the object using the Canvas renderer * * @method _renderCanvas * @param renderSession {RenderSession} * @private */ PIXI.DisplayObject.prototype._renderCanvas = function(renderSession) { // OVERWRITE; // this line is just here to pass jshinting :) renderSession = renderSession; }; /** * The position of the displayObject on the x axis relative to the local coordinates of the parent. * * @property x * @type Number */ Object.defineProperty(PIXI.DisplayObject.prototype, 'x', { get: function() { return this.position.x; }, set: function(value) { this.position.x = value; } }); /** * The position of the displayObject on the y axis relative to the local coordinates of the parent. * * @property y * @type Number */ Object.defineProperty(PIXI.DisplayObject.prototype, 'y', { get: function() { return this.position.y; }, set: function(value) { this.position.y = 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 DisplayObjectContainer * @extends 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 = []; }; // constructor PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); PIXI.DisplayObjectContainer.prototype.constructor = PIXI.DisplayObjectContainer; /** * 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.scale.x * this.getLocalBounds().width; }, set: function(value) { var width = this.getLocalBounds().width; if(width !== 0) { this.scale.x = value / ( width/this.scale.x ); } 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.scale.y * this.getLocalBounds().height; }, set: function(value) { var height = this.getLocalBounds().height; if(height !== 0) { this.scale.y = value / ( height/this.scale.y ); } else { this.scale.y = 1; } this._height = value; } }); /** * Adds a child to the container. * * @method addChild * @param child {DisplayObject} The DisplayObject to add to the container */ 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 addChildAt * @param child {DisplayObject} The child to add * @param index {Number} The index to place the child in */ 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); if(this.stage)child.setStageReference(this.stage); return child; } else { throw new Error(child + ' The index '+ index +' supplied is out of bounds ' + this.children.length); } }; /** * [NYI] Swaps the depth of 2 displayObjects * * @method swapChildren * @param child {DisplayObject} * @param child2 {DisplayObject} * @private */ PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) { if(child === child2) { return; } var index1 = this.children.indexOf(child); var index2 = this.children.indexOf(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 child at the specified index * * @method getChildAt * @param index {Number} The index to get the child from */ PIXI.DisplayObjectContainer.prototype.getChildAt = function(index) { if(index >= 0 && index < this.children.length) { return this.children[index]; } else { throw new Error('Supplied index does not exist in the child list, or the supplied DisplayObject must be a child of the caller'); } }; /** * Removes a child from the container. * * @method removeChild * @param child {DisplayObject} The DisplayObject to remove */ PIXI.DisplayObjectContainer.prototype.removeChild = function(child) { return this.removeChildAt( this.children.indexOf( child ) ); }; /** * Removes a child from the specified index position in the child list of the container. * * @method removeChildAt * @param index {Number} The index to get the child from */ PIXI.DisplayObjectContainer.prototype.removeChildAt = function(index) { var child = this.getChildAt( index ); if(this.stage) child.removeStageReference(); child.parent = undefined; this.children.splice( index, 1 ); return child; }; /** * Removes all child instances from the child list of the container. * * @method removeChildren * @param beginIndex {Number} The beginning position. Predefined value is 0. * @param endIndex {Number} The ending position. Predefined value is children's array length. */ PIXI.DisplayObjectContainer.prototype.removeChildren = function(beginIndex, endIndex) { var begin = beginIndex || 0; var end = typeof endIndex === 'number' ? endIndex : this.children.length; var range = end - begin; if (range > 0 && range <= end) { var removed = this.children.splice(begin, range); for (var i = 0; i < removed.length; i++) { var child = removed[i]; if(this.stage) child.removeStageReference(); child.parent = undefined; } return removed; } else if (range === 0 && this.children.length === 0) { return []; } else { throw new Error( 'Range Error, numeric values are outside the acceptable range' ); } }; /* * Updates the container's childrens transform for rendering * * @method updateTransform * @private */ PIXI.DisplayObjectContainer.prototype.updateTransform = function() { //this._currentBounds = null; if(!this.visible)return; PIXI.DisplayObject.prototype.updateTransform.call( this ); if(this._cacheAsBitmap)return; for(var i=0,j=this.children.length; i<j; i++) { this.children[i].updateTransform(); } }; /** * Retrieves the bounds of the displayObjectContainer as a rectangle object * * @method getBounds * @return {Rectangle} the rectangular bounding area */ PIXI.DisplayObjectContainer.prototype.getBounds = function(matrix) { if(this.children.length === 0)return PIXI.EmptyRectangle; // TODO the bounds have already been calculated this render session so return what we have if(matrix) { var matrixCache = this.worldTransform; this.worldTransform = matrix; this.updateTransform(); this.worldTransform = matrixCache; } var minX = Infinity; var minY = Infinity; var maxX = -Infinity; var maxY = -Infinity; var childBounds; var childMaxX; var childMaxY; var childVisible = false; for(var i=0,j=this.children.length; i<j; i++) { var child = this.children[i]; if(!child.visible)continue; childVisible = true; childBounds = this.children[i].getBounds( matrix ); 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; } if(!childVisible) return PIXI.EmptyRectangle; var bounds = this._bounds; bounds.x = minX; bounds.y = minY; bounds.width = maxX - minX; bounds.height = maxY - minY; // TODO: store a reference so that if this function gets called again in the render cycle we do not have to recalculate //this._currentBounds = bounds; return bounds; }; PIXI.DisplayObjectContainer.prototype.getLocalBounds = function() { var matrixCache = this.worldTransform; this.worldTransform = PIXI.identityMatrix; for(var i=0,j=this.children.length; i<j; i++) { this.children[i].updateTransform(); } var bounds = this.getBounds(); this.worldTransform = matrixCache; return bounds; }; /** * Sets the container's stage reference, the stage this object is connected to * * @method setStageReference * @param stage {Stage} the stage that the container will have as its current stage reference */ PIXI.DisplayObjectContainer.prototype.setStageReference = function(stage) { this.stage = stage; if(this._interactive)this.stage.dirty = true; for(var i=0,j=this.children.length; i<j; i++) { var child = this.children[i]; child.setStageReference(stage); } }; /** * removes the current stage reference of the container * * @method removeStageReference */ PIXI.DisplayObjectContainer.prototype.removeStageReference = function() { for(var i=0,j=this.children.length; i<j; i++) { var child = this.children[i]; child.removeStageReference(); } if(this._interactive)this.stage.dirty = true; this.stage = null; }; /** * Renders the object using the WebGL renderer * * @method _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,j; 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,j=this.children.length; i<j; 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,j=this.children.length; i<j; i++) { this.children[i]._renderWebGL(renderSession); } } }; /** * Renders the object using the Canvas renderer * * @method _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.context); } for(var i=0,j=this.children.length; i<j; i++) { var child = this.children[i]; child._renderCanvas(renderSession); } if(this._mask) { renderSession.maskManager.popMask(renderSession.context); } }; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * The Sprite object is the base for all textured objects that are rendered to the screen * * @class Sprite * @extends DisplayObjectContainer * @constructor * @param texture {Texture} The texture for this sprite * * A sprite can be created directly from an image like this : * var sprite = new PIXI.Sprite.fromImage('assets/image.png'); * yourStage.addChild(sprite); * then obviously don't forget to add it to the stage you have already created */ PIXI.Sprite = function(texture) { PIXI.DisplayObjectContainer.call( this ); /** * The anchor sets the origin point of the texture. * The default is 0,0 this means the texture's origin is the top left * Setting than anchor to 0.5,0.5 means the textures origin is centred * Setting the anchor to 1,1 would mean the textures origin points will be the bottom right corner * * @property anchor * @type Point */ this.anchor = new PIXI.Point(); /** * The texture that the sprite is using * * @property texture * @type Texture */ this.texture = texture; /** * 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 * * @property tint * @type Number * @default 0xFFFFFF */ this.tint = 0xFFFFFF;// * Math.random(); /** * The blend mode to be applied to the sprite * * @property blendMode * @type Number * @default PIXI.blendModes.NORMAL; */ this.blendMode = PIXI.blendModes.NORMAL; if(texture.baseTexture.hasLoaded) { this.onTextureUpdate(); } else { this.onTextureUpdateBind = this.onTextureUpdate.bind(this); this.texture.addEventListener( 'update', this.onTextureUpdateBind ); } this.renderable = true; }; // 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 * * @method setTexture * @param texture {Texture} The PIXI texture that is displayed by the sprite */ PIXI.Sprite.prototype.setTexture = function(texture) { this.texture = texture; this.cachedTint = 0xFFFFFF; }; /** * When the texture is updated, this event will fire to update the scale and frame * * @method 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; //this.updateFrame = true; }; /** * Returns the framing rectangle of the sprite as a PIXI.Rectangle object * * @method getBounds * @param matrix {Matrix} the transformation matrix of the sprite * @return {Rectangle} the framing rectangle */ PIXI.Sprite.prototype.getBounds = function(matrix) { var width = this.texture.frame.width; var height = this.texture.frame.height; var w0 = width * (1-this.anchor.x); var w1 = width * -this.anchor.x; var h0 = height * (1-this.anchor.y); var h1 = height * -this.anchor.y; var worldTransform = matrix || this.worldTransform ; var a = worldTransform.a; var b = worldTransform.c; var c = worldTransform.b; 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; var maxX = -Infinity; var maxY = -Infinity; var minX = Infinity; var minY = Infinity; minX = x1 < minX ? x1 : minX; minX = x2 < minX ? x2 : minX; minX = x3 < minX ? x3 : minX; minX = x4 < minX ? x4 : minX; minY = y1 < minY ? y1 : minY; minY = y2 < minY ? y2 : minY; minY = y3 < minY ? y3 : minY; minY = y4 < minY ? y4 : minY; maxX = x1 > maxX ? x1 : maxX; maxX = x2 > maxX ? x2 : maxX; maxX = x3 > maxX ? x3 : maxX; maxX = x4 > maxX ? x4 : maxX; maxY = y1 > maxY ? y1 : maxY; maxY = y2 > maxY ? y2 : maxY; maxY = y3 > maxY ? y3 : maxY; maxY = y4 > maxY ? y4 : maxY; var bounds = this._bounds; bounds.x = minX; bounds.width = maxX - minX; bounds.y = minY; bounds.height = maxY - minY; // store a reference so that if this function gets called again in the render cycle we do not have to recalculate this._currentBounds = bounds; return bounds; }; /** * Renders the object using the WebGL renderer * * @method _renderWebGL * @param renderSession {RenderSession} * @private */ PIXI.Sprite.prototype._renderWebGL = function(renderSession) { // if the sprite is not visible or the alpha is 0 then no need to render this element if(!this.visible || this.alpha <= 0)return; var i,j; // do a quick check to see if this element has a mask or a filter. if(this._mask || this._filters) { var spriteBatch = renderSession.spriteBatch; // push filter first as we need to ensure the stencil buffer is correct for any masking if(this._filters) { spriteBatch.flush(); renderSession.filterManager.pushFilter(this._filterBlock); } if(this._mask) { spriteBatch.stop(); renderSession.maskManager.pushMask(this.mask, renderSession); spriteBatch.start(); } // add this sprite to the batch spriteBatch.render(this); // now loop through the children and make sure they get rendered for(i=0,j=this.children.length; i<j; i++) { this.children[i]._renderWebGL(renderSession); } // time to stop the sprite batch as either a mask element or a filter draw will happen next spriteBatch.stop(); if(this._mask)renderSession.maskManager.popMask(this._mask, renderSession); if(this._filters)renderSession.filterManager.popFilter(); spriteBatch.start(); } else { renderSession.spriteBatch.render(this); // simple render children! for(i=0,j=this.children.length; i<j; i++) { this.children[i]._renderWebGL(renderSession); } } //TODO check culling }; /** * Renders the object using the Canvas renderer * * @method _renderCanvas * @param renderSession {RenderSession} * @private */ PIXI.Sprite.prototype._renderCanvas = function(renderSession) { // If the sprite is not visible or the alpha is 0 then no need to render this element if (this.visible === false || this.alpha === 0 || this.texture.crop.width <= 0 || this.texture.crop.height <= 0) return; if (this.blendMode !== renderSession.currentBlendMode) { renderSession.currentBlendMode = this.blendMode; renderSession.context.globalCompositeOperation = PIXI.blendModesCanvas[renderSession.currentBlendMode]; } if (this._mask) { renderSession.maskManager.pushMask(this._mask, renderSession.context); } // Ignore null sources if (this.texture.valid) { renderSession.context.globalAlpha = this.worldAlpha; // Allow for pixel rounding if (renderSession.roundPixels) { renderSession.context.setTransform( this.worldTransform.a, this.worldTransform.c, this.worldTransform.b, this.worldTransform.d, this.worldTransform.tx | 0, this.worldTransform.ty | 0); } else { renderSession.context.setTransform( this.worldTransform.a, this.worldTransform.c, this.worldTransform.b, this.worldTransform.d, this.worldTransform.tx, this.worldTransform.ty); } // If smoothingEnabled is supported and we need to change the smoothing property for this texture if (renderSession.smoothProperty && renderSession.scaleMode !== this.texture.baseTexture.scaleMode) { renderSession.scaleMode = this.texture.baseTexture.scaleMode; renderSession.context[renderSession.smoothProperty] = (renderSession.scaleMode === PIXI.scaleModes.LINEAR); } // If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions var dx = (this.texture.trim) ? this.texture.trim.x - this.anchor.x * this.texture.trim.width : this.anchor.x * -this.texture.frame.width; var dy = (this.texture.trim) ? this.texture.trim.y - this.anchor.y * this.texture.trim.height : this.anchor.y * -this.texture.frame.height; if (this.tint !== 0xFFFFFF) { if (this.cachedTint !== this.tint) { this.cachedTint = this.tint; // TODO clean up caching - how to clean up the caches? this.tintedTexture = PIXI.CanvasTinter.getTintedTexture(this, this.tint); } renderSession.context.drawImage( this.tintedTexture, 0, 0, this.texture.crop.width, this.texture.crop.height, dx, dy, this.texture.crop.width, this.texture.crop.height); } else { renderSession.context.drawImage( this.texture.baseTexture.source, this.texture.crop.x, this.texture.crop.y, this.textu