phaser-ce
Version:
Phaser CE (Community Edition) is a fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers.
837 lines (716 loc) • 27 kB
JavaScript
/**
* @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.
*
* 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 {@link #worldVisible} property.
*
* Objects that are not {@link #worldVisible} do not update their {@link #worldPosition}.
*
* @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();
this._destroyTintedTexture();
},
/**
* 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;
},
_destroyTintedTexture: function ()
{
if (!this.tintedTexture)
{
return;
}
Phaser.CanvasPool.removeByCanvas(this.tintedTexture);
this.tintedTexture = 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;
}
}
});