Merge branch 'dev'

This commit is contained in:
photonstorm
2013-10-25 18:50:14 +01:00
1232 changed files with 520360 additions and 83510 deletions
+10 -2
View File
@@ -1,9 +1,17 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* @overview
*
* Phaser - http://www.phaser.io
*
* v{version} - Built at: {buildDate}
* v<%= version %> - Built at: <%= buildDate %>
*
* @author Richard Davey http://www.photonstorm.com @photonstorm
* By Richard Davey http://www.photonstorm.com @photonstorm
*
* A feature-packed 2D HTML5 game framework born from the smouldering pits of Flixel and
* constructed via plenty of blood, sweat, tears and coffee by Richard Davey (@photonstorm).
+29
View File
@@ -0,0 +1,29 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* @overview
*
* Phaser - http://www.phaser.io
*
* v1.1 - Released October 25th 2013.
*
* By Richard Davey http://www.photonstorm.com @photonstorm
*
* A feature-packed 2D HTML5 game framework born from the smouldering pits of Flixel and
* constructed via plenty of blood, sweat, tears and coffee by Richard Davey (@photonstorm).
*
* Phaser uses Pixi.js for rendering, created by Mat Groves http://matgroves.com/ @Doormat23.
*
* Follow Phaser development progress at http://www.photonstorm.com
*
* Many thanks to Adam Saltsman (@ADAMATOMIC) for releasing Flixel, from both which Phaser
* and my love of game development originate.
*
* "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
*/
+19 -7
View File
@@ -1,10 +1,16 @@
/**
* @module Phaser
*/
var Phaser = Phaser || {
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
VERSION: '1.0.6a',
GAMES: [],
/**
* @namespace Phaser
*/
var Phaser = Phaser || {
VERSION: '<%= version %>',
GAMES: [],
AUTO: 0,
CANVAS: 1,
WEBGL: 2,
@@ -20,11 +26,17 @@ var Phaser = Phaser || {
RENDERTEXTURE: 8,
TILEMAP: 9,
TILEMAPLAYER: 10,
EMITTER: 11
EMITTER: 11,
NONE: 0,
LEFT: 1,
RIGHT: 2,
UP: 3,
DOWN: 4
};
PIXI.InteractionManager = function (dummy) {
// We don't need this in Pixi, so we've removed it to save space
// however the Stage object expects a reference to it, so here is a dummy entry.
};
};
+230
View File
@@ -0,0 +1,230 @@
/**
* We're replacing a couple of Pixi's methods here to fix or add some vital functionality:
*
* 1) Added support for Trimmed sprite sheets
* 2) Skip display objects with an alpha of zero
*
* Hopefully we can remove this once Pixi has been updated to support these things.
*/
PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject)
{
// no loger recurrsive!
var transform;
var context = this.context;
context.globalCompositeOperation = 'source-over';
// one the display object hits this. we can break the loop
var testObject = displayObject.last._iNext;
displayObject = displayObject.first;
do
{
transform = displayObject.worldTransform;
if(!displayObject.visible)
{
displayObject = displayObject.last._iNext;
continue;
}
if(!displayObject.renderable || displayObject.alpha == 0)
{
displayObject = displayObject._iNext;
continue;
}
if(displayObject instanceof PIXI.Sprite)
{
var frame = displayObject.texture.frame;
if(frame)
{
context.globalAlpha = displayObject.worldAlpha;
if (displayObject.texture.trimmed)
{
context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2] + displayObject.texture.trim.x, transform[5] + displayObject.texture.trim.y);
}
else
{
context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]);
}
context.drawImage(displayObject.texture.baseTexture.source,
frame.x,
frame.y,
frame.width,
frame.height,
(displayObject.anchor.x) * -frame.width,
(displayObject.anchor.y) * -frame.height,
frame.width,
frame.height);
}
}
else if(displayObject instanceof PIXI.Strip)
{
context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5])
this.renderStrip(displayObject);
}
else if(displayObject instanceof PIXI.TilingSprite)
{
context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5])
this.renderTilingSprite(displayObject);
}
else if(displayObject instanceof PIXI.CustomRenderable)
{
displayObject.renderCanvas(this);
}
else if(displayObject instanceof PIXI.Graphics)
{
context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5])
PIXI.CanvasGraphics.renderGraphics(displayObject, context);
}
else if(displayObject instanceof PIXI.FilterBlock)
{
if(displayObject.open)
{
context.save();
var cacheAlpha = displayObject.mask.alpha;
var maskTransform = displayObject.mask.worldTransform;
context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5])
displayObject.mask.worldAlpha = 0.5;
context.worldAlpha = 0;
PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context);
context.clip();
displayObject.mask.worldAlpha = cacheAlpha;
}
else
{
context.restore();
}
}
// count++
displayObject = displayObject._iNext;
}
while(displayObject != testObject)
}
PIXI.WebGLBatch.prototype.update = function()
{
var gl = this.gl;
var worldTransform, width, height, aX, aY, w0, w1, h0, h1, index, index2, index3
var a, b, c, d, tx, ty;
var indexRun = 0;
var displayObject = this.head;
while(displayObject)
{
if(displayObject.vcount === PIXI.visibleCount)
{
width = displayObject.texture.frame.width;
height = displayObject.texture.frame.height;
// TODO trim??
aX = displayObject.anchor.x;// - displayObject.texture.trim.x
aY = displayObject.anchor.y; //- displayObject.texture.trim.y
w0 = width * (1-aX);
w1 = width * -aX;
h0 = height * (1-aY);
h1 = height * -aY;
index = indexRun * 8;
worldTransform = displayObject.worldTransform;
a = worldTransform[0];
b = worldTransform[3];
c = worldTransform[1];
d = worldTransform[4];
tx = worldTransform[2];
ty = worldTransform[5];
if (displayObject.texture.trimmed)
{
tx += displayObject.texture.trim.x;
ty += displayObject.texture.trim.y;
}
this.verticies[index + 0 ] = a * w1 + c * h1 + tx;
this.verticies[index + 1 ] = d * h1 + b * w1 + ty;
this.verticies[index + 2 ] = a * w0 + c * h1 + tx;
this.verticies[index + 3 ] = d * h1 + b * w0 + ty;
this.verticies[index + 4 ] = a * w0 + c * h0 + tx;
this.verticies[index + 5 ] = d * h0 + b * w0 + ty;
this.verticies[index + 6] = a * w1 + c * h0 + tx;
this.verticies[index + 7] = d * h0 + b * w1 + ty;
if(displayObject.updateFrame || displayObject.texture.updateFrame)
{
this.dirtyUVS = true;
var texture = displayObject.texture;
var frame = texture.frame;
var tw = texture.baseTexture.width;
var th = texture.baseTexture.height;
this.uvs[index + 0] = frame.x / tw;
this.uvs[index +1] = frame.y / th;
this.uvs[index +2] = (frame.x + frame.width) / tw;
this.uvs[index +3] = frame.y / th;
this.uvs[index +4] = (frame.x + frame.width) / tw;
this.uvs[index +5] = (frame.y + frame.height) / th;
this.uvs[index +6] = frame.x / tw;
this.uvs[index +7] = (frame.y + frame.height) / th;
displayObject.updateFrame = false;
}
// TODO this probably could do with some optimisation....
if(displayObject.cacheAlpha != displayObject.worldAlpha)
{
displayObject.cacheAlpha = displayObject.worldAlpha;
var colorIndex = indexRun * 4;
this.colors[colorIndex] = this.colors[colorIndex + 1] = this.colors[colorIndex + 2] = this.colors[colorIndex + 3] = displayObject.worldAlpha;
this.dirtyColors = true;
}
}
else
{
index = indexRun * 8;
this.verticies[index + 0 ] = 0;
this.verticies[index + 1 ] = 0;
this.verticies[index + 2 ] = 0;
this.verticies[index + 3 ] = 0;
this.verticies[index + 4 ] = 0;
this.verticies[index + 5 ] = 0;
this.verticies[index + 6] = 0;
this.verticies[index + 7] = 0;
}
indexRun++;
displayObject = displayObject.__next;
}
}
+205 -41
View File
@@ -1,8 +1,7 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license https://github.com/photonstorm/phaser/blob/master/license.txt MIT License
* @module Phaser.Animation
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
@@ -14,7 +13,7 @@
* @param {Phaser.Game} game - A reference to the currently running game.
* @param {Phaser.Sprite} parent - A reference to the owner of this Animation.
* @param {string} name - The unique name for this animation, used in playback commands.
* @param {Phaser.Animation.FrameData} frameData - The FrameData object that contains all frames used by this Animation.
* @param {Phaser.FrameData} frameData - The FrameData object that contains all frames used by this Animation.
* @param {(Array.<number>|Array.<string>)} frames - An array of numbers or strings indicating which frames to play in which order.
* @param {number} delay - The time between each frame of the animation, given in ms.
* @param {boolean} looped - Should this animation loop or play through once.
@@ -60,6 +59,11 @@ Phaser.Animation = function (game, parent, name, frameData, frames, delay, loope
*/
this.looped = looped;
/**
* @property {boolean} looped - The loop state of the Animation.
*/
this.killOnComplete = false;
/**
* @property {boolean} isFinished - The finished state of the Animation. Set to true once playback completes, false during playback.
* @default
@@ -72,6 +76,19 @@ Phaser.Animation = function (game, parent, name, frameData, frames, delay, loope
*/
this.isPlaying = false;
/**
* @property {boolean} isPaused - The paused state of the Animation.
* @default
*/
this.isPaused = false;
/**
* @property {boolean} _pauseStartTime - The time the animation paused.
* @private
* @default
*/
this._pauseStartTime = 0;
/**
* @property {number} _frameIndex
* @private
@@ -80,7 +97,21 @@ Phaser.Animation = function (game, parent, name, frameData, frames, delay, loope
this._frameIndex = 0;
/**
* @property {Phaser.Animation.Frame} currentFrame - The currently displayed frame of the Animation.
* @property {number} _frameDiff
* @private
* @default
*/
this._frameDiff = 0;
/**
* @property {number} _frameSkip
* @private
* @default
*/
this._frameSkip = 1;
/**
* @property {Phaser.Frame} currentFrame - The currently displayed frame of the Animation.
*/
this.currentFrame = this._frameData.getFrame(this._frames[this._frameIndex]);
@@ -91,28 +122,33 @@ Phaser.Animation.prototype = {
/**
* Plays this animation.
*
* @method play
* @param {Number} [frameRate=null] The framerate to play the animation at. The speed is given in frames per second. If not provided the previously set frameRate of the Animation is used.
* @param {Boolean} [loop=null] Should the animation be looped after playback. If not provided the previously set loop value of the Animation is used.
* @return {Phaser.Animation} A reference to this Animation instance.
* @method Phaser.Animation#play
* @memberof Phaser.Animation
* @param {number} [frameRate=null] - The framerate to play the animation at. The speed is given in frames per second. If not provided the previously set frameRate of the Animation is used.
* @param {boolean} [loop=false] - Should the animation be looped after playback. If not provided the previously set loop value of the Animation is used.
* @param {boolean} [killOnComplete=false] - If set to true when the animation completes (only happens if loop=false) the parent Sprite will be killed.
* @return {Phaser.Animation} - A reference to this Animation instance.
*/
play: function (frameRate, loop) {
play: function (frameRate, loop, killOnComplete) {
frameRate = frameRate || null;
loop = loop || null;
if (frameRate !== null)
if (typeof frameRate === 'number')
{
// If they set a new frame rate then use it, otherwise use the one set on creation
this.delay = 1000 / frameRate;
// this.delay = frameRate;
}
if (loop !== null)
if (typeof loop === 'boolean')
{
// If they set a new loop value then use it, otherwise use the default set on creation
// If they set a new loop value then use it, otherwise use the one set on creation
this.looped = loop;
}
if (typeof killOnComplete !== 'undefined')
{
// Remove the parent sprite once the animation has finished?
this.killOnComplete = killOnComplete;
}
this.isPlaying = true;
this.isFinished = false;
@@ -136,7 +172,8 @@ Phaser.Animation.prototype = {
/**
* Sets this animation back to the first frame and restarts the animation.
*
* @method restart
* @method Phaser.Animation#restart
* @memberof Phaser.Animation
*/
restart: function () {
@@ -155,8 +192,9 @@ Phaser.Animation.prototype = {
/**
* Stops playback of this animation and set it to a finished state. If a resetFrame is provided it will stop playback and set frame to the first in the animation.
*
* @method stop
* @param {Boolean} [resetFrame=false] If true after the animation stops the currentFrame value will be set to the first frame in this animation.
* @method Phaser.Animation#stop
* @memberof Phaser.Animation
* @param {boolean} [resetFrame=false] - If true after the animation stops the currentFrame value will be set to the first frame in this animation.
*/
stop: function (resetFrame) {
@@ -175,21 +213,50 @@ Phaser.Animation.prototype = {
/**
* Updates this animation. Called automatically by the AnimationManager.
*
* @method update
* @method Phaser.Animation#update
* @memberof Phaser.Animation
*/
update: function () {
if (this.isPaused)
{
return false;
}
if (this.isPlaying == true && this.game.time.now >= this._timeNextFrame)
{
this._frameIndex++;
this._frameSkip = 1;
if (this._frameIndex == this._frames.length)
// Lagging?
this._frameDiff = this.game.time.now - this._timeNextFrame;
this._timeLastFrame = this.game.time.now;
if (this._frameDiff > this.delay)
{
// We need to skip a frame, work out how many
this._frameSkip = Math.floor(this._frameDiff / this.delay);
this._frameDiff -= (this._frameSkip * this.delay);
}
// And what's left now?
this._timeNextFrame = this.game.time.now + (this.delay - this._frameDiff);
this._frameIndex += this._frameSkip;
if (this._frameIndex >= this._frames.length)
{
if (this.looped)
{
this._frameIndex = 0;
this._frameIndex %= this._frames.length;
this.currentFrame = this._frameData.getFrame(this._frames[this._frameIndex]);
this._parent.setTexture(PIXI.TextureCache[this.currentFrame.uuid]);
if (this.currentFrame)
{
this._parent.setTexture(PIXI.TextureCache[this.currentFrame.uuid]);
}
this._parent.events.onAnimationLoop.dispatch(this._parent, this);
}
else
@@ -203,9 +270,6 @@ Phaser.Animation.prototype = {
this._parent.setTexture(PIXI.TextureCache[this.currentFrame.uuid]);
}
this._timeLastFrame = this.game.time.now;
this._timeNextFrame = this.game.time.now + this.delay;
return true;
}
@@ -216,7 +280,8 @@ Phaser.Animation.prototype = {
/**
* Cleans up this animation ready for deletion. Nulls all values and references.
*
* @method destroy
* @method Phaser.Animation#destroy
* @memberof Phaser.Animation
*/
destroy: function () {
@@ -232,7 +297,8 @@ Phaser.Animation.prototype = {
/**
* Called internally when the animation finishes playback. Sets the isPlaying and isFinished states and dispatches the onAnimationComplete event if it exists on the parent.
*
* @method onComplete
* @method Phaser.Animation#onComplete
* @memberof Phaser.Animation
*/
onComplete: function () {
@@ -244,28 +310,68 @@ Phaser.Animation.prototype = {
this._parent.events.onAnimationComplete.dispatch(this._parent, this);
}
if (this.killOnComplete)
{
this._parent.kill();
}
}
};
/**
* @name Phaser.Animation#paused
* @property {boolean} paused - Gets and sets the paused state of this Animation.
*/
Object.defineProperty(Phaser.Animation.prototype, "paused", {
get: function () {
return this.isPaused;
},
set: function (value) {
this.isPaused = value;
if (value)
{
// Paused
this._pauseStartTime = this.game.time.now;
}
else
{
// Un-paused
if (this.isPlaying)
{
this._timeNextFrame = this.game.time.now + this.delay;
}
}
}
});
/**
* @name Phaser.Animation#frameTotal
* @property {number} frameTotal - The total number of frames in the currently loaded FrameData, or -1 if no FrameData is loaded.
* @readonly
*/
Object.defineProperty(Phaser.Animation.prototype, "frameTotal", {
/**
* @method frameTotal
* @return {Number} The total number of frames in this animation.
*/
get: function () {
return this._frames.length;
}
});
/**
* @name Phaser.Animation#frame
* @property {number} frame - Gets or sets the current frame index and updates the Texture Cache for display.
*/
Object.defineProperty(Phaser.Animation.prototype, "frame", {
/**
* @method frame
* @return {Animation.Frame} Returns the current frame, or if not set the index of the most recent frame.
*/
get: function () {
if (this.currentFrame !== null)
@@ -279,10 +385,6 @@ Object.defineProperty(Phaser.Animation.prototype, "frame", {
},
/**
* @method frame
* @return {Number} Sets the current frame to the given frame index and updates the texture cache.
*/
set: function (value) {
this.currentFrame = this._frameData.getFrame(value);
@@ -296,3 +398,65 @@ Object.defineProperty(Phaser.Animation.prototype, "frame", {
}
});
/**
* Really handy function for when you are creating arrays of animation data but it's using frame names and not numbers.
* For example imagine you've got 30 frames named: 'explosion_0001-large' to 'explosion_0030-large'
* You could use this function to generate those by doing: Phaser.Animation.generateFrameNames('explosion_', 1, 30, '-large', 4);
*
* @method Phaser.Animation.generateFrameNames
* @param {string} prefix - The start of the filename. If the filename was 'explosion_0001-large' the prefix would be 'explosion_'.
* @param {number} start - The number to start sequentially counting from. If your frames are named 'explosion_0001' to 'explosion_0034' the start is 1.
* @param {number} stop - The number to count to. If your frames are named 'explosion_0001' to 'explosion_0034' the stop value is 34.
* @param {string} [suffix=''] - The end of the filename. If the filename was 'explosion_0001-large' the prefix would be '-large'.
* @param {number} [zeroPad=0] - The number of zeroes to pad the min and max values with. If your frames are named 'explosion_0001' to 'explosion_0034' then the zeroPad is 4.
*/
Phaser.Animation.generateFrameNames = function (prefix, start, stop, suffix, zeroPad) {
if (typeof suffix == 'undefined') { suffix = ''; }
var output = [];
var frame = '';
if (start < stop)
{
for (var i = start; i <= stop; i++)
{
if (typeof zeroPad == 'number')
{
// str, len, pad, dir
frame = Phaser.Utils.pad(i.toString(), zeroPad, '0', 1);
}
else
{
frame = i.toString();
}
frame = prefix + frame + suffix;
output.push(frame);
}
}
else
{
for (var i = start; i >= stop; i--)
{
if (typeof zeroPad == 'number')
{
// str, len, pad, dir
frame = Phaser.Utils.pad(i.toString(), zeroPad, '0', 1);
}
else
{
frame = i.toString();
}
frame = prefix + frame + suffix;
output.push(frame);
}
}
return output;
}
+127 -98
View File
@@ -1,75 +1,64 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license https://github.com/photonstorm/phaser/blob/master/license.txt MIT License
* @module Phaser.Animation
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* The AnimationManager is used to add, play and update Phaser Animations.
* The Animation Manager is used to add, play and update Phaser Animations.
* Any Game Object such as Phaser.Sprite that supports animation contains a single AnimationManager instance.
*
* @class AnimationManager
* @class Phaser.AnimationManager
* @constructor
* @param {Phaser.Sprite} sprite A reference to the Game Object that owns this AnimationManager.
* @param {Phaser.Sprite} sprite - A reference to the Game Object that owns this AnimationManager.
*/
Phaser.AnimationManager = function (sprite) {
/**
* A reference to the parent Sprite that owns this AnimationManager.
* @property sprite
* @public
* @type {Phaser.Sprite}
* @property {Phaser.Sprite} sprite - A reference to the parent Sprite that owns this AnimationManager.
*/
this.sprite = sprite;
/**
* A reference to the currently running Game.
* @property game
* @public
* @type {Phaser.Game}
* @property {Phaser.Game} game - A reference to the currently running Game.
*/
this.game = sprite.game;
/**
* The currently displayed Frame of animation, if any.
* @property currentFrame
* @public
* @type {Phaser.Animation.Frame}
*/
/**
* @property {Phaser.Frame} currentFrame - The currently displayed Frame of animation, if any.
* @default
*/
this.currentFrame = null;
/**
* Should the animation data continue to update even if the Sprite.visible is set to false.
* @property updateIfVisible
* @public
* @type {Boolean}
* @default true
*/
/**
* @property {boolean} updateIfVisible - Should the animation data continue to update even if the Sprite.visible is set to false.
* @default
*/
this.updateIfVisible = true;
/**
* A temp. var for holding the currently playing Animations FrameData.
* @property _frameData
* @private
* @type {Phaser.Animation.FrameData}
*/
/**
* @property {boolean} isLoaded - Set to true once animation data has been loaded.
* @default
*/
this.isLoaded = false;
/**
* @property {Phaser.FrameData} _frameData - A temp. var for holding the currently playing Animations FrameData.
* @private
* @default
*/
this._frameData = null;
/**
* An internal object that stores all of the Animation instances.
* @property _anims
* @private
* @type {Object}
*/
/**
* @property {object} _anims - An internal object that stores all of the Animation instances.
* @private
*/
this._anims = {};
/**
* An internal object to help avoid gc.
* @property _outputFrames
* @private
* @type {Object}
*/
/**
* @property {object} _outputFrames - An internal object to help avoid gc.
* @private
*/
this._outputFrames = [];
};
@@ -80,14 +69,15 @@ Phaser.AnimationManager.prototype = {
* Loads FrameData into the internal temporary vars and resets the frame index to zero.
* This is called automatically when a new Sprite is created.
*
* @method loadFrameData
* @method Phaser.AnimationManager#loadFrameData
* @private
* @param {Phaser.Animation.FrameData} frameData The FrameData set to load.
* @param {Phaser.FrameData} frameData - The FrameData set to load.
*/
loadFrameData: function (frameData) {
this._frameData = frameData;
this.frame = 0;
this.isLoaded = true;
},
@@ -95,12 +85,12 @@ Phaser.AnimationManager.prototype = {
* Adds a new animation under the given key. Optionally set the frames, frame rate and loop.
* Animations added in this way are played back with the play function.
*
* @method add
* @param {String} name The unique (within this Sprite) name for the animation, i.e. "run", "fire", "walk".
* @param {Array} [frames=null] An array of numbers/strings that correspond to the frames to add to this animation and in which order. e.g. [1, 2, 3] or ['run0', 'run1', run2]). If null then all frames will be used.
* @param {Number} [frameRate=60] The speed at which the animation should play. The speed is given in frames per second.
* @param {Boolean} [loop=false] {bool} Whether or not the animation is looped or just plays once.
* @param {Boolean} [useNumericIndex=true] Are the given frames using numeric indexes (default) or strings? (false)
* @method Phaser.AnimationManager#add
* @param {string} name - The unique (within this Sprite) name for the animation, i.e. "run", "fire", "walk".
* @param {Array} [frames=null] - An array of numbers/strings that correspond to the frames to add to this animation and in which order. e.g. [1, 2, 3] or ['run0', 'run1', run2]). If null then all frames will be used.
* @param {number} [frameRate=60] - The speed at which the animation should play. The speed is given in frames per second.
* @param {boolean} [loop=false] - Whether or not the animation is looped or just plays once.
* @param {boolean} [useNumericIndex=true] - Are the given frames using numeric indexes (default) or strings?
* @return {Phaser.Animation} The Animation object that was created.
*/
add: function (name, frames, frameRate, loop, useNumericIndex) {
@@ -114,7 +104,19 @@ Phaser.AnimationManager.prototype = {
frameRate = frameRate || 60;
if (typeof loop === 'undefined') { loop = false; }
if (typeof useNumericIndex === 'undefined') { useNumericIndex = true; }
// If they didn't set the useNumericIndex then let's at least try and guess it
if (typeof useNumericIndex === 'undefined')
{
if (frames && typeof frames[0] === 'number')
{
useNumericIndex = true;
}
else
{
useNumericIndex = false;
}
}
// Create the signals the AnimationManager will emit
if (this.sprite.events.onAnimationStart == null)
@@ -140,10 +142,10 @@ Phaser.AnimationManager.prototype = {
/**
* Check whether the frames in the given array are valid and exist.
*
* @method validateFrames
* @param {Array} frames An array of frames to be validated.
* @param {Boolean} [useNumericIndex=true] Validate the frames based on their numeric index (true) or string index (false)
* @return {Boolean} True if all given Frames are valid, otherwise false.
* @method Phaser.AnimationManager#validateFrames
* @param {Array} frames - An array of frames to be validated.
* @param {boolean} [useNumericIndex=true] - Validate the frames based on their numeric index (true) or string index (false)
* @return {boolean} True if all given Frames are valid, otherwise false.
*/
validateFrames: function (frames, useNumericIndex) {
@@ -175,13 +177,14 @@ Phaser.AnimationManager.prototype = {
* Play an animation based on the given key. The animation should previously have been added via sprite.animations.add()
* If the requested animation is already playing this request will be ignored. If you need to reset an already running animation do so directly on the Animation object itself.
*
* @method play
* @param {String} name The name of the animation to be played, e.g. "fire", "walk", "jump".
* @param {Number} [frameRate=null] The framerate to play the animation at. The speed is given in frames per second. If not provided the previously set frameRate of the Animation is used.
* @param {Boolean} [loop=null] Should the animation be looped after playback. If not provided the previously set loop value of the Animation is used.
* @method Phaser.AnimationManager#play
* @param {string} name - The name of the animation to be played, e.g. "fire", "walk", "jump".
* @param {number} [frameRate=null] - The framerate to play the animation at. The speed is given in frames per second. If not provided the previously set frameRate of the Animation is used.
* @param {boolean} [loop=false] - Should the animation be looped after playback. If not provided the previously set loop value of the Animation is used.
* @param {boolean} [killOnComplete=false] - If set to true when the animation completes (only happens if loop=false) the parent Sprite will be killed.
* @return {Phaser.Animation} A reference to playing Animation instance.
*/
play: function (name, frameRate, loop) {
play: function (name, frameRate, loop, killOnComplete) {
if (this._anims[name])
{
@@ -189,13 +192,13 @@ Phaser.AnimationManager.prototype = {
{
if (this.currentAnim.isPlaying == false)
{
return this.currentAnim.play(frameRate, loop);
return this.currentAnim.play(frameRate, loop, killOnComplete);
}
}
else
{
this.currentAnim = this._anims[name];
return this.currentAnim.play(frameRate, loop);
return this.currentAnim.play(frameRate, loop, killOnComplete);
}
}
@@ -205,9 +208,9 @@ Phaser.AnimationManager.prototype = {
* Stop playback of an animation. If a name is given that specific animation is stopped, otherwise the current animation is stopped.
* The currentAnim property of the AnimationManager is automatically set to the animation given.
*
* @method stop
* @param {String} [name=null] The name of the animation to be stopped, e.g. "fire". If none is given the currently running animation is stopped.
* @param {Boolean} [resetFrame=false] When the animation is stopped should the currentFrame be set to the first frame of the animation (true) or paused on the last frame displayed (false)
* @method Phaser.AnimationManager#stop
* @param {string} [name=null] - The name of the animation to be stopped, e.g. "fire". If none is given the currently running animation is stopped.
* @param {boolean} [resetFrame=false] - When the animation is stopped should the currentFrame be set to the first frame of the animation (true) or paused on the last frame displayed (false)
*/
stop: function (name, resetFrame) {
@@ -234,9 +237,9 @@ Phaser.AnimationManager.prototype = {
/**
* The main update function is called by the Sprites update loop. It's responsible for updating animation frames and firing related events.
*
* @method update
* @method Phaser.AnimationManager#update
* @protected
* @return {Boolean} True if a new animation frame has been set, otherwise false.
* @return {boolean} True if a new animation frame has been set, otherwise false.
*/
update: function () {
@@ -256,10 +259,22 @@ Phaser.AnimationManager.prototype = {
},
/**
* Refreshes the current frame data back to the parent Sprite and also resets the texture data.
*
* @method Phaser.AnimationManager#refreshFrame
*/
refreshFrame: function () {
this.sprite.currentFrame = this.currentFrame;
this.sprite.setTexture(PIXI.TextureCache[this.currentFrame.uuid]);
},
/**
* Destroys all references this AnimationManager contains. Sets the _anims to a new object and nulls the current animation.
*
* @method destroy
* @method Phaser.AnimationManager#destroy
*/
destroy: function () {
@@ -273,24 +288,26 @@ Phaser.AnimationManager.prototype = {
};
/**
* @name Phaser.AnimationManager#frameData
* @property {Phaser.FrameData} frameData - The current animations FrameData.
* @readonly
*/
Object.defineProperty(Phaser.AnimationManager.prototype, "frameData", {
/**
* @method frameData
* @return {Phaser.Animation.FrameData} Returns the FrameData of the current animation.
*/
get: function () {
return this._frameData;
}
});
/**
* @name Phaser.AnimationManager#frameTotal
* @property {number} frameTotal - The total number of frames in the currently loaded FrameData, or -1 if no FrameData is loaded.
* @readonly
*/
Object.defineProperty(Phaser.AnimationManager.prototype, "frameTotal", {
/**
* @method frameTotal
* @return {Number} Returns the total number of frames in the loaded FrameData, or -1 if no FrameData is loaded.
*/
get: function () {
if (this._frameData)
@@ -305,12 +322,32 @@ Object.defineProperty(Phaser.AnimationManager.prototype, "frameTotal", {
});
/**
* @name Phaser.AnimationManager#paused
* @property {boolean} paused - Gets and sets the paused state of the current animation.
*/
Object.defineProperty(Phaser.AnimationManager.prototype, "paused", {
get: function () {
return this.currentAnim.isPaused;
},
set: function (value) {
this.currentAnim.paused = value;
}
});
/**
* @name Phaser.AnimationManager#frame
* @property {number} frame - Gets or sets the current frame index and updates the Texture Cache for display.
*/
Object.defineProperty(Phaser.AnimationManager.prototype, "frame", {
/**
* @method frame
* @return {Number} Returns the index of the current frame.
*/
get: function () {
if (this.currentFrame)
@@ -320,13 +357,9 @@ Object.defineProperty(Phaser.AnimationManager.prototype, "frame", {
},
/**
* @method frame
* @param {Number} value Sets the current frame on the Sprite and updates the texture cache for display.
*/
set: function (value) {
if (this._frameData && this._frameData.getFrame(value) !== null)
if (typeof value === 'number' && this._frameData && this._frameData.getFrame(value) !== null)
{
this.currentFrame = this._frameData.getFrame(value);
this._frameIndex = value;
@@ -338,12 +371,12 @@ Object.defineProperty(Phaser.AnimationManager.prototype, "frame", {
});
/**
* @name Phaser.AnimationManager#frameName
* @property {string} frameName - Gets or sets the current frame name and updates the Texture Cache for display.
*/
Object.defineProperty(Phaser.AnimationManager.prototype, "frameName", {
/**
* @method frameName
* @return {String} Returns the name of the current frame if it has one.
*/
get: function () {
if (this.currentFrame)
@@ -353,13 +386,9 @@ Object.defineProperty(Phaser.AnimationManager.prototype, "frameName", {
},
/**
* @method frameName
* @param {String} value Sets the current frame on the Sprite and updates the texture cache for display.
*/
set: function (value) {
if (this._frameData && this._frameData.getFrameByName(value) !== null)
if (typeof value === 'string' && this._frameData && this._frameData.getFrameByName(value) !== null)
{
this.currentFrame = this._frameData.getFrameByName(value);
this._frameIndex = this.currentFrame.index;
@@ -1,24 +1,26 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Responsible for parsing sprite sheet and JSON data into the internal FrameData format that Phaser uses for animations.
*
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license https://github.com/photonstorm/phaser/blob/master/license.txt MIT License
* @module Phaser.Animation
* @class Phaser.AnimationParser
*/
Phaser.Animation.Parser = {
Phaser.AnimationParser = {
/**
* Parse a Sprite Sheet and extract the animation frame data from it.
*
* @method spriteSheet
* @param {Phaser.Game} game A reference to the currently running game.
* @param {String} key The Game.Cache asset key of the Sprite Sheet image.
* @param {Number} frameWidth The fixed width of each frame of the animation. If negative, indicates how many columns there are.
* @param {Number} frameHeight The fixed height of each frame of the animation. If negative, indicates how many rows there are.
* @param {Number} [frameMax=-1] The total number of animation frames to extact from the Sprite Sheet. The default value of -1 means "extract all frames".
* @return {Phaser.Animation.FrameData} A FrameData object containing the parsed frames.
* @method Phaser.AnimationParser.spriteSheet
* @param {Phaser.Game} game - A reference to the currently running game.
* @param {string} key - The Game.Cache asset key of the Sprite Sheet image.
* @param {number} frameWidth - The fixed width of each frame of the animation.
* @param {number} frameHeight - The fixed height of each frame of the animation.
* @param {number} [frameMax=-1] - The total number of animation frames to extact from the Sprite Sheet. The default value of -1 means "extract all frames".
* @return {Phaser.FrameData} A FrameData object containing the parsed frames.
*/
spriteSheet: function (game, key, frameWidth, frameHeight, frameMax) {
@@ -32,14 +34,17 @@ Phaser.Animation.Parser = {
var width = img.width;
var height = img.height;
if (frameWidth <= 0)
{
frameWidth = Math.floor(-width/Math.min(-1, frameWidth));
frameWidth = Math.floor(-width / Math.min(-1, frameWidth));
}
if (frameHeight <= 0)
{
frameHeight = Math.floor(-height/Math.min(-1, frameHeight));
frameHeight = Math.floor(-height / Math.min(-1, frameHeight));
}
var row = Math.round(width / frameWidth);
var column = Math.round(height / frameHeight);
var total = row * column;
@@ -52,12 +57,12 @@ Phaser.Animation.Parser = {
// Zero or smaller than frame sizes?
if (width == 0 || height == 0 || width < frameWidth || height < frameHeight || total === 0)
{
console.warn("Phaser.Animation.Parser.spriteSheet: width/height zero or width/height < given frameWidth/frameHeight");
console.warn("Phaser.AnimationParser.spriteSheet: width/height zero or width/height < given frameWidth/frameHeight");
return null;
}
// Let's create some frames then
var data = new Phaser.Animation.FrameData();
var data = new Phaser.FrameData();
var x = 0;
var y = 0;
@@ -65,7 +70,7 @@ Phaser.Animation.Parser = {
{
var uuid = game.rnd.uuid();
data.addFrame(new Phaser.Animation.Frame(i, x, y, frameWidth, frameHeight, '', uuid));
data.addFrame(new Phaser.Frame(i, x, y, frameWidth, frameHeight, '', uuid));
PIXI.TextureCache[uuid] = new PIXI.Texture(PIXI.BaseTextureCache[key], {
x: x,
@@ -90,24 +95,24 @@ Phaser.Animation.Parser = {
/**
* Parse the JSON data and extract the animation frame data from it.
*
* @method JSONData
* @param {Phaser.Game} game A reference to the currently running game.
* @param {Object} json The JSON data from the Texture Atlas. Must be in Array format.
* @param {String} cacheKey The Game.Cache asset key of the texture image.
* @return {Phaser.Animation.FrameData} A FrameData object containing the parsed frames.
* @method Phaser.AnimationParser.JSONData
* @param {Phaser.Game} game - A reference to the currently running game.
* @param {Object} json - The JSON data from the Texture Atlas. Must be in Array format.
* @param {string} cacheKey - The Game.Cache asset key of the texture image.
* @return {Phaser.FrameData} A FrameData object containing the parsed frames.
*/
JSONData: function (game, json, cacheKey) {
// Malformed?
if (!json['frames'])
{
console.warn("Phaser.Animation.Parser.JSONData: Invalid Texture Atlas JSON given, missing 'frames' array");
console.warn("Phaser.AnimationParser.JSONData: Invalid Texture Atlas JSON given, missing 'frames' array");
console.log(json);
return;
}
// Let's create some frames then
var data = new Phaser.Animation.FrameData();
var data = new Phaser.FrameData();
// By this stage frames is a fully parsed array
var frames = json['frames'];
@@ -117,7 +122,7 @@ Phaser.Animation.Parser = {
{
var uuid = game.rnd.uuid();
newFrame = data.addFrame(new Phaser.Animation.Frame(
newFrame = data.addFrame(new Phaser.Frame(
i,
frames[i].frame.x,
frames[i].frame.y,
@@ -146,9 +151,11 @@ Phaser.Animation.Parser = {
frames[i].spriteSourceSize.h
);
PIXI.TextureCache[uuid].realSize = frames[i].spriteSourceSize;
// PIXI.TextureCache[uuid].realSize = frames[i].sourceSize;
PIXI.TextureCache[uuid].trim.x = 0;
// We had to hack Pixi to get this to work :(
PIXI.TextureCache[uuid].trimmed = true;
PIXI.TextureCache[uuid].trim.x = frames[i].spriteSourceSize.x;
PIXI.TextureCache[uuid].trim.y = frames[i].spriteSourceSize.y;
}
}
@@ -159,24 +166,24 @@ Phaser.Animation.Parser = {
/**
* Parse the JSON data and extract the animation frame data from it.
*
* @method JSONDataHash
* @param {Phaser.Game} game A reference to the currently running game.
* @param {Object} json The JSON data from the Texture Atlas. Must be in JSON Hash format.
* @param {String} cacheKey The Game.Cache asset key of the texture image.
* @return {Phaser.Animation.FrameData} A FrameData object containing the parsed frames.
* @method Phaser.AnimationParser.JSONDataHash
* @param {Phaser.Game} game - A reference to the currently running game.
* @param {Object} json - The JSON data from the Texture Atlas. Must be in JSON Hash format.
* @param {string} cacheKey - The Game.Cache asset key of the texture image.
* @return {Phaser.FrameData} A FrameData object containing the parsed frames.
*/
JSONDataHash: function (game, json, cacheKey) {
// Malformed?
if (!json['frames'])
{
console.warn("Phaser.Animation.Parser.JSONDataHash: Invalid Texture Atlas JSON given, missing 'frames' object");
console.warn("Phaser.AnimationParser.JSONDataHash: Invalid Texture Atlas JSON given, missing 'frames' object");
console.log(json);
return;
}
// Let's create some frames then
var data = new Phaser.Animation.FrameData();
var data = new Phaser.FrameData();
// By this stage frames is a fully parsed array
var frames = json['frames'];
@@ -187,7 +194,7 @@ Phaser.Animation.Parser = {
{
var uuid = game.rnd.uuid();
newFrame = data.addFrame(new Phaser.Animation.Frame(
newFrame = data.addFrame(new Phaser.Frame(
i,
frames[key].frame.x,
frames[key].frame.y,
@@ -216,9 +223,11 @@ Phaser.Animation.Parser = {
frames[key].spriteSourceSize.h
);
PIXI.TextureCache[uuid].realSize = frames[key].spriteSourceSize;
// PIXI.TextureCache[uuid].realSize = frames[key].sourceSize;
PIXI.TextureCache[uuid].trim.x = 0;
// We had to hack Pixi to get this to work :(
PIXI.TextureCache[uuid].trimmed = true;
PIXI.TextureCache[uuid].trim.x = frames[key].spriteSourceSize.x;
PIXI.TextureCache[uuid].trim.y = frames[key].spriteSourceSize.y;
}
i++;
@@ -231,23 +240,23 @@ Phaser.Animation.Parser = {
/**
* Parse the XML data and extract the animation frame data from it.
*
* @method XMLData
* @param {Phaser.Game} game A reference to the currently running game.
* @param {Object} xml The XML data from the Texture Atlas. Must be in Starling XML format.
* @param {String} cacheKey The Game.Cache asset key of the texture image.
* @return {Phaser.Animation.FrameData} A FrameData object containing the parsed frames.
* @method Phaser.AnimationParser.XMLData
* @param {Phaser.Game} game - A reference to the currently running game.
* @param {Object} xml - The XML data from the Texture Atlas. Must be in Starling XML format.
* @param {string} cacheKey - The Game.Cache asset key of the texture image.
* @return {Phaser.FrameData} A FrameData object containing the parsed frames.
*/
XMLData: function (game, xml, cacheKey) {
// Malformed?
if (!xml.getElementsByTagName('TextureAtlas'))
{
console.warn("Phaser.Animation.Parser.XMLData: Invalid Texture Atlas XML given, missing <TextureAtlas> tag");
console.warn("Phaser.AnimationParser.XMLData: Invalid Texture Atlas XML given, missing <TextureAtlas> tag");
return;
}
// Let's create some frames then
var data = new Phaser.Animation.FrameData();
var data = new Phaser.FrameData();
var frames = xml.getElementsByTagName('SubTexture');
var newFrame;
@@ -257,7 +266,7 @@ Phaser.Animation.Parser = {
var frame = frames[i].attributes;
newFrame = data.addFrame(new Phaser.Animation.Frame(
newFrame = data.addFrame(new Phaser.Frame(
i,
frame.x.nodeValue,
frame.y.nodeValue,
@@ -275,7 +284,8 @@ Phaser.Animation.Parser = {
});
// Trimmed?
if (frame.frameX.nodeValue != '-0' || frame.frameY.nodeValue != '-0') {
if (frame.frameX.nodeValue != '-0' || frame.frameY.nodeValue != '-0')
{
newFrame.setTrim(
true,
frame.width.nodeValue,
@@ -293,7 +303,10 @@ Phaser.Animation.Parser = {
h: frame.frameHeight.nodeValue
};
PIXI.TextureCache[uuid].trim.x = 0;
// We had to hack Pixi to get this to work :(
PIXI.TextureCache[uuid].trimmed = true;
PIXI.TextureCache[uuid].trim.x = Math.abs(frame.frameX.nodeValue);
PIXI.TextureCache[uuid].trim.y = Math.abs(frame.frameY.nodeValue);
}
}
+46 -103
View File
@@ -1,198 +1,141 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license https://github.com/photonstorm/phaser/blob/master/license.txt MIT License
* @module Phaser.Animation
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* A Frame is a single frame of an animation and is part of a FrameData collection.
*
* @class Frame
* @class Phaser.Frame
* @constructor
* @param {Number} index The index of this Frame within the FrameData set it is being added to.
* @param {Number} x X position of the frame within the texture image.
* @param {Number} y Y position of the frame within the texture image.
* @param {Number} width Width of the frame within the texture image.
* @param {Number} height Height of the frame within the texture image.
* @param {String} name The name of the frame. In Texture Atlas data this is usually set to the filename.
* @param {String} uuid Internal UUID key.
* @param {number} index - The index of this Frame within the FrameData set it is being added to.
* @param {number} x - X position of the frame within the texture image.
* @param {number} y - Y position of the frame within the texture image.
* @param {number} width - Width of the frame within the texture image.
* @param {number} height - Height of the frame within the texture image.
* @param {string} name - The name of the frame. In Texture Atlas data this is usually set to the filename.
* @param {string} uuid - Internal UUID key.
*/
Phaser.Animation.Frame = function (index, x, y, width, height, name, uuid) {
Phaser.Frame = function (index, x, y, width, height, name, uuid) {
/**
* The index of this Frame within the FrameData set it is being added to.
* @property index
* @public
* @type {Number}
* @property {number} index - The index of this Frame within the FrameData set it is being added to.
*/
this.index = index;
/**
* X position within the image to cut from.
* @property x
* @public
* @type {Number}
* @property {number} x - X position within the image to cut from.
*/
this.x = x;
/**
* Y position within the image to cut from.
* @property y
* @public
* @type {Number}
* @property {number} y - Y position within the image to cut from.
*/
this.y = y;
/**
* Width of the frame.
* @property width
* @public
* @type {Number}
* @property {number} width - Width of the frame.
*/
this.width = width;
/**
* Height of the frame.
* @property height
* @public
* @type {Number}
* @property {number} height - Height of the frame.
*/
this.height = height;
/**
* Useful for Texture Atlas files. (is set to the filename value)
* @property name
* @public
* @type {String}
* @property {string} name - Useful for Texture Atlas files (is set to the filename value).
*/
this.name = name;
/**
* A link to the PIXI.TextureCache entry
* @property uuid
* @public
* @type {String}
* @property {string} uuid - A link to the PIXI.TextureCache entry.
*/
this.uuid = uuid;
/**
* center X position within the image to cut from.
* @property centerX
* @public
* @type {Number}
* @property {number} centerX - Center X position within the image to cut from.
*/
this.centerX = Math.floor(width / 2);
/**
* center Y position within the image to cut from.
* @property centerY
* @public
* @type {Number}
* @property {number} centerY - Center Y position within the image to cut from.
*/
this.centerY = Math.floor(height / 2);
/**
* The distance from the top left to the bottom-right of this Frame.
* @property distance
* @public
* @type {Number}
* @property {number} distance - The distance from the top left to the bottom-right of this Frame.
*/
this.distance = Phaser.Math.distance(0, 0, width, height);
/**
* Rotated? (not yet implemented)
* @property rotated
* @public
* @type {Boolean}
* @default false
* @property {boolean} rotated - Rotated? (not yet implemented)
* @default
*/
this.rotated = false;
/**
* Either cw or ccw, rotation is always 90 degrees.
* @property rotationDirection
* @public
* @type {String}
* @default "cw"
* @property {string} rotationDirection - Either 'cw' or 'ccw', rotation is always 90 degrees.
* @default 'cw'
*/
this.rotationDirection = 'cw';
/**
* Was it trimmed when packed?
* @property trimmed
* @public
* @type {Boolean}
* @property {boolean} trimmed - Was it trimmed when packed?
* @default
*/
this.trimmed = false;
/**
* Width of the original sprite.
* @property sourceSizeW
* @public
* @type {Number}
* @property {number} sourceSizeW - Width of the original sprite.
*/
this.sourceSizeW = width;
/**
* Height of the original sprite.
* @property sourceSizeH
* @public
* @type {Number}
* @property {number} sourceSizeH - Height of the original sprite.
*/
this.sourceSizeH = height;
/**
* X position of the trimmed sprite inside original sprite.
* @property spriteSourceSizeX
* @public
* @type {Number}
* @default 0
* @property {number} spriteSourceSizeX - X position of the trimmed sprite inside original sprite.
* @default
*/
this.spriteSourceSizeX = 0;
/**
* Y position of the trimmed sprite inside original sprite.
* @property spriteSourceSizeY
* @public
* @type {Number}
* @default 0
* @property {number} spriteSourceSizeY - Y position of the trimmed sprite inside original sprite.
* @default
*/
this.spriteSourceSizeY = 0;
/**
* Width of the trimmed sprite.
* @property spriteSourceSizeW
* @public
* @type {Number}
* @default 0
* @property {number} spriteSourceSizeW - Width of the trimmed sprite.
* @default
*/
this.spriteSourceSizeW = 0;
/**
* Height of the trimmed sprite.
* @property spriteSourceSizeH
* @public
* @type {Number}
* @default 0
* @property {number} spriteSourceSizeH - Height of the trimmed sprite.
* @default
*/
this.spriteSourceSizeH = 0;
};
Phaser.Animation.Frame.prototype = {
Phaser.Frame.prototype = {
/**
* If the frame was trimmed when added to the Texture Atlas this records the trim and source data.
*
* @method setTrim
* @param {Boolean} trimmed If this frame was trimmed or not.
* @param {Number} actualWidth The width of the frame before being trimmed.
* @param {Number} actualHeight The height of the frame before being trimmed.
* @param {Number} destX The destination X position of the trimmed frame for display.
* @param {Number} destY The destination Y position of the trimmed frame for display.
* @param {Number} destWidth The destination width of the trimmed frame for display.
* @param {Number} destHeight The destination height of the trimmed frame for display.
* @method Phaser.Frame#setTrim
* @param {boolean} trimmed - If this frame was trimmed or not.
* @param {number} actualWidth - The width of the frame before being trimmed.
* @param {number} actualHeight - The height of the frame before being trimmed.
* @param {number} destX - The destination X position of the trimmed frame for display.
* @param {number} destY - The destination Y position of the trimmed frame for display.
* @param {number} destWidth - The destination width of the trimmed frame for display.
* @param {number} destHeight - The destination height of the trimmed frame for display.
*/
setTrim: function (trimmed, actualWidth, actualHeight, destX, destY, destWidth, destHeight) {
+48 -51
View File
@@ -1,44 +1,40 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license https://github.com/photonstorm/phaser/blob/master/license.txt MIT License
* @module Phaser.Animation.FrameData
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* FrameData is a container for Frame objects, which are the internal representation of animation data in Phaser.
*
* @class FrameData
* @class Phaser.FrameData
* @constructor
*/
Phaser.Animation.FrameData = function () {
Phaser.FrameData = function () {
/**
* Local array of frames.
* @property _frames
* @private
* @type {Array}
*/
/**
* @property {Array} _frames - Local array of frames.
* @private
*/
this._frames = [];
/**
* Local array of frame names for name to index conversions.
* @property _frameNames
* @private
* @type {Array}
*/
/**
* @property {Array} _frameNames - Local array of frame names for name to index conversions.
* @private
*/
this._frameNames = [];
};
Phaser.Animation.FrameData.prototype = {
Phaser.FrameData.prototype = {
/**
* Adds a new Frame to this FrameData collection. Typically called by the Animation.Parser and not directly.
*
* @method addFrame
* @param {Phaser.Animation.Frame} frame The frame to add to this FrameData set.
* @return {Phaser.Animation.Frame} The frame that was just added.
* @method Phaser.FrameData#addFrame
* @param {Phaser.Frame} frame - The frame to add to this FrameData set.
* @return {Phaser.Frame} The frame that was just added.
*/
addFrame: function (frame) {
@@ -58,13 +54,13 @@ Phaser.Animation.FrameData.prototype = {
/**
* Get a Frame by its numerical index.
*
* @method getFrame
* @param {Number} index The index of the frame you want to get.
* @return {Phaser.Animation.Frame} The frame, if found.
* @method Phaser.FrameData#getFrame
* @param {number} index - The index of the frame you want to get.
* @return {Phaser.Frame} The frame, if found.
*/
getFrame: function (index) {
if (this._frames[index])
if (this._frames.length > index)
{
return this._frames[index];
}
@@ -76,9 +72,9 @@ Phaser.Animation.FrameData.prototype = {
/**
* Get a Frame by its frame name.
*
* @method getFrameByName
* @param {String} name The name of the frame you want to get.
* @return {Phaser.Animation.Frame} The frame, if found.
* @method Phaser.FrameData#getFrameByName
* @param {string} name - The name of the frame you want to get.
* @return {Phaser.Frame} The frame, if found.
*/
getFrameByName: function (name) {
@@ -94,9 +90,9 @@ Phaser.Animation.FrameData.prototype = {
/**
* Check if there is a Frame with the given name.
*
* @method checkFrameName
* @param {String} name The name of the frame you want to check.
* @return {Boolean} True if the frame is found, otherwise false.
* @method Phaser.FrameData#checkFrameName
* @param {string} name - The name of the frame you want to check.
* @return {boolean} True if the frame is found, otherwise false.
*/
checkFrameName: function (name) {
@@ -112,10 +108,10 @@ Phaser.Animation.FrameData.prototype = {
/**
* Returns a range of frames based on the given start and end frame indexes and returns them in an Array.
*
* @method getFrameRange
* @param {Number} start The starting frame index.
* @param {Number} end The ending frame index.
* @param {Array} [output] Optional array. If given the results will be appended to the end of this Array.
* @method Phaser.FrameData#getFrameRange
* @param {number} start - The starting frame index.
* @param {number} end - The ending frame index.
* @param {Array} [output] - If given the results will be appended to the end of this array otherwise a new array will be created.
* @return {Array} An array of Frames between the start and end index values, or an empty array if none were found.
*/
getFrameRange: function (start, end, output) {
@@ -135,10 +131,10 @@ Phaser.Animation.FrameData.prototype = {
* Returns all of the Frames in this FrameData set where the frame index is found in the input array.
* The frames are returned in the output array, or if none is provided in a new Array object.
*
* @method getFrames
* @param {Array} frames An Array containing the indexes of the frames to retrieve. If the array is empty then all frames in the FrameData are returned.
* @param {Boolean} [useNumericIndex=true] Are the given frames using numeric indexes (default) or strings? (false)
* @param {Array} [output] Optional array. If given the results will be appended to the end of this Array, otherwise a new array is created.
* @method Phaser.FrameData#getFrames
* @param {Array} frames - An Array containing the indexes of the frames to retrieve. If the array is empty then all frames in the FrameData are returned.
* @param {boolean} [useNumericIndex=true] - Are the given frames using numeric indexes (default) or strings? (false)
* @param {Array} [output] - If given the results will be appended to the end of this array otherwise a new array will be created.
* @return {Array} An array of all Frames in this FrameData set matching the given names or IDs.
*/
getFrames: function (frames, useNumericIndex, output) {
@@ -182,10 +178,10 @@ Phaser.Animation.FrameData.prototype = {
* Returns all of the Frame indexes in this FrameData set.
* The frames indexes are returned in the output array, or if none is provided in a new Array object.
*
* @method getFrameIndexes
* @param {Array} frames An Array containing the indexes of the frames to retrieve. If the array is empty then all frames in the FrameData are returned.
* @param {Boolean} [useNumericIndex=true] Are the given frames using numeric indexes (default) or strings? (false)
* @param {Array} [output] Optional array. If given the results will be appended to the end of this Array, otherwise a new array is created.
* @method Phaser.FrameData#getFrameIndexes
* @param {Array} frames - An Array containing the indexes of the frames to retrieve. If the array is empty then all frames in the FrameData are returned.
* @param {boolean} [useNumericIndex=true] - Are the given frames using numeric indexes (default) or strings? (false)
* @param {Array} [output] - If given the results will be appended to the end of this array otherwise a new array will be created.
* @return {Array} An array of all Frame indexes matching the given names or IDs.
*/
getFrameIndexes: function (frames, useNumericIndex, output) {
@@ -213,7 +209,10 @@ Phaser.Animation.FrameData.prototype = {
}
else
{
output.push(this.getFrameByName(frames[i]).index);
if (this.getFrameByName(frames[i]))
{
output.push(this.getFrameByName(frames[i]).index);
}
}
}
}
@@ -224,17 +223,15 @@ Phaser.Animation.FrameData.prototype = {
};
Object.defineProperty(Phaser.Animation.FrameData.prototype, "total", {
/**
* @name Phaser.FrameData#total
* @property {number} total - The total number of frames in this FrameData set.
* @readonly
*/
Object.defineProperty(Phaser.FrameData.prototype, "total", {
/**
* Returns the total number of frames in this FrameData set.
*
* @method total
* @return {Number} The total number of frames in this FrameData set.
*/
get: function () {
return this._frames.length;
}
});
+209 -171
View File
@@ -1,123 +1,125 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license https://github.com/photonstorm/phaser/blob/master/license.txt MIT License
* @module Phaser.Camera
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
*
* A Camera is your view into the game world. It has a position and size and renders only those objects within its field of view.
* The game automatically creates a single Stage sized camera on boot. Move the camera around the world with Phaser.Camera.x/y
*
* @class Camera
* @class Phaser.Camera
* @constructor
* @param {Phaser.Game} game game reference to the currently running game.
* @param {number} id not being used at the moment, will be when Phaser supports multiple camera
* @param {number} x position of the camera on the X axis
* @param {number} y position of the camera on the Y axis
* @param {number} width the width of the view rectangle
* @param {number} height the height of the view rectangle
* @param {Phaser.Game} game - Game reference to the currently running game.
* @param {number} id - Not being used at the moment, will be when Phaser supports multiple camera
* @param {number} x - Position of the camera on the X axis
* @param {number} y - Position of the camera on the Y axis
* @param {number} width - The width of the view rectangle
* @param {number} height - The height of the view rectangle
*/
Phaser.Camera = function (game, id, x, y, width, height) {
/**
* A reference to the currently running Game.
* @property game
* @public
* @type {Phaser.Game}
*/
/**
* @property {Phaser.Game} game - A reference to the currently running Game.
*/
this.game = game;
/**
* A reference to the game world
* @property world
* @public
* @type {Phaser.World}
*/
/**
* @property {Phaser.World} world - A reference to the game world.
*/
this.world = game.world;
/**
* reserved for future multiple camera set-ups
* @property id
* @public
* @type {number}
*/
/**
* @property {number} id - Reserved for future multiple camera set-ups.
* @default
*/
this.id = 0;
/**
* Camera view.
* The view into the world we wish to render (by default the game dimensions)
* The x/y values are in world coordinates, not screen coordinates, the width/height is how many pixels to render
* Objects outside of this view are not rendered (unless set to ignore the Camera, i.e. UI?)
* @property view
* @public
* @type {Phaser.Rectangle}
*/
/**
* Camera view.
* The view into the world we wish to render (by default the game dimensions).
* The x/y values are in world coordinates, not screen coordinates, the width/height is how many pixels to render.
* Objects outside of this view are not rendered (unless set to ignore the Camera, i.e. UI?).
* @property {Phaser.Rectangle} view
*/
this.view = new Phaser.Rectangle(x, y, width, height);
/**
* Used by Sprites to work out Camera culling.
* @property screenView
* @public
* @type {Phaser.Rectangle}
*/
* @property {Phaser.Rectangle} screenView - Used by Sprites to work out Camera culling.
*/
this.screenView = new Phaser.Rectangle(x, y, width, height);
/**
* Sprite moving inside this Rectangle will not cause camera moving.
* @property deadzone
* @type {Phaser.Rectangle}
* The Camera is bound to this Rectangle and cannot move outside of it. By default it is enabled and set to the size of the World.
* The Rectangle can be located anywhere in the world and updated as often as you like. If you don't wish the Camera to be bound
* at all then set this to null. The values can be anything and are in World coordinates, with 0,0 being the center of the world.
* @property {Phaser.Rectangle} bounds - The Rectangle in which the Camera is bounded. Set to null to allow for movement anywhere.
*/
this.bounds = new Phaser.Rectangle(x, y, width, height);
/**
* @property {Phaser.Rectangle} deadzone - Moving inside this Rectangle will not cause camera moving.
*/
this.deadzone = null;
/**
* Whether this camera is visible or not. (default is true)
* @property visible
* @public
* @default true
* @type {bool}
*/
/**
* @property {boolean} visible - Whether this camera is visible or not.
* @default
*/
this.visible = true;
/**
* Whether this camera is flush with the World Bounds or not.
* @property atLimit
* @type {bool}
/**
* @property {boolean} atLimit - Whether this camera is flush with the World Bounds or not.
*/
this.atLimit = { x: false, y: false };
/**
* If the camera is tracking a Sprite, this is a reference to it, otherwise null
* @property target
* @public
* @type {Phaser.Sprite}
/**
* @property {Phaser.Sprite} target - If the camera is tracking a Sprite, this is a reference to it, otherwise null.
* @default
*/
this.target = null;
/**
* Edge property
* @property edge
/**
* @property {number} edge - Edge property.
* @private
* @type {number}
* @default
*/
this._edge = 0;
this.displayObject = null;
};
// Consts
/**
* @constant
* @type {number}
*/
Phaser.Camera.FOLLOW_LOCKON = 0;
/**
* @constant
* @type {number}
*/
Phaser.Camera.FOLLOW_PLATFORMER = 1;
/**
* @constant
* @type {number}
*/
Phaser.Camera.FOLLOW_TOPDOWN = 2;
/**
* @constant
* @type {number}
*/
Phaser.Camera.FOLLOW_TOPDOWN_TIGHT = 3;
Phaser.Camera.prototype = {
/**
* Tells this camera which sprite to follow.
* @method follow
* @param {Phaser.Sprite} target The object you want the camera to track. Set to null to not follow anything.
* @method Phaser.Camera#follow
* @param {Phaser.Sprite} target - The object you want the camera to track. Set to null to not follow anything.
* @param {number} [style] Leverage one of the existing "deadzone" presets. If you use a custom deadzone, ignore this parameter and manually specify the deadzone after calling follow().
*/
follow: function (target, style) {
@@ -152,103 +154,136 @@ Phaser.Camera.prototype = {
break;
}
},
/**
* Move the camera focus on a display object instantly.
* @method Phaser.Camera#focusOn
* @param {any} displayObject - The display object to focus the camera on. Must have visible x/y properties.
*/
focusOn: function (displayObject) {
this.setPosition(Math.round(displayObject.x - this.view.halfWidth), Math.round(displayObject.y - this.view.halfHeight));
},
/**
* Move the camera focus to a location instantly.
* @method focusOnXY
* @param {number} x X position.
* @param {number} y Y position.
* Move the camera focus on a location instantly.
* @method Phaser.Camera#focusOnXY
* @param {number} x - X position.
* @param {number} y - Y position.
*/
focusOnXY: function (x, y) {
this.view.x = Math.round(x - this.view.halfWidth);
this.view.y = Math.round(y - this.view.halfHeight);
this.setPosition(Math.round(x - this.view.halfWidth), Math.round(y - this.view.halfHeight));
},
/**
* Update focusing and scrolling.
* @method update
* @method Phaser.Camera#update
*/
update: function () {
// Add dirty flag
if (this.target !== null)
if (this.target)
{
if (this.deadzone)
{
this._edge = this.target.x - this.deadzone.x;
if (this.view.x > this._edge)
{
this.view.x = this._edge;
}
this._edge = this.target.x + this.target.width - this.deadzone.x - this.deadzone.width;
if (this.view.x < this._edge)
{
this.view.x = this._edge;
}
this._edge = this.target.y - this.deadzone.y;
if (this.view.y > this._edge)
{
this.view.y = this._edge;
}
this._edge = this.target.y + this.target.height - this.deadzone.y - this.deadzone.height;
if (this.view.y < this._edge)
{
this.view.y = this._edge;
}
}
else
{
this.focusOnXY(this.target.x, this.target.y);
}
this.updateTarget();
}
this.checkWorldBounds();
if (this.bounds)
{
this.checkBounds();
}
if (this.view.x !== -this.displayObject.position.x)
{
this.displayObject.position.x = -this.view.x;
}
if (this.view.y !== -this.displayObject.position.y)
{
this.displayObject.position.y = -this.view.y;
}
},
updateTarget: function () {
if (this.deadzone)
{
this._edge = this.target.x - this.deadzone.x;
if (this.view.x > this._edge)
{
this.view.x = this._edge;
}
this._edge = this.target.x + this.target.width - this.deadzone.x - this.deadzone.width;
if (this.view.x < this._edge)
{
this.view.x = this._edge;
}
this._edge = this.target.y - this.deadzone.y;
if (this.view.y > this._edge)
{
this.view.y = this._edge;
}
this._edge = this.target.y + this.target.height - this.deadzone.y - this.deadzone.height;
if (this.view.y < this._edge)
{
this.view.y = this._edge;
}
}
else
{
this.focusOnXY(this.target.x, this.target.y);
}
},
setBoundsToWorld: function () {
this.bounds.setTo(this.game.world.x, this.game.world.y, this.game.world.width, this.game.world.height);
},
/**
* Method called to ensure the camera doesn't venture outside of the game world
* @method checkWorldBounds
* Method called to ensure the camera doesn't venture outside of the game world.
* @method Phaser.Camera#checkWorldBounds
*/
checkWorldBounds: function () {
checkBounds: function () {
this.atLimit.x = false;
this.atLimit.y = false;
// Make sure we didn't go outside the cameras worldBounds
if (this.view.x < this.world.bounds.left)
// Make sure we didn't go outside the cameras bounds
if (this.view.x < this.bounds.x)
{
this.atLimit.x = true;
this.view.x = this.world.bounds.left;
this.view.x = this.bounds.x;
}
if (this.view.x > this.world.bounds.right - this.width)
if (this.view.x > this.bounds.right - this.width)
{
this.atLimit.x = true;
this.view.x = (this.world.bounds.right - this.width) + 1;
this.view.x = (this.bounds.right - this.width) + 1;
}
if (this.view.y < this.world.bounds.top)
if (this.view.y < this.bounds.top)
{
this.atLimit.y = true;
this.view.y = this.world.bounds.top;
this.view.y = this.bounds.top;
}
if (this.view.y > this.world.bounds.bottom - this.height)
if (this.view.y > this.bounds.bottom - this.height)
{
this.atLimit.y = true;
this.view.y = (this.world.bounds.bottom - this.height) + 1;
this.view.y = (this.bounds.bottom - this.height) + 1;
}
this.view.floor();
@@ -257,26 +292,30 @@ Phaser.Camera.prototype = {
/**
* A helper function to set both the X and Y properties of the camera at once
* without having to use game.camera.x and game.camera.y
* without having to use game.camera.x and game.camera.y.
*
* @method setPosition
* @param {number} x X position.
* @param {number} y Y position.
* @method Phaser.Camera#setPosition
* @param {number} x - X position.
* @param {number} y - Y position.
*/
setPosition: function (x, y) {
this.view.x = x;
this.view.y = y;
this.checkWorldBounds();
if (this.bounds)
{
this.checkBounds();
}
},
/**
* Sets the size of the view rectangle given the width and height in parameters
* Sets the size of the view rectangle given the width and height in parameters.
*
* @method setSize
* @param {number} width The desired width.
* @param {number} height The desired height.
* @method Phaser.Camera#setSize
* @param {number} width - The desired width.
* @param {number} height - The desired height.
*/
setSize: function (width, height) {
@@ -287,81 +326,80 @@ Phaser.Camera.prototype = {
};
/**
* The Cameras x coordinate. This value is automatically clamped if it falls outside of the World bounds.
* @name Phaser.Camera#x
* @property {number} x - Gets or sets the cameras x position.
*/
Object.defineProperty(Phaser.Camera.prototype, "x", {
/**
* @method x
* @return {Number} The x position
*/
get: function () {
return this.view.x;
},
/**
* @method x
* @return {Number} Sets the camera's x position and clamp it if it's outside the world bounds
*/
set: function (value) {
this.view.x = value;
this.checkWorldBounds();
if (this.bounds)
{
this.checkBounds();
}
}
});
/**
* The Cameras y coordinate. This value is automatically clamped if it falls outside of the World bounds.
* @name Phaser.Camera#y
* @property {number} y - Gets or sets the cameras y position.
*/
Object.defineProperty(Phaser.Camera.prototype, "y", {
/**
* @method y
* @return {Number} The y position
*/
get: function () {
return this.view.y;
},
/**
* @method y
* @return {Number} Sets the camera's y position and clamp it if it's outside the world bounds
*/
set: function (value) {
this.view.y = value;
this.checkWorldBounds();
if (this.bounds)
{
this.checkBounds();
}
}
});
/**
* The Cameras width. By default this is the same as the Game size and should not be adjusted for now.
* @name Phaser.Camera#width
* @property {number} width - Gets or sets the cameras width.
*/
Object.defineProperty(Phaser.Camera.prototype, "width", {
/**
* @method width
* @return {Number} The width of the view rectangle, in pixels
*/
get: function () {
return this.view.width;
},
/**
* @method width
* @return {Number} Sets the width of the view rectangle
*/
set: function (value) {
this.view.width = value;
}
});
/**
* The Cameras height. By default this is the same as the Game size and should not be adjusted for now.
* @name Phaser.Camera#height
* @property {number} height - Gets or sets the cameras height.
*/
Object.defineProperty(Phaser.Camera.prototype, "height", {
/**
* @method height
* @return {Number} The height of the view rectangle, in pixels
*/
get: function () {
return this.view.height;
},
/**
* @method height
* @return {Number} Sets the height of the view rectangle
*/
set: function (value) {
this.view.height = value;
}
+145 -110
View File
@@ -1,28 +1,26 @@
/**
* Phaser.Game
*
* This is where the magic happens. The Game object is the heart of your game,
* providing quick access to common functions and handling the boot process.
*
* "Hell, there are no rules here - we're trying to accomplish something."
* Thomas A. Edison
*
* @package Phaser.Game
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license https://github.com/photonstorm/phaser/blob/master/license.txt MIT License
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Game constructor
*
* Instantiate a new <code>Phaser.Game</code> object.
*
* @class Phaser.Game
* @classdesc This is where the magic happens. The Game object is the heart of your game,
* providing quick access to common functions and handling the boot process.
* <p>"Hell, there are no rules here - we're trying to accomplish something."</p><br>
* Thomas A. Edison
* @constructor
* @param width {number} The width of your game in game pixels.
* @param height {number} The height of your game in game pixels.
* @param renderer {number} Which renderer to use (canvas or webgl)
* @param parent {string} ID of its parent DOM element.
* @param {number} width - The width of your game in game pixels.
* @param {number} height - The height of your game in game pixels.
* @param {number} renderer -Which renderer to use (canvas or webgl)
* @param {HTMLElement} parent -The Games DOM parent.
* @param {Description} state - Description.
* @param {boolean} transparent - Use a transparent canvas background or not.
* @param {boolean} antialias - Anti-alias graphics.
*/
Phaser.Game = function (width, height, renderer, parent, state, transparent, antialias) {
@@ -31,206 +29,201 @@ Phaser.Game = function (width, height, renderer, parent, state, transparent, ant
renderer = renderer || Phaser.AUTO;
parent = parent || '';
state = state || null;
transparent = transparent || false;
antialias = typeof antialias === 'undefined' ? true : antialias;
if (typeof transparent == 'undefined') { transparent = false; }
if (typeof antialias == 'undefined') { antialias = true; }
/**
* Phaser Game ID (for when Pixi supports multiple instances)
* @type {number}
* @property {number} id - Phaser Game ID (for when Pixi supports multiple instances).
*/
this.id = Phaser.GAMES.push(this) - 1;
/**
* The Games DOM parent.
* @type {HTMLElement}
* @property {HTMLElement} parent - The Games DOM parent.
*/
this.parent = parent;
// Do some more intelligent size parsing here, so they can set "100%" for example, maybe pass the scale mode in here too?
/**
* The Game width (in pixels).
* @type {number}
* @property {number} width - The Game width (in pixels).
*/
this.width = width;
/**
* The Game height (in pixels).
* @type {number}
* @property {number} height - The Game height (in pixels).
*/
this.height = height;
/**
* Use a transparent canvas background or not.
* @type {boolean}
* @property {boolean} transparent - Use a transparent canvas background or not.
*/
this.transparent = transparent;
/**
* Anti-alias graphics (in WebGL this helps with edges, in Canvas2D it retains pixel-art quality)
* @type {boolean}
* @property {boolean} antialias - Anti-alias graphics (in WebGL this helps with edges, in Canvas2D it retains pixel-art quality).
*/
this.antialias = antialias;
/**
* The Pixi Renderer
* @type {number}
* @property {number} renderer - The Pixi Renderer
* @default
*/
this.renderer = null;
/**
* The StateManager.
* @type {Phaser.StateManager}
*/
/**
* @property {number} state - The StateManager.
*/
this.state = new Phaser.StateManager(this, state);
/**
* Is game paused?
* @type {bool}
* @property {boolean} _paused - Is game paused?
* @private
* @default
*/
this._paused = false;
/**
* The Renderer this Phaser.Game will use. Either Phaser.RENDERER_AUTO, Phaser.RENDERER_CANVAS or Phaser.RENDERER_WEBGL
* @type {number}
* @property {number} renderType - The Renderer this Phaser.Game will use. Either Phaser.RENDERER_AUTO, Phaser.RENDERER_CANVAS or Phaser.RENDERER_WEBGL.
*/
this.renderType = renderer;
/**
* Whether load complete loading or not.
* @type {bool}
* @property {boolean} _loadComplete - Whether load complete loading or not.
* @private
* @default
*/
this._loadComplete = false;
/**
* Whether the game engine is booted, aka available.
* @type {bool}
* @property {boolean} isBooted - Whether the game engine is booted, aka available.
* @default
*/
this.isBooted = false;
/**
* Is game running or paused?
* @type {bool}
* @property {boolean} id -Is game running or paused?
* @default
*/
this.isRunning = false;
/**
* Automatically handles the core game loop via requestAnimationFrame or setTimeout
* @type {Phaser.RequestAnimationFrame}
* @property {Phaser.RequestAnimationFrame} raf - Automatically handles the core game loop via requestAnimationFrame or setTimeout
* @default
*/
this.raf = null;
/**
* Reference to the GameObject Factory.
* @type {Phaser.GameObjectFactory}
*/
/**
* @property {Phaser.GameObjectFactory} add - Reference to the GameObject Factory.
* @default
*/
this.add = null;
/**
* Reference to the assets cache.
* @type {Phaser.Cache}
*/
* @property {Phaser.Cache} cache - Reference to the assets cache.
* @default
*/
this.cache = null;
/**
* Reference to the input manager
* @type {Phaser.Input}
*/
* @property {Phaser.Input} input - Reference to the input manager
* @default
*/
this.input = null;
/**
* Reference to the assets loader.
* @type {Phaser.Loader}
*/
* @property {Phaser.Loader} load - Reference to the assets loader.
* @default
*/
this.load = null;
/**
* Reference to the math helper.
* @type {Phaser.GameMath}
*/
* @property {Phaser.GameMath} math - Reference to the math helper.
* @default
*/
this.math = null;
/**
* Reference to the network class.
* @type {Phaser.Net}
*/
* @property {Phaser.Net} net - Reference to the network class.
* @default
*/
this.net = null;
/**
* Reference to the sound manager.
* @type {Phaser.SoundManager}
*/
* @property {Phaser.SoundManager} sound - Reference to the sound manager.
* @default
*/
this.sound = null;
/**
* Reference to the stage.
* @type {Phaser.Stage}
*/
* @property {Phaser.Stage} stage - Reference to the stage.
* @default
*/
this.stage = null;
/**
* Reference to game clock.
* @type {Phaser.TimeManager}
*/
* @property {Phaser.TimeManager} time - Reference to game clock.
* @default
*/
this.time = null;
/**
* Reference to the tween manager.
* @type {Phaser.TweenManager}
*/
* @property {Phaser.TweenManager} tweens - Reference to the tween manager.
* @default
*/
this.tweens = null;
/**
* Reference to the world.
* @type {Phaser.World}
*/
* @property {Phaser.World} world - Reference to the world.
* @default
*/
this.world = null;
/**
* Reference to the physics manager.
* @type {Phaser.Physics.PhysicsManager}
*/
* @property {Phaser.Physics.PhysicsManager} physics - Reference to the physics manager.
* @default
*/
this.physics = null;
/**
* Instance of repeatable random data generator helper.
* @type {Phaser.RandomDataGenerator}
*/
* @property {Phaser.RandomDataGenerator} rnd - Instance of repeatable random data generator helper.
* @default
*/
this.rnd = null;
/**
* Contains device information and capabilities.
* @type {Phaser.Device}
*/
* @property {Phaser.Device} device - Contains device information and capabilities.
* @default
*/
this.device = null;
/**
* A handy reference to world.camera
* @type {Phaser.Camera}
/**
* @property {Phaser.Physics.PhysicsManager} camera - A handy reference to world.camera.
* @default
*/
this.camera = null;
/**
* A handy reference to renderer.view
* @type {HTMLCanvasElement}
/**
* @property {HTMLCanvasElement} canvas - A handy reference to renderer.view.
* @default
*/
this.canvas = null;
/**
* A handy reference to renderer.context (only set for CANVAS games)
* @type {Context}
* @property {Context} context - A handy reference to renderer.context (only set for CANVAS games)
* @default
*/
this.context = null;
/**
* A set of useful debug utilities
* @type {Phaser.Utils.Debug}
/**
* @property {Phaser.Utils.Debug} debug - A set of useful debug utilitie.
* @default
*/
this.debug = null;
/**
* The Particle Manager
* @type {Phaser.Particles}
* @property {Phaser.Particles} particles - The Particle Manager.
* @default
*/
this.particles = null;
@@ -258,9 +251,9 @@ Phaser.Game.prototype = {
/**
* Initialize engine sub modules and start the game.
* @param parent {string} ID of parent Dom element.
* @param width {number} Width of the game screen.
* @param height {number} Height of the game screen.
*
* @method Phaser.Game#boot
* @protected
*/
boot: function () {
@@ -305,14 +298,14 @@ Phaser.Game.prototype = {
this.net = new Phaser.Net(this);
this.debug = new Phaser.Utils.Debug(this);
this.load.onLoadComplete.add(this.loadComplete, this);
this.stage.boot();
this.world.boot();
this.input.boot();
this.sound.boot();
this.state.boot();
this.load.onLoadComplete.add(this.loadComplete, this);
if (this.renderType == Phaser.CANVAS)
{
console.log('%cPhaser ' + Phaser.VERSION + ' initialized. Rendering to Canvas', 'color: #ffff33; background: #000000');
@@ -322,6 +315,14 @@ Phaser.Game.prototype = {
console.log('%cPhaser ' + Phaser.VERSION + ' initialized. Rendering to WebGL', 'color: #ffff33; background: #000000');
}
var pos = Phaser.VERSION.indexOf('-');
var versionQualifier = (pos >= 0) ? Phaser.VERSION.substr(pos + 1) : null;
if (versionQualifier)
{
var article = ['a', 'e', 'i', 'o', 'u', 'y'].indexOf(versionQualifier.charAt(0)) >= 0 ? 'an' : 'a';
console.warn('You are using %s %s version of Phaser. Some things may not work.', article, versionQualifier);
}
this.isRunning = true;
this._loadComplete = false;
@@ -332,6 +333,12 @@ Phaser.Game.prototype = {
},
/**
* Checks if the device is capable of using the requested renderer and sets it up or an alternative if not.
*
* @method Phaser.Game#setUpRenderer
* @protected
*/
setUpRenderer: function () {
if (this.renderType === Phaser.CANVAS || (this.renderType === Phaser.AUTO && this.device.webGL == false))
@@ -365,6 +372,9 @@ Phaser.Game.prototype = {
/**
* Called when the load has finished, after preload was run.
*
* @method Phaser.Game#loadComplete
* @protected
*/
loadComplete: function () {
@@ -374,6 +384,13 @@ Phaser.Game.prototype = {
},
/**
* The core game loop.
*
* @method Phaser.Game#update
* @protected
* @param {number} time - The current time as provided by RequestAnimationFrame.
*/
update: function (time) {
this.time.update(time);
@@ -383,6 +400,7 @@ Phaser.Game.prototype = {
this.plugins.preUpdate();
this.physics.preUpdate();
this.stage.update();
this.input.update();
this.tweens.update();
this.sound.update();
@@ -391,6 +409,8 @@ Phaser.Game.prototype = {
this.state.update();
this.plugins.update();
this.world.postUpdate();
this.renderer.render(this.stage._stage);
this.plugins.render();
this.state.render();
@@ -402,9 +422,15 @@ Phaser.Game.prototype = {
/**
* Nuke the entire game from orbit
*
* @method Phaser.Game#destroy
*/
destroy: function () {
this.raf.stop();
this.input.destroy();
this.state.destroy();
this.state = null;
@@ -421,6 +447,12 @@ Phaser.Game.prototype = {
};
/**
* The paused state of the Game. A paused game doesn't update any of its subsystems.
* When a game is paused the onPause event is dispatched. When it is resumed the onResume event is dispatched.
* @name Phaser.Game#paused
* @property {boolean} paused - Gets and sets the paused state of the Game.
*/
Object.defineProperty(Phaser.Game.prototype, "paused", {
get: function () {
@@ -450,3 +482,6 @@ Object.defineProperty(Phaser.Game.prototype, "paused", {
});
/**
* "Deleted code is debugged code." - Jeff Sickel
*/
+414 -36
View File
@@ -1,13 +1,39 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Phaser Group constructor.
* @class Phaser.Group
* @classdesc A Group is a container for display objects that allows for fast pooling, recycling and collision checks.
* @constructor
* @param {Phaser.Game} game - A reference to the currently running game.
* @param {*} parent - The parent Group or DisplayObjectContainer that will hold this group, if any.
* @param {string} [name=group] - A name for this Group. Not used internally but useful for debugging.
* @param {boolean} [useStage=false] - Should the DisplayObjectContainer this Group creates be added to the World (default, false) or direct to the Stage (true).
*/
Phaser.Group = function (game, parent, name, useStage) {
parent = parent || null;
if (typeof parent === 'undefined')
{
parent = game.world;
}
if (typeof useStage == 'undefined')
if (typeof useStage === 'undefined')
{
useStage = false;
}
/**
* @property {Phaser.Game} game - A reference to the currently running Game.
*/
this.game = game;
/**
* @property {string} name - A name for this Group. Not used internally but useful for debugging.
*/
this.name = name || 'group';
if (useStage)
@@ -24,31 +50,53 @@ Phaser.Group = function (game, parent, name, useStage) {
if (parent instanceof Phaser.Group)
{
parent._container.addChild(this._container);
parent._container.updateTransform();
}
else
{
parent.addChild(this._container);
parent.updateTransform();
}
}
else
{
this.game.stage._stage.addChild(this._container);
this.game.stage._stage.updateTransform();
}
}
/**
* @property {number} type - Internal Phaser Type value.
* @protected
*/
this.type = Phaser.GROUP;
/**
* @property {boolean} exists - If exists is true the the Group is updated, otherwise it is skipped.
* @default
*/
this.exists = true;
/**
* Helper for sort.
*/
this._sortIndex = 'y';
* @property {Phaser.Point} scale - Replaces the PIXI.Point with a slightly more flexible one.
*/
this.scale = new Phaser.Point(1, 1);
};
Phaser.Group.prototype = {
/**
* Adds an existing object to this Group. The object can be an instance of Phaser.Sprite, Phaser.Button or any other display object.
* The child is automatically added to the top of the Group, so renders on-top of everything else within the Group. If you need to control
* that then see the addAt method.
*
* @see Phaser.Group#create
* @see Phaser.Group#addAt
* @method Phaser.Group#add
* @param {*} child - An instance of Phaser.Sprite, Phaser.Button or any other display object..
* @return {*} The child that was added to the Group.
*/
add: function (child) {
if (child.group !== this)
@@ -61,12 +109,23 @@ Phaser.Group.prototype = {
}
this._container.addChild(child);
child.updateTransform();
}
return child;
},
/**
* Adds an existing object to this Group. The object can be an instance of Phaser.Sprite, Phaser.Button or any other display object.
* The child is added to the Group at the location specified by the index value, this allows you to control child ordering.
*
* @method Phaser.Group#addAt
* @param {*} child - An instance of Phaser.Sprite, Phaser.Button or any other display object..
* @param {number} index - The index within the Group to insert the child to.
* @return {*} The child that was added to the Group.
*/
addAt: function (child, index) {
if (child.group !== this)
@@ -79,18 +138,40 @@ Phaser.Group.prototype = {
}
this._container.addChildAt(child, index);
child.updateTransform();
}
return child;
},
/**
* Returns the child found at the given index within this Group.
*
* @method Phaser.Group#getAt
* @memberof Phaser.Group
* @param {number} index - The index to return the child from.
* @return {*} The child that was found at the given index.
*/
getAt: function (index) {
return this._container.getChildAt(index);
},
/**
* Automatically creates a new Phaser.Sprite object and adds it to the top of this Group.
* Useful if you don't need to create the Sprite instances before-hand.
*
* @method Phaser.Group#create
* @param {number} x - The x coordinate to display the newly created Sprite at. The value is in relation to the Group.x point.
* @param {number} y - The y coordinate to display the newly created Sprite at. The value is in relation to the Group.y point.
* @param {string} key - The Game.cache key of the image that this Sprite will use.
* @param {number|string} [frame] - If the Sprite image contains multiple frames you can specify which one to use here.
* @param {boolean} [exists=true] - The default exists state of the Sprite.
* @return {Phaser.Sprite} The child that was created.
*/
create: function (x, y, key, frame, exists) {
if (typeof exists == 'undefined') { exists = true; }
@@ -99,6 +180,8 @@ Phaser.Group.prototype = {
child.group = this;
child.exists = exists;
child.visible = exists;
child.alive = exists;
if (child.events)
{
@@ -106,11 +189,57 @@ Phaser.Group.prototype = {
}
this._container.addChild(child);
child.updateTransform();
return child;
},
/**
* Automatically creates multiple Phaser.Sprite objects and adds them to the top of this Group.
* Useful if you need to quickly generate a pool of identical sprites, such as bullets. By default the sprites will be set to not exist
* and will be positioned at 0, 0 (relative to the Group.x/y)
*
* @method Phaser.Group#createMultiple
* @param {number} quantity - The number of Sprites to create.
* @param {string} key - The Game.cache key of the image that this Sprite will use.
* @param {number|string} [frame] - If the Sprite image contains multiple frames you can specify which one to use here.
* @param {boolean} [exists=false] - The default exists state of the Sprite.
*/
createMultiple: function (quantity, key, frame, exists) {
if (typeof exists == 'undefined') { exists = false; }
for (var i = 0; i < quantity; i++)
{
var child = new Phaser.Sprite(this.game, 0, 0, key, frame);
child.group = this;
child.exists = exists;
child.visible = exists;
child.alive = exists;
if (child.events)
{
child.events.onAddedToGroup.dispatch(child, this);
}
this._container.addChild(child);
child.updateTransform();
}
},
/**
* Swaps the position of two children in this Group.
*
* @method Phaser.Group#swap
* @param {*} child1 - The first child to swap.
* @param {*} child2 - The second child to swap.
* @return {boolean} True if the swap was successful, otherwise false.
*/
swap: function (child1, child2) {
if (child1 === child2 || !child1.parent || !child2.parent)
@@ -231,6 +360,13 @@ Phaser.Group.prototype = {
},
/**
* Brings the given child to the top of this Group so it renders above all other children.
*
* @method Phaser.Group#bringToTop
* @param {*} child - The child to bring to the top of this Group.
* @return {*} The child that was moved.
*/
bringToTop: function (child) {
if (child.group === this)
@@ -243,12 +379,26 @@ Phaser.Group.prototype = {
},
/**
* Get the index position of the given child in this Group.
*
* @method Phaser.Group#getIndex
* @param {*} child - The child to get the index for.
* @return {number} The index of the child or -1 if it's not a member of this Group.
*/
getIndex: function (child) {
return this._container.children.indexOf(child);
},
/**
* Replaces a child of this Group with the given newChild. The newChild cannot be a member of this Group.
*
* @method Phaser.Group#replace
* @param {*} oldChild - The child in this Group that will be replaced.
* @param {*} newChild - The child to be inserted into this group.
*/
replace: function (oldChild, newChild) {
if (!this._container.first._iNext)
@@ -269,11 +419,20 @@ Phaser.Group.prototype = {
this._container.removeChild(oldChild);
this._container.addChildAt(newChild, index);
newChild.events.onAddedToGroup.dispatch(newChild, this);
newChild.updateTransform();
}
},
// key is an ARRAY of values.
/**
* Sets the given property to the given value on the child. The operation controls the assignment of the value.
*
* @method Phaser.Group#setProperty
* @param {*} child - The child to set the property value on.
* @param {array} key - An array of strings that make up the property that will be set.
* @param {*} value - The value that will be set.
* @param {number} [operation=0] - Controls how the value is assigned. A value of 0 replaces the value with the new one. A value of 1 adds it, 2 subtracts it, 3 multiplies it and 4 divides it.
*/
setProperty: function (child, key, value, operation) {
operation = operation || 0;
@@ -327,11 +486,24 @@ Phaser.Group.prototype = {
},
/**
* This function allows you to quickly set the same property across all children of this Group to a new value.
* The operation parameter controls how the new value is assigned to the property, from simple replacement to addition and multiplication.
*
* @method Phaser.Group#setAll
* @param {string} key - The property, as a string, to be set. For example: 'body.velocity.x'
* @param {*} value - The value that will be set.
* @param {boolean} [checkAlive=false] - If set then only children with alive=true will be updated.
* @param {boolean} [checkVisible=false] - If set then only children with visible=true will be updated.
* @param {number} [operation=0] - Controls how the value is assigned. A value of 0 replaces the value with the new one. A value of 1 adds it, 2 subtracts it, 3 multiplies it and 4 divides it.
*/
setAll: function (key, value, checkAlive, checkVisible, operation) {
key = key.split('.');
checkAlive = checkAlive || false;
checkVisible = checkVisible || false;
if (typeof checkAlive === 'undefined') { checkAlive = false; }
if (typeof checkVisible === 'undefined') { checkVisible = false; }
operation = operation || 0;
if (this._container.children.length > 0 && this._container.first._iNext)
@@ -352,33 +524,82 @@ Phaser.Group.prototype = {
},
addAll: function (key, value, checkAlive, checkVisible) {
/**
* Adds the amount to the given property on all children in this Group.
* Group.addAll('x', 10) will add 10 to the child.x value.
*
* @method Phaser.Group#addAll
* @param {string} property - The property to increment, for example 'body.velocity.x' or 'angle'.
* @param {number} amount - The amount to increment the property by. If child.x = 10 then addAll('x', 40) would make child.x = 50.
* @param {boolean} checkAlive - If true the property will only be changed if the child is alive.
* @param {boolean} checkVisible - If true the property will only be changed if the child is visible.
*/
addAll: function (property, amount, checkAlive, checkVisible) {
this.setAll(key, value, checkAlive, checkVisible, 1);
this.setAll(property, amount, checkAlive, checkVisible, 1);
},
subAll: function (key, value, checkAlive, checkVisible) {
/**
* Subtracts the amount from the given property on all children in this Group.
* Group.subAll('x', 10) will minus 10 from the child.x value.
*
* @method Phaser.Group#subAll
* @param {string} property - The property to decrement, for example 'body.velocity.x' or 'angle'.
* @param {number} amount - The amount to subtract from the property. If child.x = 50 then subAll('x', 40) would make child.x = 10.
* @param {boolean} checkAlive - If true the property will only be changed if the child is alive.
* @param {boolean} checkVisible - If true the property will only be changed if the child is visible.
*/
subAll: function (property, amount, checkAlive, checkVisible) {
this.setAll(key, value, checkAlive, checkVisible, 2);
this.setAll(property, amount, checkAlive, checkVisible, 2);
},
multiplyAll: function (key, value, checkAlive, checkVisible) {
/**
* Multiplies the given property by the amount on all children in this Group.
* Group.multiplyAll('x', 2) will x2 the child.x value.
*
* @method Phaser.Group#multiplyAll
* @param {string} property - The property to multiply, for example 'body.velocity.x' or 'angle'.
* @param {number} amount - The amount to multiply the property by. If child.x = 10 then multiplyAll('x', 2) would make child.x = 20.
* @param {boolean} checkAlive - If true the property will only be changed if the child is alive.
* @param {boolean} checkVisible - If true the property will only be changed if the child is visible.
*/
multiplyAll: function (property, amount, checkAlive, checkVisible) {
this.setAll(key, value, checkAlive, checkVisible, 3);
this.setAll(property, amount, checkAlive, checkVisible, 3);
},
divideAll: function (key, value, checkAlive, checkVisible) {
/**
* Divides the given property by the amount on all children in this Group.
* Group.divideAll('x', 2) will half the child.x value.
*
* @method Phaser.Group#divideAll
* @param {string} property - The property to divide, for example 'body.velocity.x' or 'angle'.
* @param {number} amount - The amount to divide the property by. If child.x = 100 then divideAll('x', 2) would make child.x = 50.
* @param {boolean} checkAlive - If true the property will only be changed if the child is alive.
* @param {boolean} checkVisible - If true the property will only be changed if the child is visible.
*/
divideAll: function (property, amount, checkAlive, checkVisible) {
this.setAll(key, value, checkAlive, checkVisible, 4);
this.setAll(property, amount, checkAlive, checkVisible, 4);
},
callAllExists: function (callback, callbackContext, existsValue) {
/**
* Calls a function on all of the children that have exists=true in this Group.
* After the existsValue parameter you can add as many parameters as you like, which will all be passed to the child callback.
*
* @method Phaser.Group#callAllExists
* @param {function} callback - The function that exists on the children that will be called.
* @param {boolean} existsValue - Only children with exists=existsValue will be called.
* @param {...*} parameter - Additional parameters that will be passed to the callback.
*/
callAllExists: function (callback, existsValue) {
var args = Array.prototype.splice.call(arguments, 3);
var args = Array.prototype.splice.call(arguments, 2);
if (this._container.children.length > 0 && this._container.first._iNext)
{
@@ -401,12 +622,15 @@ Phaser.Group.prototype = {
/**
* Calls a function on all of the children regardless if they are dead or alive (see callAllExists if you need control over that)
* You must pass the context in which the callback is applied.
* After the context you can add as many parameters as you like, which will all be passed to the child.
* After the callback parameter you can add as many extra parameters as you like, which will all be passed to the child.
*
* @method Phaser.Group#callAll
* @param {function} callback - The function that exists on the children that will be called.
* @param {...*} parameter - Additional parameters that will be passed to the callback.
*/
callAll: function (callback, callbackContext) {
callAll: function (callback) {
var args = Array.prototype.splice.call(arguments, 2);
var args = Array.prototype.splice.call(arguments, 1);
if (this._container.children.length > 0 && this._container.first._iNext)
{
@@ -427,9 +651,25 @@ Phaser.Group.prototype = {
},
/**
* Allows you to call your own function on each member of this Group. You must pass the callback and context in which it will run.
* After the checkExists parameter you can add as many parameters as you like, which will all be passed to the callback along with the child.
* For example: Group.forEach(awardBonusGold, this, true, 100, 500)
*
* @method Phaser.Group#forEach
* @param {function} callback - The function that will be called. Each child of the Group will be passed to it as its first parameter.
* @param {Object} callbackContext - The context in which the function should be called (usually 'this').
* @param {boolean} checkExists - If set only children with exists=true will be passed to the callback, otherwise all children will be passed.
*/
forEach: function (callback, callbackContext, checkExists) {
if (typeof checkExists == 'undefined') { checkExists = false; }
if (typeof checkExists === 'undefined')
{
checkExists = false;
}
var args = Array.prototype.splice.call(arguments, 3);
args.unshift(null);
if (this._container.children.length > 0 && this._container.first._iNext)
{
@@ -439,7 +679,8 @@ Phaser.Group.prototype = {
{
if (checkExists == false || (checkExists && currentNode.exists))
{
callback.call(callbackContext, currentNode);
args[0] = currentNode;
callback.apply(callbackContext, args);
}
currentNode = currentNode._iNext;
@@ -450,8 +691,20 @@ Phaser.Group.prototype = {
},
/**
* Allows you to call your own function on each alive member of this Group (where child.alive=true). You must pass the callback and context in which it will run.
* You can add as many parameters as you like, which will all be passed to the callback along with the child.
* For example: Group.forEachAlive(causeDamage, this, 500)
*
* @method Phaser.Group#forEachAlive
* @param {function} callback - The function that will be called. Each child of the Group will be passed to it as its first parameter.
* @param {Object} callbackContext - The context in which the function should be called (usually 'this').
*/
forEachAlive: function (callback, callbackContext) {
var args = Array.prototype.splice.call(arguments, 2);
args.unshift(null);
if (this._container.children.length > 0 && this._container.first._iNext)
{
var currentNode = this._container.first._iNext;
@@ -460,7 +713,8 @@ Phaser.Group.prototype = {
{
if (currentNode.alive)
{
callback.call(callbackContext, currentNode);
args[0] = currentNode;
callback.apply(callbackContext, args);
}
currentNode = currentNode._iNext;
@@ -471,8 +725,20 @@ Phaser.Group.prototype = {
},
/**
* Allows you to call your own function on each dead member of this Group (where alive=false). You must pass the callback and context in which it will run.
* You can add as many parameters as you like, which will all be passed to the callback along with the child.
* For example: Group.forEachDead(bringToLife, this)
*
* @method Phaser.Group#forEachDead
* @param {function} callback - The function that will be called. Each child of the Group will be passed to it as its first parameter.
* @param {Object} callbackContext - The context in which the function should be called (usually 'this').
*/
forEachDead: function (callback, callbackContext) {
var args = Array.prototype.splice.call(arguments, 2);
args.unshift(null);
if (this._container.children.length > 0 && this._container.first._iNext)
{
var currentNode = this._container.first._iNext;
@@ -481,7 +747,8 @@ Phaser.Group.prototype = {
{
if (currentNode.alive == false)
{
callback.call(callbackContext, currentNode);
args[0] = currentNode;
callback.apply(callbackContext, args);
}
currentNode = currentNode._iNext;
@@ -492,8 +759,10 @@ Phaser.Group.prototype = {
},
/**
* Call this function to retrieve the first object with exists == (the given state) in the group.
* Call this function to retrieve the first object with exists == (the given state) in the Group.
*
* @method Phaser.Group#getFirstExists
* @param {boolean} state - True or false.
* @return {Any} The first child, or null if none found.
*/
getFirstExists: function (state) {
@@ -525,8 +794,9 @@ Phaser.Group.prototype = {
/**
* Call this function to retrieve the first object with alive == true in the group.
* This is handy for checking if everything's wiped out, or choosing a squad leader, etc.
* This is handy for checking if everything has been wiped out, or choosing a squad leader, etc.
*
* @method Phaser.Group#getFirstAlive
* @return {Any} The first alive child, or null if none found.
*/
getFirstAlive: function () {
@@ -553,8 +823,9 @@ Phaser.Group.prototype = {
/**
* Call this function to retrieve the first object with alive == false in the group.
* This is handy for checking if everything's wiped out, or choosing a squad leader, etc.
* This is handy for checking if everything has been wiped out, or choosing a squad leader, etc.
*
* @method Phaser.Group#getFirstDead
* @return {Any} The first dead child, or null if none found.
*/
getFirstDead: function () {
@@ -582,11 +853,12 @@ Phaser.Group.prototype = {
/**
* Call this function to find out how many members of the group are alive.
*
* @method Phaser.Group#countLiving
* @return {number} The number of children flagged as alive. Returns -1 if Group is empty.
*/
countLiving: function () {
var total = -1;
var total = 0;
if (this._container.children.length > 0 && this._container.first._iNext)
{
@@ -603,6 +875,10 @@ Phaser.Group.prototype = {
}
while (currentNode != this._container.last._iNext);
}
else
{
total = -1;
}
return total;
@@ -611,11 +887,12 @@ Phaser.Group.prototype = {
/**
* Call this function to find out how many members of the group are dead.
*
* @method Phaser.Group#countDead
* @return {number} The number of children flagged as dead. Returns -1 if Group is empty.
*/
countDead: function () {
var total = -1;
var total = 0;
if (this._container.children.length > 0 && this._container.first._iNext)
{
@@ -632,6 +909,10 @@ Phaser.Group.prototype = {
}
while (currentNode != this._container.last._iNext);
}
else
{
total = -1;
}
return total;
@@ -640,9 +921,9 @@ Phaser.Group.prototype = {
/**
* Returns a member at random from the group.
*
* @param {number} startIndex Optional offset off the front of the array. Default value is 0, or the beginning of the array.
* @param {number} length Optional restriction on the number of values you want to randomly select from.
*
* @method Phaser.Group#getRandom
* @param {number} startIndex - Optional offset off the front of the array. Default value is 0, or the beginning of the array.
* @param {number} length - Optional restriction on the number of values you want to randomly select from.
* @return {Any} A random child of this Group.
*/
getRandom: function (startIndex, length) {
@@ -659,14 +940,31 @@ Phaser.Group.prototype = {
},
/**
* Removes the given child from this Group and sets its group property to null.
*
* @method Phaser.Group#remove
* @param {Any} child - The child to remove.
*/
remove: function (child) {
child.events.onRemovedFromGroup.dispatch(child, this);
if (child.events)
{
child.events.onRemovedFromGroup.dispatch(child, this);
}
this._container.removeChild(child);
child.group = null;
},
/**
* Removes all children from this Group, setting all group properties to null.
* The Group container remains on the display list.
*
* @method Phaser.Group#removeAll
*/
removeAll: function () {
if (this._container.children.length == 0)
@@ -686,6 +984,13 @@ Phaser.Group.prototype = {
},
/**
* Removes all children from this Group whos index falls beteen the given startIndex and endIndex values.
*
* @method Phaser.Group#removeBetween
* @param {number} startIndex - The index to start removing children from.
* @param {number} endIndex - The index to stop removing children from. Must be higher than startIndex and less than the length of the Group.
*/
removeBetween: function (startIndex, endIndex) {
if (this._container.children.length == 0)
@@ -707,6 +1012,11 @@ Phaser.Group.prototype = {
},
/**
* Destroys this Group. Removes all children, then removes the container from the display list and nulls references.
*
* @method Phaser.Group#destroy
*/
destroy: function () {
this.removeAll();
@@ -721,6 +1031,12 @@ Phaser.Group.prototype = {
},
/**
* Dumps out a list of Group children and their index positions to the browser console. Useful for group debugging.
*
* @method Phaser.Group#dump
* @param {boolean} [full=false] - If full the dump will include the entire display list, start from the Stage. Otherwise it will only include this container.
*/
dump: function (full) {
if (typeof full == 'undefined')
@@ -807,6 +1123,24 @@ Phaser.Group.prototype = {
};
/**
* @name Phaser.Group#total
* @property {number} total - The total number of children in this Group, regardless of their alive state.
* @readonly
*/
Object.defineProperty(Phaser.Group.prototype, "total", {
get: function () {
return this._container.children.length;
}
});
/**
* @name Phaser.Group#length
* @property {number} length - The number of children in this Group.
* @readonly
*/
Object.defineProperty(Phaser.Group.prototype, "length", {
get: function () {
@@ -815,6 +1149,12 @@ Object.defineProperty(Phaser.Group.prototype, "length", {
});
/**
* The x coordinate of the Group container. You can adjust the Group container itself by modifying its coordinates.
* This will have no impact on the x/y coordinates of its children, but it will update their worldTransform and on-screen position.
* @name Phaser.Group#x
* @property {number} x - The x coordinate of the Group container.
*/
Object.defineProperty(Phaser.Group.prototype, "x", {
get: function () {
@@ -827,6 +1167,12 @@ Object.defineProperty(Phaser.Group.prototype, "x", {
});
/**
* The y coordinate of the Group container. You can adjust the Group container itself by modifying its coordinates.
* This will have no impact on the x/y coordinates of its children, but it will update their worldTransform and on-screen position.
* @name Phaser.Group#y
* @property {number} y - The y coordinate of the Group container.
*/
Object.defineProperty(Phaser.Group.prototype, "y", {
get: function () {
@@ -839,6 +1185,12 @@ Object.defineProperty(Phaser.Group.prototype, "y", {
});
/**
* The angle of rotation of the Group container. This will adjust the Group container itself by modifying its rotation.
* This will have no impact on the rotation value of its children, but it will update their worldTransform and on-screen position.
* @name Phaser.Group#angle
* @property {number} angle - The angle of rotation given in degrees, where 0 degrees = to the right.
*/
Object.defineProperty(Phaser.Group.prototype, "angle", {
get: function() {
@@ -851,6 +1203,12 @@ Object.defineProperty(Phaser.Group.prototype, "angle", {
});
/**
* The angle of rotation of the Group container. This will adjust the Group container itself by modifying its rotation.
* This will have no impact on the rotation value of its children, but it will update their worldTransform and on-screen position.
* @name Phaser.Group#rotation
* @property {number} rotation - The angle of rotation given in radians.
*/
Object.defineProperty(Phaser.Group.prototype, "rotation", {
get: function () {
@@ -863,6 +1221,10 @@ Object.defineProperty(Phaser.Group.prototype, "rotation", {
});
/**
* @name Phaser.Group#visible
* @property {boolean} visible - The visible state of the Group. Non-visible Groups and all of their children are not rendered.
*/
Object.defineProperty(Phaser.Group.prototype, "visible", {
get: function () {
@@ -874,3 +1236,19 @@ Object.defineProperty(Phaser.Group.prototype, "visible", {
}
});
/**
* @name Phaser.Group#alpha
* @property {number} alpha - The alpha value of the Group container.
*/
Object.defineProperty(Phaser.Group.prototype, "alpha", {
get: function () {
return this._container.alpha;
},
set: function (value) {
this._container.alpha = value;
}
});
+86 -102
View File
@@ -1,16 +1,58 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* A basic linked list data structure.
*
* @class Phaser.LinkedList
* @constructor
*/
Phaser.LinkedList = function () {
/**
* @property {object} next - Next element in the list.
* @default
*/
this.next = null;
/**
* @property {object} prev - Previous element in the list.
* @default
*/
this.prev = null;
/**
* @property {object} first - First element in the list.
* @default
*/
this.first = null;
/**
* @property {object} last - Last element in the list.
* @default
*/
this.last = null;
/**
* @property {object} game - Number of elements in the list.
* @default
*/
this.total = 0;
};
Phaser.LinkedList.prototype = {
/**
* Adds a new element to this linked list.
*
* @method Phaser.LinkedList#add
* @param {object} child - The element to add to this list. Can be a Phaser.Sprite or any other object you need to quickly iterate through.
* @return {object} The child that was added.
*/
add: function (child) {
// If the list is empty
@@ -21,7 +63,7 @@ Phaser.LinkedList.prototype = {
this.next = child;
child.prev = this;
this.total++;
return;
return child;
}
// Get gets appended to the end of the list, regardless of anything, and it won't have any children of its own (non-nested list)
@@ -37,40 +79,55 @@ Phaser.LinkedList.prototype = {
},
/**
* Removes the given element from this linked list if it exists.
*
* @method Phaser.LinkedList#remove
* @param {object} child - The child to be removed from the list.
*/
remove: function (child) {
// If the list is empty
if (this.first == null && this.last == null)
if (child == this.first)
{
return;
}
// It was 'first', make 'first' point to first.next
this.first = this.first.next;
}
else if (child == this.last)
{
// It was 'last', make 'last' point to last.prev
this.last = this.last.prev;
}
if (child.prev)
{
// make child.prev.next point to childs.next instead of child
child.prev.next = child.next;
}
if (child.next)
{
// make child.next.prev point to child.prev instead of child
child.next.prev = child.prev;
}
child.next = child.prev = null;
if (this.first == null )
{
this.last = null;
}
this.total--;
// The only node?
if (this.first == child && this.last == child)
{
this.first = null;
this.last = null;
this.next = null;
child.next = null;
child.prev = null;
return;
}
var childPrev = child.prev;
// Tail node?
if (child.next)
{
// Has another node after it?
child.next.prev = child.prev;
}
childPrev.next = child.next;
},
/**
* Calls a function on all members of this list, using the member as the context for the callback.
* The function must exist on the member.
*
* @method Phaser.LinkedList#callAll
* @param {function} callback - The function to call.
*/
callAll: function (callback) {
if (!this.first || !this.last)
@@ -92,79 +149,6 @@ Phaser.LinkedList.prototype = {
}
while(entity != this.last.next)
},
dump: function () {
var spacing = 20;
var output = "\n" + Phaser.Utils.pad('Node', spacing) + "|" + Phaser.Utils.pad('Next', spacing) + "|" + Phaser.Utils.pad('Previous', spacing) + "|" + Phaser.Utils.pad('First', spacing) + "|" + Phaser.Utils.pad('Last', spacing);
console.log(output);
var output = Phaser.Utils.pad('----------', spacing) + "|" + Phaser.Utils.pad('----------', spacing) + "|" + Phaser.Utils.pad('----------', spacing) + "|" + Phaser.Utils.pad('----------', spacing) + "|" + Phaser.Utils.pad('----------', spacing);
console.log(output);
var entity = this;
var testObject = entity.last.next;
entity = entity.first;
do
{
var name = entity.sprite.name || '*';
var nameNext = '-';
var namePrev = '-';
var nameFirst = '-';
var nameLast = '-';
if (entity.next)
{
nameNext = entity.next.sprite.name;
}
if (entity.prev)
{
namePrev = entity.prev.sprite.name;
}
if (entity.first)
{
nameFirst = entity.first.sprite.name;
}
if (entity.last)
{
nameLast = entity.last.sprite.name;
}
if (typeof nameNext === 'undefined')
{
nameNext = '-';
}
if (typeof namePrev === 'undefined')
{
namePrev = '-';
}
if (typeof nameFirst === 'undefined')
{
nameFirst = '-';
}
if (typeof nameLast === 'undefined')
{
nameLast = '-';
}
var output = Phaser.Utils.pad(name, spacing) + "|" + Phaser.Utils.pad(nameNext, spacing) + "|" + Phaser.Utils.pad(namePrev, spacing) + "|" + Phaser.Utils.pad(nameFirst, spacing) + "|" + Phaser.Utils.pad(nameLast, spacing);
console.log(output);
entity = entity.next;
}
while(entity != testObject)
}
}
};
+56 -4
View File
@@ -1,19 +1,66 @@
/**
* Phaser - Plugin
*
* This is a base Plugin template to use for any Phaser plugin development
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* This is a base Plugin template to use for any Phaser plugin development.
*
* @class Phaser.Plugin
* @classdesc Phaser - Plugin
* @constructor
* @param {Phaser.Game} game - A reference to the currently running game.
* @param {Any} parent - The object that owns this plugin, usually Phaser.PluginManager.
*/
Phaser.Plugin = function (game, parent) {
if (typeof parent === 'undefined') { parent = null; }
/**
* @property {Phaser.Game} game - A reference to the currently running game.
*/
this.game = game;
/**
* @property {Any} parent - The parent of this plugin. If added to the PluginManager the parent will be set to that, otherwise it will be null.
*/
this.parent = parent;
/**
* @property {boolean} active - A Plugin with active=true has its preUpdate and update methods called by the parent, otherwise they are skipped.
* @default
*/
this.active = false;
/**
* @property {boolean} visible - A Plugin with visible=true has its render and postRender methods called by the parent, otherwise they are skipped.
* @default
*/
this.visible = false;
/**
* @property {boolean} hasPreUpdate - A flag to indicate if this plugin has a preUpdate method.
* @default
*/
this.hasPreUpdate = false;
/**
* @property {boolean} hasUpdate - A flag to indicate if this plugin has an update method.
* @default
*/
this.hasUpdate = false;
/**
* @property {boolean} hasRender - A flag to indicate if this plugin has a render method.
* @default
*/
this.hasRender = false;
/**
* @property {boolean} hasPostRender - A flag to indicate if this plugin has a postRender method.
* @default
*/
this.hasPostRender = false;
};
@@ -21,8 +68,9 @@ Phaser.Plugin = function (game, parent) {
Phaser.Plugin.prototype = {
/**
* Pre-update is called at the start of the update cycle, before any other updates have taken place (including Physics).
* Pre-update is called at the very start of the update cycle, before any other subsystems have been updated (including Physics).
* It is only called if active is set to true.
* @method Phaser.Plugin#preUpdate
*/
preUpdate: function () {
},
@@ -30,6 +78,7 @@ Phaser.Plugin.prototype = {
/**
* Update is called after all the core subsystems (Input, Tweens, Sound, etc) and the State have updated, but before the render.
* It is only called if active is set to true.
* @method Phaser.Plugin#update
*/
update: function () {
},
@@ -37,6 +86,7 @@ Phaser.Plugin.prototype = {
/**
* Render is called right after the Game Renderer completes, but before the State.render.
* It is only called if visible is set to true.
* @method Phaser.Plugin#render
*/
render: function () {
},
@@ -44,12 +94,14 @@ Phaser.Plugin.prototype = {
/**
* Post-render is called after the Game Renderer and State.render have run.
* It is only called if visible is set to true.
* @method Phaser.Plugin#postRender
*/
postRender: function () {
},
/**
* Clear down this Plugin and null out references
* @method Phaser.Plugin#destroy
*/
destroy: function () {
+68 -5
View File
@@ -1,14 +1,41 @@
/**
* Phaser - PluginManager
*
* TODO: We can optimise this a lot by using separate hashes per function (update, render, etc)
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Description.
*
* @class Phaser.PluginManager
* @classdesc Phaser - PluginManager
* @constructor
* @param {Phaser.Game} game - A reference to the currently running game.
* @param {Description} parent - Description.
*/
Phaser.PluginManager = function(game, parent) {
/**
* @property {Phaser.Game} game - A reference to the currently running game.
*/
this.game = game;
/**
* @property {Description} _parent - Description.
* @private
*/
this._parent = parent;
/**
* @property {array} plugins - Description.
*/
this.plugins = [];
/**
* @property {array} _pluginsLength - Description.
* @private
* @default
*/
this._pluginsLength = 0;
};
@@ -17,8 +44,10 @@ Phaser.PluginManager.prototype = {
/**
* Add a new Plugin to the PluginManager.
* The plugins game and parent reference are set to this game and pluginmanager parent.
* @type {Phaser.Plugin}
* The plugin's game and parent reference are set to this game and pluginmanager parent.
* @method Phaser.PluginManager#add
* @param {Phaser.Plugin} plugin - Description.
* @return {Phaser.Plugin} Description.
*/
add: function (plugin) {
@@ -82,6 +111,11 @@ Phaser.PluginManager.prototype = {
}
},
/**
* Remove a Plugin from the PluginManager.
* @method Phaser.PluginManager#remove
* @param {Phaser.Plugin} plugin - The plugin to be removed.
*/
remove: function (plugin) {
// TODO
@@ -89,6 +123,12 @@ Phaser.PluginManager.prototype = {
},
/**
* Pre-update is called at the very start of the update cycle, before any other subsystems have been updated (including Physics).
* It only calls plugins who have active=true.
*
* @method Phaser.PluginManager#preUpdate
*/
preUpdate: function () {
if (this._pluginsLength == 0)
@@ -106,6 +146,12 @@ Phaser.PluginManager.prototype = {
},
/**
* Update is called after all the core subsystems (Input, Tweens, Sound, etc) and the State have updated, but before the render.
* It only calls plugins who have active=true.
*
* @method Phaser.PluginManager#update
*/
update: function () {
if (this._pluginsLength == 0)
@@ -123,6 +169,12 @@ Phaser.PluginManager.prototype = {
},
/**
* Render is called right after the Game Renderer completes, but before the State.render.
* It only calls plugins who have visible=true.
*
* @method Phaser.PluginManager#render
*/
render: function () {
if (this._pluginsLength == 0)
@@ -140,6 +192,12 @@ Phaser.PluginManager.prototype = {
},
/**
* Post-render is called after the Game Renderer and State.render have run.
* It only calls plugins who have visible=true.
*
* @method Phaser.PluginManager#postRender
*/
postRender: function () {
if (this._pluginsLength == 0)
@@ -157,6 +215,11 @@ Phaser.PluginManager.prototype = {
},
/**
* Clear down this PluginManager and null out references
*
* @method Phaser.PluginManager#destroy
*/
destroy: function () {
this.plugins.length = 0;
+114 -64
View File
@@ -1,23 +1,35 @@
/**
* Phaser.Signal
*
* A Signal is used for object communication via a custom broadcaster instead of Events.
*
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* @class Phaser.Signal
* @classdesc A Signal is used for object communication via a custom broadcaster instead of Events.
* @author Miller Medeiros http://millermedeiros.github.com/js-signals/
* @constructor
*/
Phaser.Signal = function () {
/**
* @type Array.<Phaser.SignalBinding>
* @private
*/
* @property {Array.<Phaser.SignalBinding>} _bindings - Description.
* @private
*/
this._bindings = [];
/**
* @property {Description} _prevParams - Description.
* @private
*/
this._prevParams = null;
// enforce dispatch to aways work on same context (#47)
var self = this;
/**
* @property {Description} dispatch - Description.
*/
this.dispatch = function(){
Phaser.Signal.prototype.dispatch.apply(self, arguments);
};
@@ -27,26 +39,33 @@ Phaser.Signal = function () {
Phaser.Signal.prototype = {
/**
* If Signal should keep record of previously dispatched parameters and
* automatically execute listener during `add()`/`addOnce()` if Signal was
* already dispatched before.
* @type boolean
*/
* If Signal should keep record of previously dispatched parameters and
* automatically execute listener during `add()`/`addOnce()` if Signal was
* already dispatched before.
* @property {boolean} memorize
*/
memorize: false,
/**
* @type boolean
* @private
*/
* @property {boolean} _shouldPropagate
* @private
*/
_shouldPropagate: true,
/**
* If Signal is active and should broadcast events.
* <p><strong>IMPORTANT:</strong> Setting this property during a dispatch will only affect the next dispatch, if you want to stop the propagation of a signal use `halt()` instead.</p>
* @type boolean
*/
* If Signal is active and should broadcast events.
* <p><strong>IMPORTANT:</strong> Setting this property during a dispatch will only affect the next dispatch, if you want to stop the propagation of a signal use `halt()` instead.</p>
* @property {boolean} active
* @default
*/
active: true,
/**
* @method Phaser.Signal#validateListener
* @param {function} listener - Signal handler function.
* @param {Description} fnName - Description.
* @private
*/
validateListener: function (listener, fnName) {
if (typeof listener !== 'function') {
throw new Error( 'listener is a required param of {fn}() and should be a Function.'.replace('{fn}', fnName) );
@@ -54,11 +73,12 @@ Phaser.Signal.prototype = {
},
/**
* @param {Function} listener
* @param {boolean} isOnce
* @param {Object} [listenerContext]
* @param {Number} [priority]
* @return {Phaser.SignalBinding}
* @method Phaser.Signal#_registerListener
* @param {function} listener - Signal handler function.
* @param {boolean} isOnce - Description.
* @param {object} [listenerContext] - Description.
* @param {number} [priority] - The priority level of the event listener. Listeners with higher priority will be executed before listeners with lower priority. Listeners with same priority level will be executed at the same order as they were added. (default = 0).
* @return {Phaser.SignalBinding} An Object representing the binding between the Signal and listener.
* @private
*/
_registerListener: function (listener, isOnce, listenerContext, priority) {
@@ -84,7 +104,8 @@ Phaser.Signal.prototype = {
},
/**
* @param {Phaser.SignalBinding} binding
* @method Phaser.Signal#_addBinding
* @param {Phaser.SignalBinding} binding - An Object representing the binding between the Signal and listener.
* @private
*/
_addBinding: function (binding) {
@@ -95,8 +116,9 @@ Phaser.Signal.prototype = {
},
/**
* @param {Function} listener
* @return {number}
* @method Phaser.Signal#_indexOfListener
* @param {function} listener - Signal handler function.
* @return {number} Description.
* @private
*/
_indexOfListener: function (listener, context) {
@@ -113,9 +135,11 @@ Phaser.Signal.prototype = {
/**
* Check if listener was attached to Signal.
* @param {Function} listener
* @param {Object} [context]
* @return {boolean} if Signal has the specified listener.
*
* @method Phaser.Signal#has
* @param {Function} listener - Signal handler function.
* @param {Object} [context] - Context on which listener will be executed (object that should represent the `this` variable inside listener function).
* @return {boolean} If Signal has the specified listener.
*/
has: function (listener, context) {
return this._indexOfListener(listener, context) !== -1;
@@ -123,9 +147,11 @@ Phaser.Signal.prototype = {
/**
* Add a listener to the signal.
* @param {Function} listener Signal handler function.
* @param {Object} [listenerContext] Context on which listener will be executed (object that should represent the `this` variable inside listener function).
* @param {Number} [priority] The priority level of the event listener. Listeners with higher priority will be executed before listeners with lower priority. Listeners with same priority level will be executed at the same order as they were added. (default = 0)
*
* @method Phaser.Signal#add
* @param {function} listener - Signal handler function.
* @param {object} [listenerContext] Context on which listener will be executed (object that should represent the `this` variable inside listener function).
* @param {number} [priority] The priority level of the event listener. Listeners with higher priority will be executed before listeners with lower priority. Listeners with same priority level will be executed at the same order as they were added. (default = 0).
* @return {Phaser.SignalBinding} An Object representing the binding between the Signal and listener.
*/
add: function (listener, listenerContext, priority) {
@@ -134,37 +160,48 @@ Phaser.Signal.prototype = {
},
/**
* Add listener to the signal that should be removed after first execution (will be executed only once).
* @param {Function} listener Signal handler function.
* @param {Object} [listenerContext] Context on which listener will be executed (object that should represent the `this` variable inside listener function).
* @param {Number} [priority] The priority level of the event listener. Listeners with higher priority will be executed before listeners with lower priority. Listeners with same priority level will be executed at the same order as they were added. (default = 0)
* @return {Phaser.SignalBinding} An Object representing the binding between the Signal and listener.
*/
* Add listener to the signal that should be removed after first execution (will be executed only once).
*
* @method Phaser.Signal#addOnce
* @param {function} listener Signal handler function.
* @param {object} [listenerContext] Context on which listener will be executed (object that should represent the `this` variable inside listener function).
* @param {number} [priority] The priority level of the event listener. Listeners with higher priority will be executed before listeners with lower priority. Listeners with same priority level will be executed at the same order as they were added. (default = 0)
* @return {Phaser.SignalBinding} An Object representing the binding between the Signal and listener.
*/
addOnce: function (listener, listenerContext, priority) {
this.validateListener(listener, 'addOnce');
return this._registerListener(listener, true, listenerContext, priority);
},
/**
* Remove a single listener from the dispatch queue.
* @param {Function} listener Handler function that should be removed.
* @param {Object} [context] Execution context (since you can add the same handler multiple times if executing in a different context).
* @return {Function} Listener handler function.
*/
* Remove a single listener from the dispatch queue.
*
* @method Phaser.Signal#remove
* @param {function} listener Handler function that should be removed.
* @param {object} [context] Execution context (since you can add the same handler multiple times if executing in a different context).
* @return {function} Listener handler function.
*/
remove: function (listener, context) {
this.validateListener(listener, 'remove');
var i = this._indexOfListener(listener, context);
if (i !== -1) {
if (i !== -1)
{
this._bindings[i]._destroy(); //no reason to a Phaser.SignalBinding exist if it isn't attached to a signal
this._bindings.splice(i, 1);
}
return listener;
},
/**
* Remove all listeners from the Signal.
*/
* Remove all listeners from the Signal.
*
* @method Phaser.Signal#removeAll
*/
removeAll: function () {
var n = this._bindings.length;
while (n--) {
@@ -174,25 +211,32 @@ Phaser.Signal.prototype = {
},
/**
* @return {number} Number of listeners attached to the Signal.
*/
* Gets the total number of listeneres attached to ths Signal.
*
* @method Phaser.Signal#getNumListeners
* @return {number} Number of listeners attached to the Signal.
*/
getNumListeners: function () {
return this._bindings.length;
},
/**
* Stop propagation of the event, blocking the dispatch to next listeners on the queue.
* <p><strong>IMPORTANT:</strong> should be called only during signal dispatch, calling it before/after dispatch won't affect signal broadcast.</p>
* @see Signal.prototype.disable
*/
* Stop propagation of the event, blocking the dispatch to next listeners on the queue.
* <p><strong>IMPORTANT:</strong> should be called only during signal dispatch, calling it before/after dispatch won't affect signal broadcast.</p>
* @see Signal.prototype.disable
*
* @method Phaser.Signal#halt
*/
halt: function () {
this._shouldPropagate = false;
},
/**
* Dispatch/Broadcast Signal to all listeners added to the queue.
* @param {...*} [params] Parameters that should be passed to each handler.
*/
* Dispatch/Broadcast Signal to all listeners added to the queue.
*
* @method Phaser.Signal#dispatch
* @param {any} [params] - Parameters that should be passed to each handler.
*/
dispatch: function (params) {
if (! this.active) {
return;
@@ -220,17 +264,21 @@ Phaser.Signal.prototype = {
},
/**
* Forget memorized arguments.
* @see Signal.memorize
*/
* Forget memorized arguments.
* @see Signal.memorize
*
* @method Phaser.Signal#forget
*/
forget: function(){
this._prevParams = null;
},
/**
* Remove all bindings from signal and destroy any reference to external objects (destroy Signal object).
* <p><strong>IMPORTANT:</strong> calling any method on the signal instance after calling dispose will throw errors.</p>
*/
* Remove all bindings from signal and destroy any reference to external objects (destroy Signal object).
* <p><strong>IMPORTANT:</strong> calling any method on the signal instance after calling dispose will throw errors.</p>
*
* @method Phaser.Signal#dispose
*/
dispose: function () {
this.removeAll();
delete this._bindings;
@@ -238,8 +286,10 @@ Phaser.Signal.prototype = {
},
/**
* @return {string} String representation of the object.
*/
*
* @method Phaser.Signal#toString
* @return {string} String representation of the object.
*/
toString: function () {
return '[Phaser.Signal active:'+ this.active +' numListeners:'+ this.getNumListeners() +']';
}
+67 -56
View File
@@ -1,3 +1,9 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Phaser.SignalBinding
*
@@ -5,52 +11,47 @@
* <br />- <strong>This is an internal constructor and shouldn't be called by regular users.</strong>
* <br />- inspired by Joa Ebert AS3 SignalBinding and Robert Penner's Slot classes.
*
* @class Phaser.SignalBinding
* @name SignalBinding
* @author Miller Medeiros http://millermedeiros.github.com/js-signals/
* @constructor
* @internal
* @name SignalBinding
* @param {Signal} signal Reference to Signal object that listener is currently bound to.
* @param {Function} listener Handler function bound to the signal.
* @param {boolean} isOnce If binding should be executed just once.
* @param {Object} [listenerContext] Context on which listener will be executed (object that should represent the `this` variable inside listener function).
* @param {Number} [priority] The priority level of the event listener. (default = 0).
* @inner
* @param {Signal} signal - Reference to Signal object that listener is currently bound to.
* @param {function} listener - Handler function bound to the signal.
* @param {boolean} isOnce - If binding should be executed just once.
* @param {object} [listenerContext] - Context on which listener will be executed (object that should represent the `this` variable inside listener function).
* @param {number} [priority] - The priority level of the event listener. (default = 0).
*/
Phaser.SignalBinding = function (signal, listener, isOnce, listenerContext, priority) {
/**
* Handler function bound to the signal.
* @type Function
* @private
*/
* @property {Phaser.Game} _listener - Handler function bound to the signal.
* @private
*/
this._listener = listener;
/**
* If binding should be executed just once.
* @type boolean
* @private
*/
* @property {boolean} _isOnce - If binding should be executed just once.
* @private
*/
this._isOnce = isOnce;
/**
* Context on which listener will be executed (object that should represent the `this` variable inside listener function).
* @memberOf SignalBinding.prototype
* @name context
* @type Object|undefined|null
*/
* @property {object|undefined|null} context - Context on which listener will be executed (object that should represent the `this` variable inside listener function).
* @memberof SignalBinding.prototype
*/
this.context = listenerContext;
/**
* Reference to Signal object that listener is currently bound to.
* @type Signal
* @private
*/
* @property {Signal} _signal - Reference to Signal object that listener is currently bound to.
* @private
*/
this._signal = signal;
/**
* Listener priority
* @type Number
* @private
*/
* @property {number} _priority - Listener priority.
* @private
*/
this._priority = priority || 0;
};
@@ -58,23 +59,26 @@ Phaser.SignalBinding = function (signal, listener, isOnce, listenerContext, prio
Phaser.SignalBinding.prototype = {
/**
* If binding is active and should be executed.
* @type boolean
*/
* If binding is active and should be executed.
* @property {boolean} active
* @default
*/
active: true,
/**
* Default parameters passed to listener during `Signal.dispatch` and `SignalBinding.execute`. (curried parameters)
* @type Array|null
*/
* Default parameters passed to listener during `Signal.dispatch` and `SignalBinding.execute` (curried parameters).
* @property {array|null} params
* @default
*/
params: null,
/**
* Call listener passing arbitrary parameters.
* <p>If binding was added using `Signal.addOnce()` it will be automatically removed from signal dispatch queue, this method is used internally for the signal dispatch.</p>
* @param {Array} [paramsArr] Array of parameters that should be passed to the listener
* @return {*} Value returned by the listener.
*/
* Call listener passing arbitrary parameters.
* <p>If binding was added using `Signal.addOnce()` it will be automatically removed from signal dispatch queue, this method is used internally for the signal dispatch.</p>
* @method Phaser.SignalBinding#execute
* @param {array} [paramsArr] - Array of parameters that should be passed to the listener.
* @return {Description} Value returned by the listener.
*/
execute: function (paramsArr) {
var handlerReturn, params;
@@ -95,46 +99,52 @@ Phaser.SignalBinding.prototype = {
},
/**
* Detach binding from signal.
* - alias to: mySignal.remove(myBinding.getListener());
* @return {Function|null} Handler function bound to the signal or `null` if binding was previously detached.
*/
* Detach binding from signal.
* <p>alias to: @see mySignal.remove(myBinding.getListener());
* @method Phaser.SignalBinding#detach
* @return {function|null} Handler function bound to the signal or `null` if binding was previously detached.
*/
detach: function () {
return this.isBound() ? this._signal.remove(this._listener, this.context) : null;
},
/**
* @return {Boolean} `true` if binding is still bound to the signal and have a listener.
*/
* @method Phaser.SignalBinding#isBound
* @return {boolean} True if binding is still bound to the signal and has a listener.
*/
isBound: function () {
return (!!this._signal && !!this._listener);
},
/**
* @return {boolean} If SignalBinding will only be executed once.
*/
* @method Phaser.SignalBinding#isOnce
* @return {boolean} If SignalBinding will only be executed once.
*/
isOnce: function () {
return this._isOnce;
},
/**
* @return {Function} Handler function bound to the signal.
*/
* @method Phaser.SignalBinding#getListener
* @return {Function} Handler function bound to the signal.
*/
getListener: function () {
return this._listener;
},
/**
* @return {Signal} Signal that listener is currently bound to.
*/
* @method Phaser.SignalBinding#getSignal
* @return {Signal} Signal that listener is currently bound to.
*/
getSignal: function () {
return this._signal;
},
/**
* Delete instance properties
* @private
*/
* @method Phaser.SignalBinding#_destroy
* Delete instance properties
* @private
*/
_destroy: function () {
delete this._signal;
delete this._listener;
@@ -142,8 +152,9 @@ Phaser.SignalBinding.prototype = {
},
/**
* @return {string} String representation of the object.
*/
* @method Phaser.SignalBinding#toString
* @return {string} String representation of the object.
*/
toString: function () {
return '[Phaser.SignalBinding isOnce:' + this._isOnce +', isBound:'+ this.isBound() +', active:' + this.active + ']';
}
+64 -60
View File
@@ -1,96 +1,86 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license https://github.com/photonstorm/phaser/blob/master/license.txt MIT License
* @module Phaser.Stage
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
*
* The Stage controls the canvas on which everything is displayed. It handles display within the browser,
* focus handling, game resizing, scaling and the pause, boot and orientation screens.
*
* @class Stage
* @class Phaser.Stage
* @constructor
* @param {Phaser.Game} game Game reference to the currently running game.
* @param {number} width Width of the canvas element
* @param {number} height Height of the canvas element
* @param {Phaser.Game} game - Game reference to the currently running game.
* @param {number} width - Width of the canvas element.
* @param {number} height - Height of the canvas element.
*/
Phaser.Stage = function (game, width, height) {
/**
* A reference to the currently running Game.
* @property game
* @public
* @type {Phaser.Game}
*/
/**
* @property {Phaser.Game} game - A reference to the currently running Game.
*/
this.game = game;
/**
* Background color of the stage (defaults to black). Set via the public backgroundColor property.
* @property _backgroundColor
* @private
* @type {string}
*/
* @property {string} game - Background color of the stage (defaults to black). Set via the public backgroundColor property.
* @private
* @default 'rgb(0,0,0)'
*/
this._backgroundColor = 'rgb(0,0,0)';
/**
* Get the offset values (for input and other things)
* @property offset
* @public
* @type {Phaser.Point}
*/
* @property {Phaser.Point} offset - Get the offset values (for input and other things).
*/
this.offset = new Phaser.Point;
/**
* reference to the newly created <canvas> element
* @property canvas
* @public
* @type {HTMLCanvasElement}
* @property {HTMLCanvasElement} canvas - Reference to the newly created &lt;canvas&gt; element.
*/
this.canvas = Phaser.Canvas.create(width, height);
this.canvas = Phaser.Canvas.create(width, height);
this.canvas.style['-webkit-full-screen'] = 'width: 100%; height: 100%';
/**
* The Pixi Stage which is hooked to the renderer
* @property _stage
* @property {PIXI.Stage} _stage - The Pixi Stage which is hooked to the renderer.
* @private
* @type {PIXI.Stage}
*/
this._stage = new PIXI.Stage(0x000000, false);
this._stage.name = '_stage_root';
/**
* The current scaleMode
* @property scaleMode
* @public
* @type {number}
*/
* @property {number} scaleMode - The current scaleMode.
*/
this.scaleMode = Phaser.StageScaleMode.NO_SCALE;
/**
* The scale of the current running game
* @property scale
* @public
* @type {Phaser.StageScaleMode}
* @property {Phaser.StageScaleMode} scale - The scale of the current running game.
*/
this.scale = new Phaser.StageScaleMode(this.game, width, height);
/**
* aspect ratio
* @property aspectRatio
* @public
* @type {number}
*/
* @property {number} aspectRatio - Aspect ratio.
*/
this.aspectRatio = width / height;
/**
* @property {number} _nextOffsetCheck - The time to run the next offset check.
* @private
*/
this._nextOffsetCheck = 0;
/**
* @property {number|false} checkOffsetInterval - The time (in ms) between which the stage should check to see if it has moved.
* @default
*/
this.checkOffsetInterval = 2500;
};
Phaser.Stage.prototype = {
/**
* Initialises the stage and adds the event listeners
* @method boot
* Initialises the stage and adds the event listeners.
* @method Phaser.Stage#boot
* @private
*/
boot: function () {
@@ -104,6 +94,9 @@ Phaser.Stage.prototype = {
return _this.visibilityChange(event);
}
Phaser.Canvas.setUserSelect(this.canvas, 'none');
Phaser.Canvas.setTouchAction(this.canvas, 'none');
document.addEventListener('visibilitychange', this._onChange, false);
document.addEventListener('webkitvisibilitychange', this._onChange, false);
document.addEventListener('pagehide', this._onChange, false);
@@ -112,12 +105,30 @@ Phaser.Stage.prototype = {
window.onblur = this._onChange;
window.onfocus = this._onChange;
},
/**
* Runs Stage processes that need periodic updates, such as the offset checks.
* @method Phaser.Stage#update
*/
update: function () {
if (this.checkOffsetInterval !== false)
{
if (this.game.time.now > this._nextOffsetCheck)
{
Phaser.Canvas.getOffset(this.canvas, this.offset);
this._nextOffsetCheck = this.game.time.now + this.checkOffsetInterval;
}
}
},
/**
* This method is called when the document visibility is changed.
* @method visibilityChange
* @param {Event} event Its type will be used to decide whether the game should be paused or not
* @method Phaser.Stage#visibilityChange
* @param {Event} event - Its type will be used to decide whether the game should be paused or not.
*/
visibilityChange: function (event) {
@@ -128,12 +139,10 @@ Phaser.Stage.prototype = {
if (event.type == 'pagehide' || event.type == 'blur' || document['hidden'] == true || document['webkitHidden'] == true)
{
// console.log('visibilityChange - hidden', event);
this.game.paused = true;
}
else
{
// console.log('visibilityChange - shown', event);
this.game.paused = false;
}
@@ -141,21 +150,16 @@ Phaser.Stage.prototype = {
};
/**
* @name Phaser.Stage#backgroundColor
* @property {number|string} paused - Gets and sets the background color of the stage. The color can be given as a number: 0xff0000 or a hex string: '#ff0000'
*/
Object.defineProperty(Phaser.Stage.prototype, "backgroundColor", {
/**
* @method backgroundColor
* @return {string} returns the background color of the stage
*/
get: function () {
return this._backgroundColor;
},
/**
* @method backgroundColor
* @param {string} the background color you want the stage to have
* @return {string} returns the background color of the stage
*/
set: function (color) {
this._backgroundColor = color;
+105 -8
View File
@@ -1,30 +1,100 @@
/**
* State
*
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* This is a base State class which can be extended if you are creating your own game.
* It provides quick access to common functions such as the camera, cache, input, match, sound and more.
*
* @package Phaser.State
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license https://github.com/photonstorm/phaser/blob/master/license.txt MIT License
* @class Phaser.State
* @constructor
*/
Phaser.State = function () {
/**
* @property {Phaser.Game} game - A reference to the currently running Game.
*/
this.game = null;
/**
* @property {Phaser.GameObjectFactory} add - Reference to the GameObjectFactory.
* @default
*/
this.add = null;
/**
* @property {Phaser.Physics.PhysicsManager} camera - A handy reference to world.camera.
* @default
*/
this.camera = null;
/**
* @property {Phaser.Cache} cache - Reference to the assets cache.
* @default
*/
this.cache = null;
/**
* @property {Phaser.Input} input - Reference to the input manager
* @default
*/
this.input = null;
/**
* @property {Phaser.Loader} load - Reference to the assets loader.
* @default
*/
this.load = null;
/**
* @property {Phaser.GameMath} math - Reference to the math helper.
* @default
*/
this.math = null;
/**
* @property {Phaser.SoundManager} sound - Reference to the sound manager.
* @default
*/
this.sound = null;
/**
* @property {Phaser.Stage} stage - Reference to the stage.
* @default
*/
this.stage = null;
/**
* @property {Phaser.TimeManager} time - Reference to game clock.
* @default
*/
this.time = null;
/**
* @property {Phaser.TweenManager} tweens - Reference to the tween manager.
* @default
*/
this.tweens = null;
/**
* @property {Phaser.World} world - Reference to the world.
* @default
*/
this.world = null;
/**
* @property {Description} add - Description.
* @default
*/
this.particles = null;
/**
* @property {Phaser.Physics.PhysicsManager} physics - Reference to the physics manager.
* @default
*/
this.physics = null;
};
@@ -34,37 +104,64 @@ Phaser.State.prototype = {
/**
* Override this method to add some load operations.
* If you need to use the loader, you may need to use them here.
*
* @method Phaser.State#preload
*/
preload: function () {
},
/**
* Put update logic here.
*
* @method Phaser.State#loadUpdate
*/
loadUpdate: function () {
},
/**
* Put render operations here.
*
* @method Phaser.State#loadRender
*/
loadRender: function () {
},
/**
* This method is called after the game engine successfully switches states.
* Feel free to add any setup code here.(Do not load anything here, override preload() instead)
* Feel free to add any setup code here (do not load anything here, override preload() instead).
*
* @method Phaser.State#create
*/
create: function () {
},
/**
* Put update logic here.
*
* @method Phaser.State#update
*/
update: function () {
},
/**
* Put render operations here.
*
* @method Phaser.State#render
*/
render: function () {
},
/**
* This method will be called when game paused.
*
* @method Phaser.State#paused
*/
paused: function () {
},
/**
* This method will be called when the state is destroyed
* This method will be called when the state is destroyed.
* @method Phaser.State#destroy
*/
destroy: function () {
}
+112 -34
View File
@@ -1,7 +1,29 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* The State Manager is responsible for loading, setting up and switching game states.
*
* @class Phaser.StateManager
* @constructor
* @param {Phaser.Game} game - A reference to the currently running game.
* @param {Phaser.State|Object} [pendingState=null] - A State object to seed the manager with.
*/
Phaser.StateManager = function (game, pendingState) {
/**
* A reference to the currently running game.
* @property {Phaser.Game} game.
*/
this.game = game;
/**
* Description.
* @property {Description} states.
*/
this.states = {};
if (pendingState !== null)
@@ -14,94 +36,102 @@ Phaser.StateManager = function (game, pendingState) {
Phaser.StateManager.prototype = {
/**
* @type {Phaser.Game}
* A reference to the currently running game.
* @property {Phaser.Game} game.
*/
game: null,
/**
* The state to be switched to in the next frame.
* @type {State}
* @property {State} _pendingState
* @private
*/
_pendingState: null,
/**
* Flag that sets if the State has been created or not.
* @type {Boolean}
* @property {boolean}_created
* @private
*/
_created: false,
/**
* The state to be switched to in the next frame.
* @type {Object}
* @property {Description} states
*/
states: {},
/**
* The current active State object (defaults to null)
* @type {String}
* The current active State object (defaults to null).
* @property {string} current
*/
current: '',
/**
* This will be called when the state is started (i.e. set as the current active state)
* @type {function}
* This will be called when the state is started (i.e. set as the current active state).
* @property {function} onInitCallback
*/
onInitCallback: null,
/**
* This will be called when init states. (loading assets...)
* @type {function}
* This will be called when init states (loading assets...).
* @property {function} onPreloadCallback
*/
onPreloadCallback: null,
/**
* This will be called when create states. (setup states...)
* @type {function}
* This will be called when create states (setup states...).
* @property {function} onCreateCallback
*/
onCreateCallback: null,
/**
* This will be called when State is updated, this doesn't happen during load (see onLoadUpdateCallback)
* @type {function}
* This will be called when State is updated, this doesn't happen during load (@see onLoadUpdateCallback).
* @property {function} onUpdateCallback
*/
onUpdateCallback: null,
/**
* This will be called when the State is rendered, this doesn't happen during load (see onLoadRenderCallback)
* @type {function}
* This will be called when the State is rendered, this doesn't happen during load (see onLoadRenderCallback).
* @property {function} onRenderCallback
*/
onRenderCallback: null,
/**
* This will be called before the State is rendered and before the stage is cleared
* @type {function}
* This will be called before the State is rendered and before the stage is cleared.
* @property {function} onPreRenderCallback
*/
onPreRenderCallback: null,
/**
* This will be called when the State is updated but only during the load process
* @type {function}
* This will be called when the State is updated but only during the load process.
* @property {function} onLoadUpdateCallback
*/
onLoadUpdateCallback: null,
/**
* This will be called when the State is rendered but only during the load process
* @type {function}
* This will be called when the State is rendered but only during the load process.
* @property {function} onLoadRenderCallback
*/
onLoadRenderCallback: null,
/**
* This will be called when states paused.
* @type {function}
* @property {function} onPausedCallback
*/
onPausedCallback: null,
/**
* This will be called when the state is shut down (i.e. swapped to another state)
* @type {function}
* This will be called when the state is shut down (i.e. swapped to another state).
* @property {function} onShutDownCallback
*/
onShutDownCallback: null,
/**
* Description.
* @method Phaser.StateManager#boot
* @private
*/
boot: function () {
// console.log('Phaser.StateManager.boot');
@@ -127,9 +157,10 @@ Phaser.StateManager.prototype = {
/**
* Add a new State.
* @param key {String} A unique key you use to reference this state, i.e. "MainMenu", "Level1".
* @param state {State} The state you want to switch to.
* @param autoStart {Boolean} Start the state immediately after creating it? (default true)
* @method Phaser.StateManager#add
* @param key {string} - A unique key you use to reference this state, i.e. "MainMenu", "Level1".
* @param state {State} - The state you want to switch to.
* @param autoStart {boolean} - Start the state immediately after creating it? (default true)
*/
add: function (key, state, autoStart) {
@@ -178,6 +209,11 @@ Phaser.StateManager.prototype = {
},
/**
* Delete the given state.
* @method Phaser.StateManager#remove
* @param {string} key - A unique key you use to reference this state, i.e. "MainMenu", "Level1".
*/
remove: function (key) {
if (this.current == key)
@@ -203,9 +239,10 @@ Phaser.StateManager.prototype = {
/**
* Start the given state
* @param key {String} The key of the state you want to start.
* @param [clearWorld] {bool} clear everything in the world? (Default to true)
* @param [clearCache] {bool} clear asset cache? (Default to false and ONLY available when clearWorld=true)
* @method Phaser.StateManager#start
* @param {string} key - The key of the state you want to start.
* @param {boolean} [clearWorld] - clear everything in the world? (Default to true)
* @param {boolean} [clearCache] - clear asset cache? (Default to false and ONLY available when clearWorld=true)
*/
start: function (key, clearWorld, clearCache) {
@@ -235,7 +272,9 @@ Phaser.StateManager.prototype = {
this.onShutDownCallback.call(this.callbackContext);
}
if (clearWorld) {
if (clearWorld)
{
this.game.tweens.removeAll();
this.game.world.destroy();
@@ -275,11 +314,21 @@ Phaser.StateManager.prototype = {
}
},
// Used by onInit and onShutdown when those functions don't exist on the state
/**
* Used by onInit and onShutdown when those functions don't exist on the state
* @method Phaser.StateManager#dummy
* @private
*/
dummy: function () {
},
/**
* Description.
* @method Phaser.StateManager#checkState
* @param {string} key - The key of the state you want to check.
* @return {boolean} Description.
*/
checkState: function (key) {
if (this.states[key])
@@ -312,6 +361,12 @@ Phaser.StateManager.prototype = {
},
/**
* Links game properties to the State given by the key.
* @method Phaser.StateManager#link
* @param {string} key - State key.
* @protected
*/
link: function (key) {
// console.log('linked');
@@ -333,6 +388,12 @@ Phaser.StateManager.prototype = {
},
/**
* Sets the current State. Should not be called directly (use StateManager.start)
* @method Phaser.StateManager#setCurrentState
* @param {string} key - State key.
* @protected
*/
setCurrentState: function (key) {
this.callbackContext = this.states[key];
@@ -361,6 +422,10 @@ Phaser.StateManager.prototype = {
},
/**
* @method Phaser.StateManager#loadComplete
* @protected
*/
loadComplete: function () {
// console.log('Phaser.StateManager.loadComplete');
@@ -378,6 +443,10 @@ Phaser.StateManager.prototype = {
},
/**
* @method Phaser.StateManager#update
* @protected
*/
update: function () {
if (this._created && this.onUpdateCallback)
@@ -394,6 +463,10 @@ Phaser.StateManager.prototype = {
},
/**
* @method Phaser.StateManager#preRender
* @protected
*/
preRender: function () {
if (this.onPreRenderCallback)
@@ -403,6 +476,10 @@ Phaser.StateManager.prototype = {
},
/**
* @method Phaser.StateManager#render
* @protected
*/
render: function () {
if (this._created && this.onRenderCallback)
@@ -421,6 +498,7 @@ Phaser.StateManager.prototype = {
/**
* Nuke the entire game from orbit
* @method Phaser.StateManager#destroy
*/
destroy: function () {
+177 -157
View File
@@ -1,240 +1,260 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license https://github.com/photonstorm/phaser/blob/master/license.txt MIT License
* @module Phaser.World
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
*
* "This world is but a canvas to our imagination." - Henry David Thoreau
*
* A game has only one world. The world is an abstract place in which all game objects live. It is not bound
* by stage limits and can be any size. You look into the world via cameras. All game objects live within
* the world at world-based coordinates. By default a world is created the same size as your Stage.
*
* @class World
* @constructor
* @param {Phaser.Game} game Reference to the current game instance.
*/
* "This world is but a canvas to our imagination." - Henry David Thoreau
*
* A game has only one world. The world is an abstract place in which all game objects live. It is not bound
* by stage limits and can be any size. You look into the world via cameras. All game objects live within
* the world at world-based coordinates. By default a world is created the same size as your Stage.
*
* @class Phaser.World
* @constructor
* @param {Phaser.Game} game - Reference to the current game instance.
*/
Phaser.World = function (game) {
/**
* A reference to the currently running Game.
* @property game
* @public
* @type {Phaser.Game}
*/
this.game = game;
Phaser.Group.call(this, game, null, '__world', false);
/**
* Bound of this world that objects can not escape from.
* @property bounds
* @public
* @type {Phaser.Rectangle}
*/
* @property {Phaser.Point} scale - Replaces the PIXI.Point with a slightly more flexible one.
*/
this.scale = new Phaser.Point(1, 1);
/**
* The World has no fixed size, but it does have a bounds outside of which objects are no longer considered as being "in world" and you should use this to clean-up the display list and purge dead objects.
* By default we set the Bounds to be from 0,0 to Game.width,Game.height. I.e. it will match the size given to the game constructor with 0,0 representing the top-left of the display.
* However 0,0 is actually the center of the world, and if you rotate or scale the world all of that will happen from 0,0.
* So if you want to make a game in which the world itself will rotate you should adjust the bounds so that 0,0 is the center point, i.e. set them to -1000,-1000,2000,2000 for a 2000x2000 sized world centered around 0,0.
* @property {Phaser.Rectangle} bounds - Bound of this world that objects can not escape from.
*/
this.bounds = new Phaser.Rectangle(0, 0, game.width, game.height);
/**
* Camera instance.
* @property camera
* @public
* @type {Phaser.Camera}
*/
* @property {Phaser.Camera} camera - Camera instance.
*/
this.camera = null;
/**
* Reset each frame, keeps a count of the total number of objects updated.
* @property currentRenderOrderID
* @public
* @type {Number}
*/
* @property {number} currentRenderOrderID - Reset each frame, keeps a count of the total number of objects updated.
*/
this.currentRenderOrderID = 0;
};
Phaser.World.prototype = Object.create(Phaser.Group.prototype);
Phaser.World.prototype.constructor = Phaser.World;
/**
* Initialises the game world.
*
* @method Phaser.World#boot
* @protected
*/
Phaser.World.prototype.boot = function () {
this.camera = new Phaser.Camera(this.game, 0, 0, 0, this.game.width, this.game.height);
this.camera.displayObject = this._container;
this.game.camera = this.camera;
}
/**
* This is called automatically every frame, and is where main logic happens.
*
* @method Phaser.World#update
*/
Phaser.World.prototype.update = function () {
this.currentRenderOrderID = 0;
/**
* Object container stores every object created with `create*` methods.
* @property group
* @public
* @type {Phaser.Group}
*/
this.group = null;
};
Phaser.World.prototype = {
/**
* Initialises the game world
*
* @method boot
*/
boot: function () {
this.camera = new Phaser.Camera(this.game, 0, 0, 0, this.game.width, this.game.height);
this.game.camera = this.camera;
this.group = new Phaser.Group(this.game, null, '__world', true);
},
/**
* This is called automatically every frame, and is where main logic happens.
* @method update
*/
update: function () {
this.camera.update();
this.currentRenderOrderID = 0;
if (this.game.stage._stage.first._iNext)
if (this.game.stage._stage.first._iNext)
{
var currentNode = this.game.stage._stage.first._iNext;
do
{
var currentNode = this.game.stage._stage.first._iNext;
do
if (currentNode['preUpdate'])
{
if (currentNode['preUpdate'])
{
currentNode.preUpdate();
}
if (currentNode['update'])
{
currentNode.update();
}
currentNode = currentNode._iNext;
currentNode.preUpdate();
}
while (currentNode != this.game.stage._stage.last._iNext)
if (currentNode['update'])
{
currentNode.update();
}
currentNode = currentNode._iNext;
}
while (currentNode != this.game.stage._stage.last._iNext)
}
},
}
/**
* Updates the size of this world.
* @method setSize
* @param {number} width New width of the world.
* @param {number} height New height of the world.
*/
setSize: function (width, height) {
/**
* This is called automatically every frame, and is where main logic happens.
* @method Phaser.World#postUpdate
*/
Phaser.World.prototype.postUpdate = function () {
if (width >= this.game.width)
this.camera.update();
if (this.game.stage._stage.first._iNext)
{
var currentNode = this.game.stage._stage.first._iNext;
do
{
this.bounds.width = width;
if (currentNode['postUpdate'])
{
currentNode.postUpdate();
}
currentNode = currentNode._iNext;
}
if (height >= this.game.height)
{
this.bounds.height = height;
}
},
/**
* Destroyer of worlds.
* @method destroy
*/
destroy: function () {
this.camera.x = 0;
this.camera.y = 0;
this.game.input.reset(true);
this.group.removeAll();
while (currentNode != this.game.stage._stage.last._iNext)
}
};
// Getters / Setters
}
/**
* Updates the size of this world. Note that this doesn't modify the world x/y coordinates, just the width and height.
* If you need to adjust the bounds of the world
* @method Phaser.World#setBounds
* @param {number} x - Top left most corner of the world.
* @param {number} y - Top left most corner of the world.
* @param {number} width - New width of the world.
* @param {number} height - New height of the world.
*/
Phaser.World.prototype.setBounds = function (x, y, width, height) {
this.bounds.setTo(x, y, width, height);
if (this.camera.bounds)
{
this.camera.bounds.setTo(x, y, width, height);
}
}
/**
* Destroyer of worlds.
* @method Phaser.World#destroy
*/
Phaser.World.prototype.destroy = function () {
this.camera.x = 0;
this.camera.y = 0;
this.game.input.reset(true);
this.removeAll();
}
/**
* @name Phaser.World#width
* @property {number} width - Gets or sets the current width of the game world.
*/
Object.defineProperty(Phaser.World.prototype, "width", {
/**
* @method width
* @return {Number} The current width of the game world
*/
get: function () {
return this.bounds.width;
},
/**
* @method width
* @return {Number} Sets the width of the game world
*/
set: function (value) {
this.bounds.width = value;
}
});
/**
* @name Phaser.World#height
* @property {number} height - Gets or sets the current height of the game world.
*/
Object.defineProperty(Phaser.World.prototype, "height", {
/**
* @method height
* @return {Number} The current height of the game world
*/
get: function () {
return this.bounds.height;
},
/**
* @method height
* @return {Number} Sets the width of the game world
*/
set: function (value) {
this.bounds.height = value;
}
});
/**
* @name Phaser.World#centerX
* @property {number} centerX - Gets the X position corresponding to the center point of the world.
* @readonly
*/
Object.defineProperty(Phaser.World.prototype, "centerX", {
/**
* @method centerX
* @return {Number} return the X position of the center point of the world
*/
get: function () {
return this.bounds.halfWidth;
}
});
/**
* @name Phaser.World#centerY
* @property {number} centerY - Gets the Y position corresponding to the center point of the world.
* @readonly
*/
Object.defineProperty(Phaser.World.prototype, "centerY", {
/**
* @method centerY
* @return {Number} return the Y position of the center point of the world
*/
get: function () {
return this.bounds.halfHeight;
}
});
/**
* @name Phaser.World#randomX
* @property {number} randomX - Gets a random integer which is lesser than or equal to the current width of the game world.
* @readonly
*/
Object.defineProperty(Phaser.World.prototype, "randomX", {
/**
* @method randomX
* @return {Number} a random integer which is lesser or equal to the current width of the game world
*/
get: function () {
return Math.round(Math.random() * this.bounds.width);
if (this.bounds.x < 0)
{
return this.game.rnd.integerInRange(this.bounds.x, (this.bounds.width - Math.abs(this.bounds.x)));
}
else
{
return this.game.rnd.integerInRange(this.bounds.x, this.bounds.width);
}
}
});
/**
* @name Phaser.World#randomY
* @property {number} randomY - Gets a random integer which is lesser than or equal to the current height of the game world.
* @readonly
*/
Object.defineProperty(Phaser.World.prototype, "randomY", {
/**
* @method randomY
* @return {Number} a random integer which is lesser or equal to the current height of the game world
*/
get: function () {
return Math.round(Math.random() * this.bounds.height);
if (this.bounds.y < 0)
{
return this.game.rnd.integerInRange(this.bounds.y, (this.bounds.height - Math.abs(this.bounds.y)));
}
else
{
return this.game.rnd.integerInRange(this.bounds.y, this.bounds.height);
}
}
});
+116 -12
View File
@@ -1,37 +1,89 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Creates a new <code>BitmapText</code>.
* @class Phaser.BitmapText
* @constructor
* @param {Phaser.Game} game - A reference to the currently running game.
* @param {number} x - X position of the new bitmapText object.
* @param {number} y - Y position of the new bitmapText object.
* @param {string} text - The actual text that will be written.
* @param {object} style - The style object containing style attributes like font, font size , etc.
*/
Phaser.BitmapText = function (game, x, y, text, style) {
x = x || 0;
y = y || 0;
text = text || '';
style = style || '';
// If exists = false then the Sprite isn't updated by the core game loop or physics subsystem at all
/**
* @property {boolean} exists - If exists = false then the Sprite isn't updated by the core game loop or physics subsystem at all.
* @default
*/
this.exists = true;
// This is a handy little var your game can use to determine if a sprite is alive or not, it doesn't effect rendering
/**
* @property {boolean} alive - This is a handy little var your game can use to determine if a sprite is alive or not, it doesn't effect rendering.
* @default
*/
this.alive = true;
/**
* @property {Description} group - Description.
* @default
*/
this.group = null;
/**
* @property {string} name - Description.
* @default
*/
this.name = '';
/**
* @property {Phaser.Game} game - A reference to the currently running Game.
*/
this.game = game;
PIXI.BitmapText.call(this, text, style);
/**
* @property {Description} type - Description.
*/
this.type = Phaser.BITMAPTEXT;
/**
* @property {number} position.x - Description.
*/
this.position.x = x;
/**
* @property {number} position.y - Description.
*/
this.position.y = y;
// Replaces the PIXI.Point with a slightly more flexible one
/**
* @property {Phaser.Point} anchor - Description.
*/
this.anchor = new Phaser.Point();
/**
* @property {Phaser.Point} scale - Description.
*/
this.scale = new Phaser.Point(1, 1);
// Influence of camera movement upon the position
this.scrollFactor = new Phaser.Point(1, 1);
// A mini cache for storing all of the calculated values
/**
* @property {function} _cache - Description.
* @private
*/
this._cache = {
dirty: false,
@@ -39,7 +91,7 @@ Phaser.BitmapText = function (game, x, y, text, style) {
// Transform cache
a00: 1, a01: 0, a02: x, a10: 0, a11: 1, a12: y, id: 1,
// The previous calculated position inc. camera x/y and scrollFactor
// The previous calculated position
x: -1, y: -1,
// The actual scale values based on the worldTransform
@@ -47,9 +99,13 @@ Phaser.BitmapText = function (game, x, y, text, style) {
};
this._cache.x = this.x - (this.game.world.camera.x * this.scrollFactor.x);
this._cache.y = this.y - (this.game.world.camera.y * this.scrollFactor.y);
this._cache.x = this.x;
this._cache.y = this.y;
/**
* @property {boolean} renderable - Description.
* @private
*/
this.renderable = true;
};
@@ -59,8 +115,9 @@ Phaser.BitmapText.prototype = Object.create(PIXI.BitmapText.prototype);
Phaser.BitmapText.prototype.constructor = Phaser.BitmapText;
/**
* Automatically called by World.update
*/
* Automatically called by World.update
* @method Phaser.BitmapText.prototype.update
*/
Phaser.BitmapText.prototype.update = function() {
if (!this.exists)
@@ -70,8 +127,8 @@ Phaser.BitmapText.prototype.update = function() {
this._cache.dirty = false;
this._cache.x = this.x - (this.game.world.camera.x * this.scrollFactor.x);
this._cache.y = this.y - (this.game.world.camera.y * this.scrollFactor.y);
this._cache.x = this.x;
this._cache.y = this.y;
if (this.position.x != this._cache.x || this.position.y != this._cache.y)
{
@@ -85,6 +142,39 @@ Phaser.BitmapText.prototype.update = function() {
}
/**
* @method Phaser.Text.prototype.destroy
*/
Phaser.BitmapText.prototype.destroy = function() {
if (this.group)
{
this.group.remove(this);
}
if (this.canvas.parentNode)
{
this.canvas.parentNode.removeChild(this.canvas);
}
else
{
this.canvas = null;
this.context = null;
}
this.exists = false;
this.group = null;
}
/**
* Get
* @returns {Description}
*//**
* Set
* @param {Description} value - Description
*/
Object.defineProperty(Phaser.BitmapText.prototype, 'angle', {
get: function() {
@@ -97,6 +187,13 @@ Object.defineProperty(Phaser.BitmapText.prototype, 'angle', {
});
/**
* Get
* @returns {Description}
*//**
* Set
* @param {Description} value - Description
*/
Object.defineProperty(Phaser.BitmapText.prototype, 'x', {
get: function() {
@@ -109,6 +206,13 @@ Object.defineProperty(Phaser.BitmapText.prototype, 'x', {
});
/**
* Get
* @returns {Description}
*//**
* Set
* @param {Description} value - Description
*/
Object.defineProperty(Phaser.BitmapText.prototype, 'y', {
get: function() {
+391 -132
View File
@@ -1,28 +1,89 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Warning: Bullet is an experimental object that we don't advise using for now.
*
* A Bullet is like a stripped-down Sprite, useful for when you just need to get something moving around the screen quickly with little of the extra
* features that a Sprite supports.
* Bullet is MISSING the following:
*
* animation, all input events, crop support, health/damage, loadTexture
*
* @class Phaser.Bullet
* @constructor
* @param {Phaser.Game} game - Current game instance.
* @param {number} x - X position of the new bullet.
* @param {number} y - Y position of the new bullet.
* @param {string|Phaser.RenderTexture|PIXI.Texture} key - This is the image or texture used by the Sprite during rendering. It can be a string which is a reference to the Cache entry, or an instance of a RenderTexture or PIXI.Texture.
* @param {string|number} frame - If this Sprite is using part of a sprite sheet or texture atlas you can specify the exact frame to use by giving a string or numeric index.
*/
Phaser.Bullet = function (game, x, y, key, frame) {
x = x || 0;
y = y || 0;
key = key || null;
frame = frame || null;
/**
* @property {Phaser.Game} game - A reference to the currently running Game.
*/
this.game = game;
// If exists = false then the Sprite isn't updated by the core game loop or physics subsystem at all
/**
* @property {boolean} exists - If exists = false then the Sprite isn't updated by the core game loop or physics subsystem at all.
* @default
*/
this.exists = true;
// This is a handy little var your game can use to determine if a sprite is alive or not, it doesn't effect rendering
/**
* @property {boolean} alive - This is a handy little var your game can use to determine if a sprite is alive or not, it doesn't effect rendering.
* @default
*/
this.alive = true;
/**
* @property {Description} group - Description.
* @default
*/
this.group = null;
/**
* @property {string} name - The user defined name given to this Sprite.
* @default
*/
this.name = '';
/**
* @property {Description} type - Description.
*/
this.type = Phaser.BULLET;
/**
* @property {number} renderOrderID - Description.
* @default
*/
this.renderOrderID = -1;
// If you would like the Sprite to have a lifespan once 'born' you can set this to a positive value. Handy for particles, bullets, etc.
// The lifespan is decremented by game.time.elapsed each update, once it reaches zero the kill() function is called.
/**
* If you would like the Sprite to have a lifespan once 'born' you can set this to a positive value. Handy for particles, bullets, etc.
* The lifespan is decremented by game.time.elapsed each update, once it reaches zero the kill() function is called.
* @property {number} lifespan
* @default
*/
this.lifespan = 0;
/**
* @property {Events} events - The Signals you can subscribe to that are dispatched when certain things happen on this Sprite or its components
*/
this.events = new Phaser.Events(this);
/**
* @property {Description} key - Description.
*/
this.key = key;
if (key instanceof Phaser.RenderTexture)
@@ -31,6 +92,12 @@ Phaser.Bullet = function (game, x, y, key, frame) {
this.currentFrame = this.game.cache.getTextureFrame(key.name);
}
else if (key instanceof PIXI.Texture)
{
PIXI.Sprite.call(this, key);
this.currentFrame = frame;
}
else
{
if (key == null || this.game.cache.checkImageKey(key) == false)
@@ -42,17 +109,21 @@ Phaser.Bullet = function (game, x, y, key, frame) {
if (this.game.cache.isSpriteSheet(key))
{
/*
this.animations.loadFrameData(this.game.cache.getFrameData(key));
if (frame !== null)
{
if (typeof frame === 'string')
{
this.currentFrame = this.game.cache.getFrameByName(key, frame);
this.frameName = frame;
}
else
{
this.currentFrame = this.game.cache.getFrameByIndex(key, frame);
this.frame = frame;
}
}
*/
}
else
{
@@ -61,54 +132,65 @@ Phaser.Bullet = function (game, x, y, key, frame) {
}
/**
* The anchor sets the origin point of the texture.
* The default is 0,0 this means the textures origin is the top left
* Setting than anchor to 0.5,0.5 means the textures origin is centered
* Setting the anchor to 1,1 would mean the textures origin points will be the bottom right
*
* @property anchor
* @type Point
*/
* The anchor sets the origin point of the texture.
* The default is 0,0 this means the textures origin is the top left
* Setting than anchor to 0.5,0.5 means the textures origin is centered
* Setting the anchor to 1,1 would mean the textures origin points will be the bottom right
*
* @property {Phaser.Point} anchor
*/
this.anchor = new Phaser.Point();
/**
* @property {number} x - Description.
*/
this.x = x;
/**
* @property {number} y - Description.
*/
this.y = y;
/**
* @property {Description} position - Description.
*/
this.position.x = x;
this.position.y = y;
/**
* Should this Sprite be automatically culled if out of range of the camera?
* A culled sprite has its visible property set to 'false'.
* Note that this check doesn't look at this Sprites children, which may still be in camera range.
* So you should set autoCull to false if the Sprite will have children likely to still be in camera range.
*
* @property autoCull
* @type Boolean
*/
* Should this Sprite be automatically culled if out of range of the camera?
* A culled sprite has its visible property set to 'false'.
* Note that this check doesn't look at this Sprites children, which may still be in camera range.
* So you should set autoCull to false if the Sprite will have children likely to still be in camera range.
*
* @property {boolean} autoCull
* @default
*/
this.autoCull = false;
// Replaces the PIXI.Point with a slightly more flexible one
/**
* @property {Phaser.Point} scale - Replaces the PIXI.Point with a slightly more flexible one.
*/
this.scale = new Phaser.Point(1, 1);
// Influence of camera movement upon the position
this.scrollFactor = new Phaser.Point(1, 1);
// A mini cache for storing all of the calculated values
/**
* @property {Phaser.Point} _cache - A mini cache for storing all of the calculated values.
* @private
*/
this._cache = {
dirty: false,
// Transform cache
a00: 1, a01: 0, a02: x, a10: 0, a11: 1, a12: y, id: 1,
a00: -1, a01: -1, a02: -1, a10: -1, a11: -1, a12: -1, id: -1,
// Input specific transform cache
i01: 0, i10: 0, idi: 1,
i01: -1, i10: -1, idi: -1,
// Bounds check
left: null, right: null, top: null, bottom: null,
// The previous calculated position inc. camera x/y and scrollFactor
// The previous calculated position
x: -1, y: -1,
// The actual scale values based on the worldTransform
@@ -120,35 +202,93 @@ Phaser.Bullet = function (game, x, y, key, frame) {
// The actual width/height of the image if from a trimmed atlas, multiplied by the final calculated scale size
halfWidth: Math.floor(this.currentFrame.sourceSizeW / 2), halfHeight: Math.floor(this.currentFrame.sourceSizeH / 2),
// The current frame details
frameID: this.currentFrame.uuid, frameWidth: this.currentFrame.width, frameHeight: this.currentFrame.height,
boundsX: 0, boundsY: 0,
// If this sprite visible to the camera (regardless of being set to visible or not)
cameraVisible: true
};
/**
* @property {Phaser.Point} offset - Corner point defaults.
*/
this.offset = new Phaser.Point;
/**
* @property {Phaser.Point} center - Description.
*/
this.center = new Phaser.Point(x + Math.floor(this._cache.width / 2), y + Math.floor(this._cache.height / 2));
/**
* @property {Phaser.Point} topLeft - Description.
*/
this.topLeft = new Phaser.Point(x, y);
/**
* @property {Phaser.Point} topRight - Description.
*/
this.topRight = new Phaser.Point(x + this._cache.width, y);
/**
* @property {Phaser.Point} bottomRight - Description.
*/
this.bottomRight = new Phaser.Point(x + this._cache.width, y + this._cache.height);
/**
* @property {Phaser.Point} bottomLeft - Description.
*/
this.bottomLeft = new Phaser.Point(x, y + this._cache.height);
/**
* @property {Phaser.Rectangle} bounds - Description.
*/
this.bounds = new Phaser.Rectangle(x, y, this._cache.width, this._cache.height);
this.bounds = new Phaser.Rectangle(x, y, this.currentFrame.sourceSizeW, this.currentFrame.sourceSizeH);
// Set-up the physics body
/**
* @property {Phaser.Physics.Arcade.Body} body - Set-up the physics body.
*/
this.body = new Phaser.Physics.Arcade.Body(this);
// World bounds check
/**
* @property {Description} inWorld - World bounds check.
*/
this.inWorld = Phaser.Rectangle.intersects(this.bounds, this.game.world.bounds);
/**
* @property {number} inWorldThreshold - World bounds check.
* @default
*/
this.inWorldThreshold = 0;
/**
* @property {boolean} outOfBoundsKill - Kills this sprite as soon as it goes outside of the World bounds.
* @default
*/
this.outOfBoundsKill = false;
/**
* @property {boolean} _outOfBoundsFired - Description.
* @private
* @default
*/
this._outOfBoundsFired = false;
/**
* A Sprite that is fixed to the camera ignores the position of any ancestors in the display list and uses its x/y coordinates as offsets from the top left of the camera.
* @property {boolean} fixedToCamera - Fixes this Sprite to the Camera.
* @default
*/
this.fixedToCamera = false;
};
// Needed to keep the PIXI.Sprite constructor in the prototype chain (as the core pixi renderer uses an instanceof check sadly)
Phaser.Bullet.prototype = Object.create(PIXI.Sprite.prototype);
Phaser.Bullet.prototype.constructor = Phaser.Bullet;
/**
* Automatically called by World.update. You can create your own update in Objects that extend Phaser.Bullet.
*/
* Automatically called by World.preUpdate. You can create your own update in Objects that extend Phaser.Sprite.
* @method Phaser.Sprite.prototype.preUpdate
*/
Phaser.Bullet.prototype.preUpdate = function() {
if (!this.exists)
@@ -168,142 +308,226 @@ Phaser.Bullet.prototype.preUpdate = function() {
}
}
// this._cache.dirty = false;
this._cache.x = this.x - (this.game.world.camera.x * this.scrollFactor.x);
this._cache.y = this.y - (this.game.world.camera.y * this.scrollFactor.y);
// If this sprite or the camera have moved then let's update everything
if (this.position.x != this._cache.x || this.position.y != this._cache.y)
{
this.position.x = this._cache.x;
this.position.y = this._cache.y;
// this._cache.dirty = true;
}
this._cache.dirty = false;
if (this.visible)
{
this.renderOrderID = this.game.world.currentRenderOrderID++;
/*
// Only update the values we need
if (this.worldTransform[0] != this._cache.a00 || this.worldTransform[1] != this._cache.a01)
{
this._cache.a00 = this.worldTransform[0]; // scaleX a
this._cache.a01 = this.worldTransform[1]; // skewY c
this._cache.i01 = this.worldTransform[1]; // skewY c
this._cache.scaleX = Math.sqrt((this._cache.a00 * this._cache.a00) + (this._cache.a01 * this._cache.a01)); // round this off a bit?
this._cache.a01 *= -1;
this._cache.dirty = true;
}
// Need to test, but probably highly unlikely that a scaleX would happen without effecting the Y skew
if (this.worldTransform[3] != this._cache.a10 || this.worldTransform[4] != this._cache.a11)
{
this._cache.a10 = this.worldTransform[3]; // skewX b
this._cache.i10 = this.worldTransform[3]; // skewX b
this._cache.a11 = this.worldTransform[4]; // scaleY d
this._cache.scaleY = Math.sqrt((this._cache.a10 * this._cache.a10) + (this._cache.a11 * this._cache.a11)); // round this off a bit?
this._cache.a10 *= -1;
this._cache.dirty = true;
}
if (this.worldTransform[2] != this._cache.a02 || this.worldTransform[5] != this._cache.a12)
{
this._cache.a02 = this.worldTransform[2]; // translateX tx
this._cache.a12 = this.worldTransform[5]; // translateY ty
this._cache.dirty = true;
}
if (this._cache.dirty)
{
this._cache.width = Math.floor(this.currentFrame.sourceSizeW * this._cache.scaleX);
this._cache.height = Math.floor(this.currentFrame.sourceSizeH * this._cache.scaleY);
this._cache.halfWidth = Math.floor(this._cache.width / 2);
this._cache.halfHeight = Math.floor(this._cache.height / 2);
this._cache.id = 1 / (this._cache.a00 * this._cache.a11 + this._cache.a01 * -this._cache.a10);
this._cache.idi = 1 / (this._cache.a00 * this._cache.a11 + this._cache.i01 * -this._cache.i10);
this.updateBounds();
}
*/
}
else
{
// We still need to work out the bounds in case the camera has moved
// but we can't use the local or worldTransform to do it, as Pixi resets that if a Sprite is invisible.
// So we'll compare against the cached state + new position.
if (this._cache.dirty && this.visible == false)
{
this.bounds.x -= this._cache.boundsX - this._cache.x;
this._cache.boundsX = this._cache.x;
this.bounds.y -= this._cache.boundsy - this._cache.y;
this._cache.boundsY = this._cache.y;
}
this.prevX = this.x;
this.prevY = this.y;
// |a c tx|
// |b d ty|
// |0 0 1|
if (this.worldTransform[1] != this._cache.i01 || this.worldTransform[3] != this._cache.i10)
{
this._cache.a00 = this.worldTransform[0]; // scaleX a
this._cache.a01 = this.worldTransform[1]; // skewY c
this._cache.a10 = this.worldTransform[3]; // skewX b
this._cache.a11 = this.worldTransform[4]; // scaleY d
this._cache.i01 = this.worldTransform[1]; // skewY c (remains non-modified for input checks)
this._cache.i10 = this.worldTransform[3]; // skewX b (remains non-modified for input checks)
this._cache.scaleX = Math.sqrt((this._cache.a00 * this._cache.a00) + (this._cache.a01 * this._cache.a01)); // round this off a bit?
this._cache.scaleY = Math.sqrt((this._cache.a10 * this._cache.a10) + (this._cache.a11 * this._cache.a11)); // round this off a bit?
this._cache.a01 *= -1;
this._cache.a10 *= -1;
this._cache.dirty = true;
}
if (this.worldTransform[2] != this._cache.a02 || this.worldTransform[5] != this._cache.a12)
{
this._cache.a02 = this.worldTransform[2]; // translateX tx
this._cache.a12 = this.worldTransform[5]; // translateY ty
this._cache.dirty = true;
}
// Re-run the camera visibility check
// if (this._cache.dirty)
// {
if (this._cache.dirty)
{
this._cache.cameraVisible = Phaser.Rectangle.intersects(this.game.world.camera.screenView, this.bounds, 0);
if (this.autoCull == true)
{
this.visible = this._cache.cameraVisible;
// Won't get rendered but will still get its transform updated
this.renderable = this._cache.cameraVisible;
}
// Update our physics bounds
this.body.updateBounds(this.center.x, this.center.y, this._cache.scaleX, this._cache.scaleY);
// }
if (this.body)
{
this.body.updateBounds(this.center.x, this.center.y, this._cache.scaleX, this._cache.scaleY);
}
}
this.body.update();
if (this.body)
{
this.body.preUpdate();
}
}
Phaser.Bullet.prototype.revive = function() {
Phaser.Bullet.prototype.postUpdate = function() {
if (this.exists)
{
// The sprite is positioned in this call, after taking into consideration motion updates and collision
if (this.body)
{
this.body.postUpdate();
}
if (this.fixedToCamera)
{
this._cache.x = this.game.camera.view.x + this.x;
this._cache.y = this.game.camera.view.y + this.y;
}
else
{
this._cache.x = this.x;
this._cache.y = this.y;
}
if (this.position.x != this._cache.x || this.position.y != this._cache.y)
{
this.position.x = this._cache.x;
this.position.y = this._cache.y;
}
}
}
Phaser.Bullet.prototype.deltaAbsX = function () {
return (this.deltaX() > 0 ? this.deltaX() : -this.deltaX());
}
Phaser.Bullet.prototype.deltaAbsY = function () {
return (this.deltaY() > 0 ? this.deltaY() : -this.deltaY());
}
Phaser.Bullet.prototype.deltaX = function () {
return this.x - this.prevX;
}
Phaser.Bullet.prototype.deltaY = function () {
return this.y - this.prevY;
}
/**
* Description.
*
* @method Phaser.Bullet.prototype.revive
*/
Phaser.Bullet.prototype.revive = function(health) {
if (typeof health === 'undefined') { health = 1; }
this.alive = true;
this.exists = true;
this.visible = true;
// this.events.onRevived.dispatch(this);
this.health = health;
this.events.onRevived.dispatch(this);
}
/**
* Description.
*
* @method Phaser.Bullet.prototype.kill
*/
Phaser.Bullet.prototype.kill = function() {
this.alive = false;
this.exists = false;
this.visible = false;
// this.events.onKilled.dispatch(this);
this.events.onKilled.dispatch(this);
}
/**
* Description.
*
* @method Phaser.Bullet.prototype.destroy
*/
Phaser.Bullet.prototype.destroy = function() {
if (this.group)
{
this.group.remove(this);
}
this.events.destroy();
this.alive = false;
this.exists = false;
this.visible = false;
this.game = null;
}
/**
* Description.
*
* @method Phaser.Sprite.prototype.reset
*/
Phaser.Bullet.prototype.reset = function(x, y) {
this.x = x;
this.y = y;
this.position.x = x;
this.position.y = y;
this.position.x = this.x;
this.position.y = this.y;
this.alive = true;
this.exists = true;
this.visible = true;
this.renderable = true;
this._outOfBoundsFired = false;
this.body.reset();
if (this.body)
{
this.body.reset();
}
}
/**
* Description.
*
* @method Phaser.Sprite.prototype.updateBounds
*/
Phaser.Bullet.prototype.updateBounds = function() {
// Update the edge points
// this.bounds.setTo(this._cache.left, this._cache.top, this._cache.right - this._cache.left, this._cache.bottom - this._cache.top);
this.offset.setTo(this._cache.a02 - (this.anchor.x * this._cache.width), this._cache.a12 - (this.anchor.y * this._cache.height));
this.getLocalPosition(this.center, this.offset.x + this._cache.halfWidth, this.offset.y + this._cache.halfHeight);
this.getLocalPosition(this.topLeft, this.offset.x, this.offset.y);
this.getLocalPosition(this.topRight, this.offset.x + this._cache.width, this.offset.y);
this.getLocalPosition(this.bottomLeft, this.offset.x, this.offset.y + this._cache.height);
this.getLocalPosition(this.bottomRight, this.offset.x + this._cache.width, this.offset.y + this._cache.height);
this._cache.left = Phaser.Math.min(this.topLeft.x, this.topRight.x, this.bottomLeft.x, this.bottomRight.x);
this._cache.right = Phaser.Math.max(this.topLeft.x, this.topRight.x, this.bottomLeft.x, this.bottomRight.x);
this._cache.top = Phaser.Math.min(this.topLeft.y, this.topRight.y, this.bottomLeft.y, this.bottomRight.y);
this._cache.bottom = Phaser.Math.max(this.topLeft.y, this.topRight.y, this.bottomLeft.y, this.bottomRight.y);
this.bounds.setTo(this._cache.left, this._cache.top, this._cache.right - this._cache.left, this._cache.bottom - this._cache.top);
// This is the coordinate the Bullet was at when the last bounds was created
this._cache.boundsX = this._cache.x;
this._cache.boundsY = this._cache.y;
if (this.inWorld == false)
{
// Sprite WAS out of the screen, is it still?
// Bullet WAS out of the screen, is it still?
this.inWorld = Phaser.Rectangle.intersects(this.bounds, this.game.world.bounds, this.inWorldThreshold);
if (this.inWorld)
@@ -314,18 +538,46 @@ Phaser.Bullet.prototype.updateBounds = function() {
}
else
{
// Sprite WAS in the screen, has it now left?
// Bullet WAS in the screen, has it now left?
this.inWorld = Phaser.Rectangle.intersects(this.bounds, this.game.world.bounds, this.inWorldThreshold);
if (this.inWorld == false)
{
this.events.onOutOfBounds.dispatch(this);
this._outOfBoundsFired = true;
if (this.outOfBoundsKill)
{
this.kill();
}
}
}
}
/**
* Description.
*
* @method Phaser.Bullet.prototype.getLocalPosition
* @param {Description} p - Description.
* @param {number} x - Description.
* @param {number} y - Description.
* @return {Description} Description.
*/
Phaser.Bullet.prototype.getLocalPosition = function(p, x, y) {
p.x = ((this._cache.a11 * this._cache.id * x + -this._cache.a01 * this._cache.id * y + (this._cache.a12 * this._cache.a01 - this._cache.a02 * this._cache.a11) * this._cache.id) * this._cache.scaleX) + this._cache.a02;
p.y = ((this._cache.a00 * this._cache.id * y + -this._cache.a10 * this._cache.id * x + (-this._cache.a12 * this._cache.a00 + this._cache.a02 * this._cache.a10) * this._cache.id) * this._cache.scaleY) + this._cache.a12;
return p;
}
/**
* Description.
*
* @method Phaser.Bullet.prototype.bringToTop
*/
Phaser.Bullet.prototype.bringToTop = function() {
if (this.group)
@@ -339,26 +591,33 @@ Phaser.Bullet.prototype.bringToTop = function() {
}
/**
* Indicates the rotation of the Bullet, in degrees, from its original orientation. Values from 0 to 180 represent clockwise rotation; values from 0 to -180 represent counterclockwise rotation.
* Values outside this range are added to or subtracted from 360 to obtain a value within the range. For example, the statement player.angle = 450 is the same as player.angle = 90.
* If you wish to work in radians instead of degrees use the property Bullet.rotation instead.
* @name Phaser.Bullet#angle
* @property {number} angle - Gets or sets the Bullets angle of rotation in degrees.
*/
Object.defineProperty(Phaser.Bullet.prototype, 'angle', {
get: function() {
return Phaser.Math.radToDeg(this.rotation);
return Phaser.Math.wrapAngle(Phaser.Math.radToDeg(this.rotation));
},
set: function(value) {
this.rotation = Phaser.Math.degToRad(value);
this.rotation = Phaser.Math.degToRad(Phaser.Math.wrapAngle(value));
}
});
/**
* Is this sprite visible to the camera or not?
* @returns {boolean}
*/
Object.defineProperty(Phaser.Bullet.prototype, "inCamera", {
/**
* Is this sprite visible to the camera or not?
*/
get: function () {
return this._cache.cameraVisible;
}
});
+166 -34
View File
@@ -1,13 +1,13 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license https://github.com/photonstorm/phaser/blob/master/license.txt MIT License
* @module Phaser.Button
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Create a new <code>Button</code> object.
* @class Button
* @class Phaser.Button
* @constructor
*
* @param {Phaser.Game} game Current game instance.
@@ -22,7 +22,6 @@
*/
Phaser.Button = function (game, x, y, key, callback, callbackContext, overFrame, outFrame, downFrame) {
x = x || 0;
y = y || 0;
key = key || null;
@@ -31,21 +30,86 @@ Phaser.Button = function (game, x, y, key, callback, callbackContext, overFrame,
Phaser.Sprite.call(this, game, x, y, key, outFrame);
/**
* @property {Description} type - Description.
*/
this.type = Phaser.BUTTON;
/**
* @property {Description} _onOverFrameName - Description.
* @private
* @default
*/
this._onOverFrameName = null;
/**
* @property {Description} _onOutFrameName - Description.
* @private
* @default
*/
this._onOutFrameName = null;
/**
* @property {Description} _onDownFrameName - Description.
* @private
* @default
*/
this._onDownFrameName = null;
/**
* @property {Description} _onUpFrameName - Description.
* @private
* @default
*/
this._onUpFrameName = null;
/**
* @property {Description} _onOverFrameID - Description.
* @private
* @default
*/
this._onOverFrameID = null;
/**
* @property {Description} _onOutFrameID - Description.
* @private
* @default
*/
this._onOutFrameID = null;
/**
* @property {Description} _onDownFrameID - Description.
* @private
* @default
*/
this._onDownFrameID = null;
/**
* @property {Description} _onUpFrameID - Description.
* @private
* @default
*/
this._onUpFrameID = null;
// These are the signals the game will subscribe to
/**
* @property {Phaser.Signal} onInputOver - Description.
*/
this.onInputOver = new Phaser.Signal;
/**
* @property {Phaser.Signal} onInputOut - Description.
*/
this.onInputOut = new Phaser.Signal;
/**
* @property {Phaser.Signal} onInputDown - Description.
*/
this.onInputDown = new Phaser.Signal;
/**
* @property {Phaser.Signal} onInputUp - Description.
*/
this.onInputUp = new Phaser.Signal;
this.setFrames(overFrame, outFrame, downFrame);
@@ -55,7 +119,9 @@ Phaser.Button = function (game, x, y, key, callback, callbackContext, overFrame,
this.onInputUp.add(callback, callbackContext);
}
this.input.start(0, false, true);
this.freezeFrames = false;
this.input.start(0, true);
// Redirect the input events to here so we can handle animation updates, etc
this.events.onInputOver.add(this.onInputOverHandler, this);
@@ -70,12 +136,12 @@ Phaser.Button.prototype.constructor = Phaser.Button;
/**
* Used to manually set the frames that will be used for the different states of the button
* exactly like setting them in the constructor
* exactly like setting them in the constructor.
*
* @method setFrames
* @param {string|number} [overFrame] This is the frame or frameName that will be set when this button is in an over state. Give either a number to use a frame ID or a string for a frame name.
* @param {string|number} [outFrame] This is the frame or frameName that will be set when this button is in an out state. Give either a number to use a frame ID or a string for a frame name.
* @param {string|number} [downFrame] This is the frame or frameName that will be set when this button is in a down state. Give either a number to use a frame ID or a string for a frame name.
* @method Phaser.Button.prototype.setFrames
* @param {string|number} [overFrame] - This is the frame or frameName that will be set when this button is in an over state. Give either a number to use a frame ID or a string for a frame name.
* @param {string|number} [outFrame] - This is the frame or frameName that will be set when this button is in an out state. Give either a number to use a frame ID or a string for a frame name.
* @param {string|number} [downFrame] - This is the frame or frameName that will be set when this button is in a down state. Give either a number to use a frame ID or a string for a frame name.
*/
Phaser.Button.prototype.setFrames = function (overFrame, outFrame, downFrame) {
@@ -84,10 +150,20 @@ Phaser.Button.prototype.setFrames = function (overFrame, outFrame, downFrame) {
if (typeof overFrame === 'string')
{
this._onOverFrameName = overFrame;
if (this.input.pointerOver())
{
this.frameName = overFrame;
}
}
else
{
this._onOverFrameID = overFrame;
if (this.input.pointerOver())
{
this.frame = overFrame;
}
}
}
@@ -97,11 +173,21 @@ Phaser.Button.prototype.setFrames = function (overFrame, outFrame, downFrame) {
{
this._onOutFrameName = outFrame;
this._onUpFrameName = outFrame;
if (this.input.pointerOver() == false)
{
this.frameName = outFrame;
}
}
else
{
this._onOutFrameID = outFrame;
this._onUpFrameID = outFrame;
if (this.input.pointerOver() == false)
{
this.frame = outFrame;
}
}
}
@@ -110,24 +196,43 @@ Phaser.Button.prototype.setFrames = function (overFrame, outFrame, downFrame) {
if (typeof downFrame === 'string')
{
this._onDownFrameName = downFrame;
if (this.input.pointerOver())
{
this.frameName = downFrame;
}
}
else
{
this._onDownFrameID = downFrame;
if (this.input.pointerOver())
{
this.frame = downFrame;
}
}
}
};
/**
* Description.
*
* @method Phaser.Button.prototype.onInputOverHandler
* @param {Description} pointer - Description.
*/
Phaser.Button.prototype.onInputOverHandler = function (pointer) {
if (this._onOverFrameName != null)
if (this.freezeFrames == false)
{
this.frameName = this._onOverFrameName;
}
else if (this._onOverFrameID != null)
{
this.frame = this._onOverFrameID;
if (this._onOverFrameName != null)
{
this.frameName = this._onOverFrameName;
}
else if (this._onOverFrameID != null)
{
this.frame = this._onOverFrameID;
}
}
if (this.onInputOver)
@@ -136,15 +241,24 @@ Phaser.Button.prototype.onInputOverHandler = function (pointer) {
}
};
/**
* Description.
*
* @method Phaser.Button.prototype.onInputOutHandler
* @param {Description} pointer - Description.
*/
Phaser.Button.prototype.onInputOutHandler = function (pointer) {
if (this._onOutFrameName != null)
if (this.freezeFrames == false)
{
this.frameName = this._onOutFrameName;
}
else if (this._onOutFrameID != null)
{
this.frame = this._onOutFrameID;
if (this._onOutFrameName != null)
{
this.frameName = this._onOutFrameName;
}
else if (this._onOutFrameID != null)
{
this.frame = this._onOutFrameID;
}
}
if (this.onInputOut)
@@ -153,15 +267,24 @@ Phaser.Button.prototype.onInputOutHandler = function (pointer) {
}
};
/**
* Description.
*
* @method Phaser.Button.prototype.onInputDownHandler
* @param {Description} pointer - Description.
*/
Phaser.Button.prototype.onInputDownHandler = function (pointer) {
if (this._onDownFrameName != null)
if (this.freezeFrames == false)
{
this.frameName = this._onDownFrameName;
}
else if (this._onDownFrameID != null)
{
this.frame = this._onDownFrameID;
if (this._onDownFrameName != null)
{
this.frameName = this._onDownFrameName;
}
else if (this._onDownFrameID != null)
{
this.frame = this._onDownFrameID;
}
}
if (this.onInputDown)
@@ -170,15 +293,24 @@ Phaser.Button.prototype.onInputDownHandler = function (pointer) {
}
};
/**
* Description.
*
* @method Phaser.Button.prototype.onInputUpHandler
* @param {Description} pointer - Description.
*/
Phaser.Button.prototype.onInputUpHandler = function (pointer) {
if (this._onUpFrameName != null)
if (this.freezeFrames == false)
{
this.frameName = this._onUpFrameName;
}
else if (this._onUpFrameID != null)
{
this.frame = this._onUpFrameID;
if (this._onUpFrameName != null)
{
this.frameName = this._onUpFrameName;
}
else if (this._onUpFrameID != null)
{
this.frame = this._onUpFrameID;
}
}
if (this.onInputUp)
+44 -1
View File
@@ -1,6 +1,17 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* The Events component is a collection of events fired by the parent game object and its components.
* @param parent The game object using this Input component
*
* @class Phaser.Events
* @constructor
*
* @param {Phaser.Sprite} sprite - A reference to Description.
*/
Phaser.Events = function (sprite) {
@@ -22,4 +33,36 @@ Phaser.Events = function (sprite) {
this.onAnimationComplete = null;
this.onAnimationLoop = null;
};
Phaser.Events.prototype = {
destroy: function () {
this.parent = null;
this.onAddedToGroup.dispose();
this.onRemovedFromGroup.dispose();
this.onKilled.dispose();
this.onRevived.dispose();
this.onOutOfBounds.dispose();
if (this.onInputOver)
{
this.onInputOver.dispose();
this.onInputOut.dispose();
this.onInputDown.dispose();
this.onInputUp.dispose();
this.onDragStart.dispose();
this.onDragStop.dispose();
}
if (this.onAnimationStart)
{
this.onAnimationStart.dispose();
this.onAnimationComplete.dispose();
this.onAnimationLoop.dispose();
}
}
};
+181 -29
View File
@@ -1,58 +1,83 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* The Game Object Factory is a quick way to create all of the different sorts of core objects that Phaser uses.
*
* @class Phaser.GameObjectFactory
* @constructor
* @param {Phaser.Game} game - A reference to the currently running game.
*/
Phaser.GameObjectFactory = function (game) {
/**
* @property {Phaser.Game} game - A reference to the currently running Game.
*/
this.game = game;
/**
* @property {Phaser.World} world - A reference to the game world.
*/
this.world = this.game.world;
};
Phaser.GameObjectFactory.prototype = {
game: null,
world: null,
/**
* Adds an existing object to the game world.
* @method Phaser.GameObjectFactory#existing
* @param {*} object - An instance of Phaser.Sprite, Phaser.Button or any other display object..
* @return {*} The child that was added to the Group.
*/
existing: function (object) {
return this.world.group.add(object);
return this.world.add(object);
},
/**
* Create a new Sprite with specific position and sprite sheet key.
*
* @param x {number} X position of the new sprite.
* @param y {number} Y position of the new sprite.
* @param [key] {string|RenderTexture} The image key as defined in the Game.Cache to use as the texture for this sprite OR a RenderTexture
* @param [frame] {string|number} If the sprite uses an image from a texture atlas or sprite sheet you can pass the frame here. Either a number for a frame ID or a string for a frame name.
* @returns {Sprite} The newly created sprite object.
* @method Phaser.GameObjectFactory#sprite
* @param {number} x - X position of the new sprite.
* @param {number} y - Y position of the new sprite.
* @param {string|Phaser.RenderTexture|PIXI.Texture} key - This is the image or texture used by the Sprite during rendering. It can be a string which is a reference to the Cache entry, or an instance of a RenderTexture or PIXI.Texture.
* @param {string|number} [frame] - If the sprite uses an image from a texture atlas or sprite sheet you can pass the frame here. Either a number for a frame ID or a string for a frame name.
* @returns {Phaser.Sprite} the newly created sprite object.
*/
sprite: function (x, y, key, frame) {
return this.world.group.add(new Phaser.Sprite(this.game, x, y, key, frame));
return this.world.create(x, y, key, frame);
},
/**
* Create a new Sprite with specific position and sprite sheet key that will automatically be added as a child of the given parent.
*
* @param x {number} X position of the new sprite.
* @param y {number} Y position of the new sprite.
* @param [key] {string|RenderTexture} The image key as defined in the Game.Cache to use as the texture for this sprite OR a RenderTexture
* @param [frame] {string|number} If the sprite uses an image from a texture atlas or sprite sheet you can pass the frame here. Either a number for a frame ID or a string for a frame name.
* @returns {Sprite} The newly created sprite object.
* @method Phaser.GameObjectFactory#child
* @param {Phaser.Group} group - The Group to add this child to.
* @param {number} x - X position of the new sprite.
* @param {number} y - Y position of the new sprite.
* @param {string|RenderTexture} [key] - The image key as defined in the Game.Cache to use as the texture for this sprite OR a RenderTexture.
* @param {string|number} [frame] - If the sprite uses an image from a texture atlas or sprite sheet you can pass the frame here. Either a number for a frame ID or a string for a frame name.
* @returns {Phaser.Sprite} the newly created sprite object.
*/
child: function (parent, x, y, key, frame) {
child: function (group, x, y, key, frame) {
var child = this.world.group.add(new Phaser.Sprite(this.game, x, y, key, frame));
parent.addChild(child);
return child;
return group.create(x, y, key, frame);
},
/**
* Create a tween object for a specific object. The object can be any JavaScript object or Phaser object such as Sprite.
*
* @param obj {object} Object the tween will be run on.
* @return {Phaser.Tween} The newly created tween object.
* @method Phaser.GameObjectFactory#tween
* @param {object} obj - Object the tween will be run on.
* @return {Phaser.Tween} Description.
*/
tween: function (obj) {
@@ -60,60 +85,187 @@ Phaser.GameObjectFactory.prototype = {
},
/**
* A Group is a container for display objects that allows for fast pooling, recycling and collision checks.
*
* @method Phaser.GameObjectFactory#group
* @param {*} parent - The parent Group or DisplayObjectContainer that will hold this group, if any.
* @param {string} [name=group] - A name for this Group. Not used internally but useful for debugging.
* @return {Phaser.Group} The newly created group.
*/
group: function (parent, name) {
return new Phaser.Group(this.game, parent, name);
},
/**
* Creates a new instance of the Sound class.
*
* @method Phaser.GameObjectFactory#audio
* @param {string} key - The Game.cache key of the sound that this object will use.
* @param {number} volume - The volume at which the sound will be played.
* @param {boolean} loop - Whether or not the sound will loop.
* @return {Phaser.Sound} The newly created text object.
*/
audio: function (key, volume, loop) {
return this.game.sound.add(key, volume, loop);
},
/**
* Creates a new <code>TileSprite</code>.
*
* @method Phaser.GameObjectFactory#tileSprite
* @param {number} x - X position of the new tileSprite.
* @param {number} y - Y position of the new tileSprite.
* @param {number} width - the width of the tilesprite.
* @param {number} height - the height of the tilesprite.
* @param {string|Phaser.RenderTexture|PIXI.Texture} key - This is the image or texture used by the Sprite during rendering. It can be a string which is a reference to the Cache entry, or an instance of a RenderTexture or PIXI.Texture.
* @param {string|number} frame - If this Sprite is using part of a sprite sheet or texture atlas you can specify the exact frame to use by giving a string or numeric index.
* @return {Phaser.TileSprite} The newly created tileSprite object.
*/
tileSprite: function (x, y, width, height, key, frame) {
return this.world.group.add(new Phaser.TileSprite(this.game, x, y, width, height, key, frame));
return this.world.add(new Phaser.TileSprite(this.game, x, y, width, height, key, frame));
},
/**
* Creates a new <code>Text</code>.
*
* @method Phaser.GameObjectFactory#text
* @param {number} x - X position of the new text object.
* @param {number} y - Y position of the new text object.
* @param {string} text - The actual text that will be written.
* @param {object} style - The style object containing style attributes like font, font size , etc.
* @return {Phaser.Text} The newly created text object.
*/
text: function (x, y, text, style) {
return this.world.group.add(new Phaser.Text(this.game, x, y, text, style));
return this.world.add(new Phaser.Text(this.game, x, y, text, style));
},
/**
* Creates a new <code>Button</code> object.
*
* @method Phaser.GameObjectFactory#button
* @param {number} [x] X position of the new button object.
* @param {number} [y] Y position of the new button object.
* @param {string} [key] The image key as defined in the Game.Cache to use as the texture for this button.
* @param {function} [callback] The function to call when this button is pressed
* @param {object} [callbackContext] The context in which the callback will be called (usually 'this')
* @param {string|number} [overFrame] This is the frame or frameName that will be set when this button is in an over state. Give either a number to use a frame ID or a string for a frame name.
* @param {string|number} [outFrame] This is the frame or frameName that will be set when this button is in an out state. Give either a number to use a frame ID or a string for a frame name.
* @param {string|number} [downFrame] This is the frame or frameName that will be set when this button is in a down state. Give either a number to use a frame ID or a string for a frame name.
* @return {Phaser.Button} The newly created button object.
*/
button: function (x, y, key, callback, callbackContext, overFrame, outFrame, downFrame) {
return this.world.group.add(new Phaser.Button(this.game, x, y, key, callback, callbackContext, overFrame, outFrame, downFrame));
return this.world.add(new Phaser.Button(this.game, x, y, key, callback, callbackContext, overFrame, outFrame, downFrame));
},
/**
* Creates a new <code>Graphics</code> object.
*
* @method Phaser.GameObjectFactory#graphics
* @param {number} x - X position of the new graphics object.
* @param {number} y - Y position of the new graphics object.
* @return {Phaser.Graphics} The newly created graphics object.
*/
graphics: function (x, y) {
return this.world.group.add(new Phaser.Graphics(this.game, x, y));
return this.world.add(new Phaser.Graphics(this.game, x, y));
},
/**
* Emitter is a lightweight particle emitter. It can be used for one-time explosions or for
* continuous effects like rain and fire. All it really does is launch Particle objects out
* at set intervals, and fixes their positions and velocities accorindgly.
*
* @method Phaser.GameObjectFactory#emitter
* @param {number} [x=0] - The x coordinate within the Emitter that the particles are emitted from.
* @param {number} [y=0] - The y coordinate within the Emitter that the particles are emitted from.
* @param {number} [maxParticles=50] - The total number of particles in this emitter.
* @return {Phaser.Emitter} The newly created emitter object.
*/
emitter: function (x, y, maxParticles) {
return this.game.particles.add(new Phaser.Particles.Arcade.Emitter(this.game, x, y, maxParticles));
},
/**
* * Create a new <code>BitmapText</code>.
*
* @method Phaser.GameObjectFactory#bitmapText
* @param {number} x - X position of the new bitmapText object.
* @param {number} y - Y position of the new bitmapText object.
* @param {string} text - The actual text that will be written.
* @param {object} style - The style object containing style attributes like font, font size , etc.
* @return {Phaser.BitmapText} The newly created bitmapText object.
*/
bitmapText: function (x, y, text, style) {
return this.world.group.add(new Phaser.BitmapText(this.game, x, y, text, style));
return this.world.add(new Phaser.BitmapText(this.game, x, y, text, style));
},
tilemap: function (x, y, key, resizeWorld, tileWidth, tileHeight) {
/**
* Creates a new Tilemap object.
*
* @method Phaser.GameObjectFactory#tilemap
* @param {string} key - Asset key for the JSON file.
* @return {Phaser.Tilemap} The newly created tilemap object.
*/
tilemap: function (key) {
return this.world.group.add(new Phaser.Tilemap(this.game, key, x, y, resizeWorld, tileWidth, tileHeight));
return new Phaser.Tilemap(this.game, key);
},
/**
* Creates a new Tileset object.
*
* @method Phaser.GameObjectFactory#tileset
* @param {string} key - The image key as defined in the Game.Cache to use as the tileset.
* @return {Phaser.Tileset} The newly created tileset object.
*/
tileset: function (key) {
return this.game.cache.getTileset(key);
},
/**
* Creates a new Tilemap Layer object.
*
* @method Phaser.GameObjectFactory#tilemapLayer
* @param {number} x - X position of the new tilemapLayer.
* @param {number} y - Y position of the new tilemapLayer.
* @param {number} width - the width of the tilemapLayer.
* @param {number} height - the height of the tilemapLayer.
* @return {Phaser.TilemapLayer} The newly created tilemaplayer object.
*/
tilemapLayer: function (x, y, width, height, tileset, tilemap, layer) {
return this.world.add(new Phaser.TilemapLayer(this.game, x, y, width, height, tileset, tilemap, layer));
},
/**
* A dynamic initially blank canvas to which images can be drawn.
*
* @method Phaser.GameObjectFactory#renderTexture
* @param {string} key - Asset key for the render texture.
* @param {number} width - the width of the render texture.
* @param {number} height - the height of the render texture.
* @return {Phaser.RenderTexture} The newly created renderTexture object.
*/
renderTexture: function (key, width, height) {
var texture = new Phaser.RenderTexture(this.game, key, width, height);
@@ -122,6 +274,6 @@ Phaser.GameObjectFactory.prototype = {
return texture;
},
}
};
+65 -5
View File
@@ -1,27 +1,87 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Creates a new <code>Graphics</code> object.
*
* @class Phaser.Graphics
* @constructor
*
* @param {Phaser.Game} game Current game instance.
* @param {number} x - X position of the new graphics object.
* @param {number} y - Y position of the new graphics object.
*/
Phaser.Graphics = function (game, x, y) {
this.game = game;
PIXI.Graphics.call(this);
Phaser.Sprite.call(this, game, x, y);
/**
* @property {Description} type - Description.
*/
this.type = Phaser.GRAPHICS;
};
Phaser.Graphics.prototype = Object.create(PIXI.Graphics.prototype);
Phaser.Graphics.prototype.constructor = Phaser.Graphics;
Phaser.Graphics.prototype = Phaser.Utils.extend(true, Phaser.Graphics.prototype, Phaser.Sprite.prototype);
// Add our own custom methods
/**
* Description.
*
* @method Phaser.Sprite.prototype.destroy
*/
Phaser.Graphics.prototype.destroy = function() {
this.clear();
if (this.group)
{
this.group.remove(this);
}
this.game = null;
}
Object.defineProperty(Phaser.Graphics.prototype, 'angle', {
get: function() {
return Phaser.Math.radToDeg(this.rotation);
return Phaser.Math.wrapAngle(Phaser.Math.radToDeg(this.rotation));
},
set: function(value) {
this.rotation = Phaser.Math.degToRad(value);
this.rotation = Phaser.Math.degToRad(Phaser.Math.wrapAngle(value));
}
});
Object.defineProperty(Phaser.Graphics.prototype, 'x', {
get: function() {
return this.position.x;
},
set: function(value) {
this.position.x = value;
}
});
Object.defineProperty(Phaser.Graphics.prototype, 'y', {
get: function() {
return this.position.y;
},
set: function(value) {
this.position.y = value;
}
});
+39 -2
View File
@@ -1,20 +1,57 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* A dynamic initially blank canvas to which images can be drawn
* @class Phaser.RenderTexture
* @constructor
* @param {Phaser.Game} game - Current game instance.
* @param {string} key - Asset key for the render texture.
* @param {number} width - the width of the render texture.
* @param {number} height - the height of the render texture.
*/
Phaser.RenderTexture = function (game, key, width, height) {
/**
* @property {Phaser.Game} game - A reference to the currently running game.
*/
this.game = game;
/**
* @property {string} name - the name of the object.
*/
this.name = key;
PIXI.EventTarget.call( this );
/**
* @property {number} width - the width.
*/
this.width = width || 100;
/**
* @property {number} height - the height.
*/
this.height = height || 100;
// I know this has a typo in it, but it's because the PIXI.RenderTexture does and we need to pair-up with it
// once they update pixi to fix the typo, we'll fix it here too :)
/**
* I know this has a typo in it, but it's because the PIXI.RenderTexture does and we need to pair-up with it
* once they update pixi to fix the typo, we'll fix it here too :)
* @property {Description} indetityMatrix - Description.
*/
this.indetityMatrix = PIXI.mat3.create();
/**
* @property {Description} frame - Description.
*/
this.frame = new PIXI.Rectangle(0, 0, this.width, this.height);
/**
* @property {Description} type - Description.
*/
this.type = Phaser.RENDERTEXTURE;
if (PIXI.gl)
+776 -253
View File
File diff suppressed because it is too large Load Diff
+101 -13
View File
@@ -1,3 +1,19 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Create a new <code>Text</code>.
* @class Phaser.Text
* @constructor
* @param {Phaser.Game} game - Current game instance.
* @param {number} x - X position of the new text object.
* @param {number} y - Y position of the new text object.
* @param {string} text - The actual text that will be written.
* @param {object} style - The style object containing style attributes like font, font size ,
*/
Phaser.Text = function (game, x, y, text, style) {
x = x || 0;
@@ -6,15 +22,34 @@ Phaser.Text = function (game, x, y, text, style) {
style = style || '';
// If exists = false then the Sprite isn't updated by the core game loop or physics subsystem at all
/**
* @property {boolean} exists - Description.
* @default
*/
this.exists = true;
// This is a handy little var your game can use to determine if a sprite is alive or not, it doesn't effect rendering
/**
* @property {boolean} alive - Description.
* @default
*/
this.alive = true;
/**
* @property {Description} group - Description.
* @default
*/
this.group = null;
/**
* @property {string} name - Description.
* @default
*/
this.name = '';
/**
* @property {Phaser.Game} game - A reference to the currently running game.
*/
this.game = game;
this._text = text;
@@ -22,19 +57,33 @@ Phaser.Text = function (game, x, y, text, style) {
PIXI.Text.call(this, text, style);
/**
* @property {Description} type - Description.
*/
this.type = Phaser.TEXT;
/**
* @property {Description} position - Description.
*/
this.position.x = this.x = x;
this.position.y = this.y = y;
// Replaces the PIXI.Point with a slightly more flexible one
/**
* @property {Phaser.Point} anchor - Description.
*/
this.anchor = new Phaser.Point();
/**
* @property {Phaser.Point} scale - Description.
*/
this.scale = new Phaser.Point(1, 1);
// Influence of camera movement upon the position
this.scrollFactor = new Phaser.Point(1, 1);
// A mini cache for storing all of the calculated values
/**
* @property {Description} _cache - Description.
* @private
*/
this._cache = {
dirty: false,
@@ -42,7 +91,7 @@ Phaser.Text = function (game, x, y, text, style) {
// Transform cache
a00: 1, a01: 0, a02: x, a10: 0, a11: 1, a12: y, id: 1,
// The previous calculated position inc. camera x/y and scrollFactor
// The previous calculated position
x: -1, y: -1,
// The actual scale values based on the worldTransform
@@ -50,9 +99,12 @@ Phaser.Text = function (game, x, y, text, style) {
};
this._cache.x = this.x - (this.game.world.camera.x * this.scrollFactor.x);
this._cache.y = this.y - (this.game.world.camera.y * this.scrollFactor.y);
this._cache.x = this.x;
this._cache.y = this.y;
/**
* @property {boolean} renderable - Description.
*/
this.renderable = true;
};
@@ -60,7 +112,10 @@ Phaser.Text = function (game, x, y, text, style) {
Phaser.Text.prototype = Object.create(PIXI.Text.prototype);
Phaser.Text.prototype.constructor = Phaser.Text;
// Automatically called by World.update
/**
* Automatically called by World.update.
* @method Phaser.Text.prototype.update
*/
Phaser.Text.prototype.update = function() {
if (!this.exists)
@@ -70,8 +125,8 @@ Phaser.Text.prototype.update = function() {
this._cache.dirty = false;
this._cache.x = this.x - (this.game.world.camera.x * this.scrollFactor.x);
this._cache.y = this.y - (this.game.world.camera.y * this.scrollFactor.y);
this._cache.x = this.x;
this._cache.y = this.y;
if (this.position.x != this._cache.x || this.position.y != this._cache.y)
{
@@ -82,6 +137,39 @@ Phaser.Text.prototype.update = function() {
}
/**
* @method Phaser.Text.prototype.destroy
*/
Phaser.Text.prototype.destroy = function() {
if (this.group)
{
this.group.remove(this);
}
if (this.canvas.parentNode)
{
this.canvas.parentNode.removeChild(this.canvas);
}
else
{
this.canvas = null;
this.context = null;
}
this.exists = false;
this.group = null;
}
/**
* Get
* @returns {Description}
*//**
* Set
* @param {Description} value - Description
*/
Object.defineProperty(Phaser.Text.prototype, 'angle', {
get: function() {
@@ -94,7 +182,7 @@ Object.defineProperty(Phaser.Text.prototype, 'angle', {
});
Object.defineProperty(Phaser.Text.prototype, 'text', {
Object.defineProperty(Phaser.Text.prototype, 'content', {
get: function() {
return this._text;
@@ -106,14 +194,14 @@ Object.defineProperty(Phaser.Text.prototype, 'text', {
if (value !== this._text)
{
this._text = value;
this.dirty = true;
this.setText(value);
}
}
});
Object.defineProperty(Phaser.Text.prototype, 'style', {
Object.defineProperty(Phaser.Text.prototype, 'font', {
get: function() {
return this._style;
@@ -125,7 +213,7 @@ Object.defineProperty(Phaser.Text.prototype, 'style', {
if (value !== this._style)
{
this._style = value;
this.dirty = true;
this.setStyle(value);
}
}
+28 -10
View File
@@ -1,3 +1,21 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Create a new <code>TileSprite</code>.
* @class Phaser.Tilemap
* @constructor
* @param {Phaser.Game} game - Current game instance.
* @param {number} x - X position of the new tileSprite.
* @param {number} y - Y position of the new tileSprite.
* @param {number} width - the width of the tilesprite.
* @param {number} height - the height of the tilesprite.
* @param {string|Phaser.RenderTexture|PIXI.Texture} key - This is the image or texture used by the Sprite during rendering. It can be a string which is a reference to the Cache entry, or an instance of a RenderTexture or PIXI.Texture.
* @param {string|number} frame - If this Sprite is using part of a sprite sheet or texture atlas you can specify the exact frame to use by giving a string or numeric index.
*/
Phaser.TileSprite = function (game, x, y, width, height, key, frame) {
x = x || 0;
@@ -9,26 +27,26 @@ Phaser.TileSprite = function (game, x, y, width, height, key, frame) {
Phaser.Sprite.call(this, game, x, y, key, frame);
/**
* @property {Description} texture - Description.
*/
this.texture = PIXI.TextureCache[key];
PIXI.TilingSprite.call(this, this.texture, width, height);
/**
* @property {Description} type - Description.
*/
this.type = Phaser.TILESPRITE;
/**
* The scaling of the image that is being tiled
*
* @property tileScale
* @type Point
*/
* @property {Point} tileScale - The scaling of the image that is being tiled.
*/
this.tileScale = new Phaser.Point(1, 1);
/**
* The offset position of the image that is being tiled
*
* @property tilePosition
* @type Point
*/
* @property {Point} tilePosition - The offset position of the image that is being tiled.
*/
this.tilePosition = new Phaser.Point(0, 0);
};
+113 -141
View File
@@ -1,13 +1,18 @@
/**
* Phaser - Circle
*
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Creates a new Circle object with the center coordinate specified by the x and y parameters and the diameter specified by the diameter parameter. If you call this function without parameters, a circle with x, y, diameter and radius properties set to 0 is created.
* @class Circle
* @classdesc Phaser - Circle
* @constructor
* @param {Number} [x] The x coordinate of the center of the circle.
* @param {Number} [y] The y coordinate of the center of the circle.
* @param {Number} [diameter] The diameter of the circle.
* @return {Circle} This circle object
* @param {number} [x] The x coordinate of the center of the circle.
* @param {number} [y] The y coordinate of the center of the circle.
* @param {number} [diameter] The diameter of the circle.
* @return {Phaser.Circle} This circle object
**/
Phaser.Circle = function (x, y, diameter) {
@@ -16,23 +21,27 @@ Phaser.Circle = function (x, y, diameter) {
diameter = diameter || 0;
/**
* The x coordinate of the center of the circle
* @property x
* @type Number
* @property {number} x - The x coordinate of the center of the circle.
**/
this.x = x;
/**
* The y coordinate of the center of the circle
* @property y
* @type Number
* @property {number} y - The y coordinate of the center of the circle.
**/
this.y = y;
/**
* @property {number} _diameter - The diameter of the circle.
* @private
**/
this._diameter = diameter;
if (diameter > 0)
{
/**
* @property {number} _radius - The radius of the circle.
* @private
**/
this._radius = diameter * 0.5;
}
else
@@ -46,8 +55,8 @@ Phaser.Circle.prototype = {
/**
* The circumference of the circle.
* @method circumference
* @return {Number}
* @method Phaser.Circle#circumference
* @return {number}
**/
circumference: function () {
return 2 * (Math.PI * this._radius);
@@ -55,11 +64,11 @@ Phaser.Circle.prototype = {
/**
* Sets the members of Circle to the specified values.
* @method setTo
* @param {Number} x The x coordinate of the center of the circle.
* @param {Number} y The y coordinate of the center of the circle.
* @param {Number} diameter The diameter of the circle in pixels.
* @return {Circle} This circle object
* @method Phaser.Circle#setTo
* @param {number} x - The x coordinate of the center of the circle.
* @param {number} y - The y coordinate of the center of the circle.
* @param {number} diameter - The diameter of the circle in pixels.
* @return {Circle} This circle object.
**/
setTo: function (x, y, diameter) {
this.x = x;
@@ -71,7 +80,7 @@ Phaser.Circle.prototype = {
/**
* Copies the x, y and diameter properties from any given object to this Circle.
* @method copyFrom
* @method Phaser.Circle#copyFrom
* @param {any} source - The object to copy from.
* @return {Circle} This Circle object.
**/
@@ -81,7 +90,7 @@ Phaser.Circle.prototype = {
/**
* Copies the x, y and diameter properties from this Circle to any given object.
* @method copyTo
* @method Phaser.Circle#copyTo
* @param {any} dest - The object to copy to.
* @return {Object} This dest object.
**/
@@ -95,10 +104,10 @@ Phaser.Circle.prototype = {
/**
* Returns the distance from the center of the Circle object to the given object
* (can be Circle, Point or anything with x/y properties)
* @method distance
* @param {object} dest The target object. Must have visible x and y properties that represent the center of the object.
* @param {bool} [optional] round Round the distance to the nearest integer (default false)
* @return {Number} The distance between this Point object and the destination Point object.
* @method Phaser.Circle#distance
* @param {object} dest - The target object. Must have visible x and y properties that represent the center of the object.
* @param {boolean} [round] - Round the distance to the nearest integer (default false).
* @return {number} The distance between this Point object and the destination Point object.
*/
distance: function (dest, round) {
@@ -117,8 +126,8 @@ Phaser.Circle.prototype = {
/**
* Returns a new Circle object with the same values for the x, y, width, and height properties as this Circle object.
* @method clone
* @param {Phaser.Circle} out Optional Circle object. If given the values will be set into the object, otherwise a brand new Circle object will be created and returned.
* @method Phaser.Circle#clone
* @param {Phaser.Circle} out - Optional Circle object. If given the values will be set into the object, otherwise a brand new Circle object will be created and returned.
* @return {Phaser.Circle} The cloned Circle object.
*/
clone: function(out) {
@@ -131,10 +140,10 @@ Phaser.Circle.prototype = {
/**
* Return true if the given x/y coordinates are within this Circle object.
* @method contains
* @param {Number} x The X value of the coordinate to test.
* @param {Number} y The Y value of the coordinate to test.
* @return {bool} True if the coordinates are within this circle, otherwise false.
* @method Phaser.Circle#contains
* @param {number} x - The X value of the coordinate to test.
* @param {number} y - The Y value of the coordinate to test.
* @return {boolean} True if the coordinates are within this circle, otherwise false.
*/
contains: function (x, y) {
return Phaser.Circle.contains(this, x, y);
@@ -142,10 +151,10 @@ Phaser.Circle.prototype = {
/**
* Returns a Point object containing the coordinates of a point on the circumference of the Circle based on the given angle.
* @method circumferencePoint
* @param {Number} angle The angle in radians (unless asDegrees is true) to return the point from.
* @param {bool} asDegrees Is the given angle in radians (false) or degrees (true)?
* @param {Phaser.Point} [optional] output An optional Point object to put the result in to. If none specified a new Point object will be created.
* @method Phaser.Circle#circumferencePoint
* @param {number} angle - The angle in radians (unless asDegrees is true) to return the point from.
* @param {boolean} asDegrees - Is the given angle in radians (false) or degrees (true)?
* @param {Phaser.Point} [out] - An optional Point object to put the result in to. If none specified a new Point object will be created.
* @return {Phaser.Point} The Point object holding the result.
*/
circumferencePoint: function (angle, asDegrees, out) {
@@ -154,9 +163,9 @@ Phaser.Circle.prototype = {
/**
* Adjusts the location of the Circle object, as determined by its center coordinate, by the specified amounts.
* @method offset
* @param {Number} dx Moves the x value of the Circle object by this amount.
* @param {Number} dy Moves the y value of the Circle object by this amount.
* @method Phaser.Circle#offset
* @param {number} dx - Moves the x value of the Circle object by this amount.
* @param {number} dy - Moves the y value of the Circle object by this amount.
* @return {Circle} This Circle object.
**/
offset: function (dx, dy) {
@@ -167,7 +176,7 @@ Phaser.Circle.prototype = {
/**
* Adjusts the location of the Circle object using a Point object as a parameter. This method is similar to the Circle.offset() method, except that it takes a Point object as a parameter.
* @method offsetPoint
* @method Phaser.Circle#offsetPoint
* @param {Point} point A Point object to use to offset this Circle object (or any valid object with exposed x and y properties).
* @return {Circle} This Circle object.
**/
@@ -177,7 +186,7 @@ Phaser.Circle.prototype = {
/**
* Returns a string representation of this object.
* @method toString
* @method Phaser.Circle#toString
* @return {string} a string representation of the instance.
**/
toString: function () {
@@ -186,24 +195,17 @@ Phaser.Circle.prototype = {
};
// Getters / Setters
/**
* The largest distance between any two points on the circle. The same as the radius * 2.
* @name Phaser.Circle#diameter
* @property {number} diameter - Gets or sets the diameter of the circle.
*/
Object.defineProperty(Phaser.Circle.prototype, "diameter", {
/**
* The diameter of the circle. The largest distance between any two points on the circle. The same as the radius * 2.
* @method diameter
* @return {Number}
**/
get: function () {
return this._diameter;
},
/**
* The diameter of the circle. The largest distance between any two points on the circle. The same as the radius * 2.
* @method diameter
* @param {Number} The diameter of the circle.
**/
set: function (value) {
if (value > 0) {
this._diameter = value;
@@ -213,22 +215,17 @@ Object.defineProperty(Phaser.Circle.prototype, "diameter", {
});
/**
* The length of a line extending from the center of the circle to any point on the circle itself. The same as half the diameter.
* @name Phaser.Circle#radius
* @property {number} radius - Gets or sets the radius of the circle.
*/
Object.defineProperty(Phaser.Circle.prototype, "radius", {
/**
* The radius of the circle. The length of a line extending from the center of the circle to any point on the circle itself. The same as half the diameter.
* @method radius
* @return {Number}
**/
get: function () {
return this._radius;
},
/**
* The radius of the circle. The length of a line extending from the center of the circle to any point on the circle itself. The same as half the diameter.
* @method radius
* @param {Number} The radius of the circle.
**/
set: function (value) {
if (value > 0) {
this._radius = value;
@@ -238,22 +235,17 @@ Object.defineProperty(Phaser.Circle.prototype, "radius", {
});
/**
* The x coordinate of the leftmost point of the circle. Changing the left property of a Circle object has no effect on the x and y properties. However it does affect the diameter, whereas changing the x value does not affect the diameter property.
* @name Phaser.Circle#left
* @propety {number} left - Gets or sets the value of the leftmost point of the circle.
*/
Object.defineProperty(Phaser.Circle.prototype, "left", {
/**
* The x coordinate of the leftmost point of the circle. Changing the left property of a Circle object has no effect on the x and y properties. However it does affect the diameter, whereas changing the x value does not affect the diameter property.
* @method left
* @return {Number} The x coordinate of the leftmost point of the circle.
**/
get: function () {
return this.x - this._radius;
},
/**
* The x coordinate of the leftmost point of the circle. Changing the left property of a Circle object has no effect on the x and y properties. However it does affect the diameter, whereas changing the x value does not affect the diameter property.
* @method left
* @param {Number} The value to adjust the position of the leftmost point of the circle by.
**/
set: function (value) {
if (value > this.x) {
this._radius = 0;
@@ -265,22 +257,17 @@ Object.defineProperty(Phaser.Circle.prototype, "left", {
});
/**
* The x coordinate of the rightmost point of the circle. Changing the right property of a Circle object has no effect on the x and y properties. However it does affect the diameter, whereas changing the x value does not affect the diameter property.
* @name Phaser.Circle#right
* @property {number} right - Gets or sets the value of the rightmost point of the circle.
*/
Object.defineProperty(Phaser.Circle.prototype, "right", {
/**
* The x coordinate of the rightmost point of the circle. Changing the right property of a Circle object has no effect on the x and y properties. However it does affect the diameter, whereas changing the x value does not affect the diameter property.
* @method right
* @return {Number}
**/
get: function () {
return this.x + this._radius;
},
/**
* The x coordinate of the rightmost point of the circle. Changing the right property of a Circle object has no effect on the x and y properties. However it does affect the diameter, whereas changing the x value does not affect the diameter property.
* @method right
* @param {Number} The amount to adjust the diameter of the circle by.
**/
set: function (value) {
if (value < this.x) {
this._radius = 0;
@@ -292,22 +279,17 @@ Object.defineProperty(Phaser.Circle.prototype, "right", {
});
/**
* The sum of the y minus the radius property. Changing the top property of a Circle object has no effect on the x and y properties, but does change the diameter.
* @name Phaser.Circle#top
* @property {number} top - Gets or sets the top of the circle.
*/
Object.defineProperty(Phaser.Circle.prototype, "top", {
/**
* The sum of the y minus the radius property. Changing the top property of a Circle object has no effect on the x and y properties, but does change the diameter.
* @method bottom
* @return {Number}
**/
get: function () {
return this.y - this._radius;
},
/**
* The sum of the y minus the radius property. Changing the top property of a Circle object has no effect on the x and y properties, but does change the diameter.
* @method bottom
* @param {Number} The amount to adjust the height of the circle by.
**/
set: function (value) {
if (value > this.y) {
this._radius = 0;
@@ -319,22 +301,17 @@ Object.defineProperty(Phaser.Circle.prototype, "top", {
});
/**
* The sum of the y and radius properties. Changing the bottom property of a Circle object has no effect on the x and y properties, but does change the diameter.
* @name Phaser.Circle#bottom
* @property {number} bottom - Gets or sets the bottom of the circle.
*/
Object.defineProperty(Phaser.Circle.prototype, "bottom", {
/**
* The sum of the y and radius properties. Changing the bottom property of a Circle object has no effect on the x and y properties, but does change the diameter.
* @method bottom
* @return {Number}
**/
get: function () {
return this.y + this._radius;
},
/**
* The sum of the y and radius properties. Changing the bottom property of a Circle object has no effect on the x and y properties, but does change the diameter.
* @method bottom
* @param {Number} The value to adjust the height of the circle by.
**/
set: function (value) {
if (value < this.y) {
@@ -347,13 +324,14 @@ Object.defineProperty(Phaser.Circle.prototype, "bottom", {
});
/**
* The area of this Circle.
* @name Phaser.Circle#area
* @property {number} area - The area of this circle.
* @readonly
*/
Object.defineProperty(Phaser.Circle.prototype, "area", {
/**
* Gets the area of this Circle.
* @method area
* @return {Number} This area of this circle.
**/
get: function () {
if (this._radius > 0) {
return Math.PI * this._radius * this._radius;
@@ -364,37 +342,31 @@ Object.defineProperty(Phaser.Circle.prototype, "area", {
});
/**
* Determines whether or not this Circle object is empty. Will return a value of true if the Circle objects diameter is less than or equal to 0; otherwise false.
* If set to true it will reset all of the Circle objects properties to 0. A Circle object is empty if its diameter is less than or equal to 0.
* @name Phaser.Circle#empty
* @property {boolean} empty - Gets or sets the empty state of the circle.
*/
Object.defineProperty(Phaser.Circle.prototype, "empty", {
/**
* Determines whether or not this Circle object is empty.
* @method empty
* @return {bool} A value of true if the Circle objects diameter is less than or equal to 0; otherwise false.
**/
get: function () {
return (this._diameter == 0);
},
/**
* Sets all of the Circle objects properties to 0. A Circle object is empty if its diameter is less than or equal to 0.
* @method setEmpty
* @return {Circle} This Circle object
**/
set: function (value) {
this.setTo(0, 0, 0);
}
});
// Statics
/**
* Return true if the given x/y coordinates are within the Circle object.
* @method contains
* @param {Phaser.Circle} a The Circle to be checked.
* @param {Number} x The X value of the coordinate to test.
* @param {Number} y The Y value of the coordinate to test.
* @return {bool} True if the coordinates are within this circle, otherwise false.
* @method Phaser.Circle.contains
* @param {Phaser.Circle} a - The Circle to be checked.
* @param {number} x - The X value of the coordinate to test.
* @param {number} y - The Y value of the coordinate to test.
* @return {boolean} True if the coordinates are within this circle, otherwise false.
*/
Phaser.Circle.contains = function (a, x, y) {
@@ -414,10 +386,10 @@ Phaser.Circle.contains = function (a, x, y) {
/**
* Determines whether the two Circle objects match. This method compares the x, y and diameter properties.
* @method equals
* @param {Phaser.Circle} a The first Circle object.
* @param {Phaser.Circle} b The second Circle object.
* @return {bool} A value of true if the object has exactly the same values for the x, y and diameter properties as this Circle object; otherwise false.
* @method Phaser.Circle.equals
* @param {Phaser.Circle} a - The first Circle object.
* @param {Phaser.Circle} b - The second Circle object.
* @return {boolean} A value of true if the object has exactly the same values for the x, y and diameter properties as this Circle object; otherwise false.
*/
Phaser.Circle.equals = function (a, b) {
return (a.x == b.x && a.y == b.y && a.diameter == b.diameter);
@@ -426,10 +398,10 @@ Phaser.Circle.equals = function (a, b) {
/**
* Determines whether the two Circle objects intersect.
* This method checks the radius distances between the two Circle objects to see if they intersect.
* @method intersects
* @param {Phaser.Circle} a The first Circle object.
* @param {Phaser.Circle} b The second Circle object.
* @return {bool} A value of true if the specified object intersects with this Circle object; otherwise false.
* @method Phaser.Circle.intersects
* @param {Phaser.Circle} a - The first Circle object.
* @param {Phaser.Circle} b - The second Circle object.
* @return {boolean} A value of true if the specified object intersects with this Circle object; otherwise false.
*/
Phaser.Circle.intersects = function (a, b) {
return (Phaser.Math.distance(a.x, a.y, b.x, b.y) <= (a.radius + b.radius));
@@ -437,11 +409,11 @@ Phaser.Circle.intersects = function (a, b) {
/**
* Returns a Point object containing the coordinates of a point on the circumference of the Circle based on the given angle.
* @method circumferencePoint
* @param {Phaser.Circle} a The first Circle object.
* @param {Number} angle The angle in radians (unless asDegrees is true) to return the point from.
* @param {bool} asDegrees Is the given angle in radians (false) or degrees (true)?
* @param {Phaser.Point} [optional] output An optional Point object to put the result in to. If none specified a new Point object will be created.
* @method Phaser.Circle.circumferencePoint
* @param {Phaser.Circle} a - The first Circle object.
* @param {number} angle - The angle in radians (unless asDegrees is true) to return the point from.
* @param {boolean} asDegrees - Is the given angle in radians (false) or degrees (true)?
* @param {Phaser.Point} [out] - An optional Point object to put the result in to. If none specified a new Point object will be created.
* @return {Phaser.Point} The Point object holding the result.
*/
Phaser.Circle.circumferencePoint = function (a, angle, asDegrees, out) {
@@ -462,10 +434,10 @@ Phaser.Circle.circumferencePoint = function (a, angle, asDegrees, out) {
/**
* Checks if the given Circle and Rectangle objects intersect.
* @method intersectsRectangle
* @param {Phaser.Circle} c The Circle object to test.
* @param {Phaser.Rectangle} r The Rectangle object to test.
* @return {bool} True if the two objects intersect, otherwise false.
* @method Phaser.Circle.intersectsRectangle
* @param {Phaser.Circle} c - The Circle object to test.
* @param {Phaser.Rectangle} r - The Rectangle object to test.
* @return {boolean} True if the two objects intersect, otherwise false.
*/
Phaser.Circle.intersectsRectangle = function (c, r) {
+112 -83
View File
@@ -1,27 +1,30 @@
/**
* Phaser - Point
*
* The Point object represents a location in a two-dimensional coordinate system, where x represents the horizontal axis and y represents the vertical axis.
*
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license https://github.com/photonstorm/phaser/blob/master/license.txt MIT License
* @module Phaser
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Creates a new Point. If you pass no parameters a Point is created set to (0,0).
* @class Point
* @class Phaser.Point
* @classdesc The Point object represents a location in a two-dimensional coordinate system, where x represents the horizontal axis and y represents the vertical axis.
* @constructor
* @param {Number} x The horizontal position of this Point (default 0)
* @param {Number} y The vertical position of this Point (default 0)
* @param {number} x The horizontal position of this Point (default 0)
* @param {number} y The vertical position of this Point (default 0)
**/
Phaser.Point = function (x, y) {
x = x || 0;
y = y || 0;
/**
* @property {number} x - The x coordinate of the point.
**/
this.x = x;
/**
* @property {number} y - The y coordinate of the point.
**/
this.y = y;
};
@@ -30,7 +33,7 @@ Phaser.Point.prototype = {
/**
* Copies the x and y properties from any given object to this Point.
* @method copyFrom
* @method Phaser.Point#copyFrom
* @param {any} source - The object to copy from.
* @return {Point} This Point object.
**/
@@ -40,7 +43,7 @@ Phaser.Point.prototype = {
/**
* Inverts the x and y values of this Point
* @method invert
* @method Phaser.Point#invert
* @return {Point} This Point object.
**/
invert: function () {
@@ -49,17 +52,26 @@ Phaser.Point.prototype = {
/**
* Sets the x and y values of this Point object to the given coordinates.
* @method setTo
* @param {Number} x - The horizontal position of this point.
* @param {Number} y - The vertical position of this point.
* @method Phaser.Point#setTo
* @param {number} x - The horizontal position of this point.
* @param {number} y - The vertical position of this point.
* @return {Point} This Point object. Useful for chaining method calls.
**/
setTo: function (x, y) {
this.x = x;
this.y = y;
return this;
},
/**
* Adds the given x and y values to this Point.
* @method Phaser.Point#add
* @param {number} x - The value to add to Point.x.
* @param {number} y - The value to add to Point.y.
* @return {Phaser.Point} This Point object. Useful for chaining method calls.
**/
add: function (x, y) {
this.x += x;
@@ -68,6 +80,13 @@ Phaser.Point.prototype = {
},
/**
* Subtracts the given x and y values from this Point.
* @method Phaser.Point#subtract
* @param {number} x - The value to subtract from Point.x.
* @param {number} y - The value to subtract from Point.y.
* @return {Phaser.Point} This Point object. Useful for chaining method calls.
**/
subtract: function (x, y) {
this.x -= x;
@@ -76,6 +95,13 @@ Phaser.Point.prototype = {
},
/**
* Multiplies Point.x and Point.y by the given x and y values.
* @method Phaser.Point#multiply
* @param {number} x - The value to multiply Point.x by.
* @param {number} y - The value to multiply Point.x by.
* @return {Phaser.Point} This Point object. Useful for chaining method calls.
**/
multiply: function (x, y) {
this.x *= x;
@@ -84,6 +110,13 @@ Phaser.Point.prototype = {
},
/**
* Divides Point.x and Point.y by the given x and y values.
* @method Phaser.Point#divide
* @param {number} x - The value to divide Point.x by.
* @param {number} y - The value to divide Point.x by.
* @return {Phaser.Point} This Point object. Useful for chaining method calls.
**/
divide: function (x, y) {
this.x /= x;
@@ -93,10 +126,10 @@ Phaser.Point.prototype = {
},
/**
* Clamps the x value of this Point to be between the given min and max
* @method clampX
* @param {Number} min The minimum value to clamp this Point to
* @param {Number} max The maximum value to clamp this Point to
* Clamps the x value of this Point to be between the given min and max.
* @method Phaser.Point#clampX
* @param {number} min - The minimum value to clamp this Point to.
* @param {number} max - The maximum value to clamp this Point to.
* @return {Phaser.Point} This Point object.
*/
clampX: function (min, max) {
@@ -108,9 +141,9 @@ Phaser.Point.prototype = {
/**
* Clamps the y value of this Point to be between the given min and max
* @method clampY
* @param {Number} min The minimum value to clamp this Point to
* @param {Number} max The maximum value to clamp this Point to
* @method Phaser.Point#clampY
* @param {number} min - The minimum value to clamp this Point to.
* @param {number} max - The maximum value to clamp this Point to.
* @return {Phaser.Point} This Point object.
*/
clampY: function (min, max) {
@@ -121,10 +154,10 @@ Phaser.Point.prototype = {
},
/**
* Clamps this Point object values to be between the given min and max
* @method clamp
* @param {Number} min The minimum value to clamp this Point to
* @param {Number} max The maximum value to clamp this Point to
* Clamps this Point object values to be between the given min and max.
* @method Phaser.Point#clamp
* @param {number} min - The minimum value to clamp this Point to.
* @param {number} max - The maximum value to clamp this Point to.
* @return {Phaser.Point} This Point object.
*/
clamp: function (min, max) {
@@ -137,8 +170,8 @@ Phaser.Point.prototype = {
/**
* Creates a copy of the given Point.
* @method clone
* @param {Phaser.Point} output Optional Point object. If given the values will be set into this object, otherwise a brand new Point object will be created and returned.
* @method Phaser.Point#clone
* @param {Phaser.Point} [output] Optional Point object. If given the values will be set into this object, otherwise a brand new Point object will be created and returned.
* @return {Phaser.Point} The new Point object.
*/
clone: function (output) {
@@ -151,7 +184,7 @@ Phaser.Point.prototype = {
/**
* Copies the x and y properties from any given object to this Point.
* @method copyFrom
* @method Phaser.Point#copyFrom
* @param {any} source - The object to copy from.
* @return {Point} This Point object.
**/
@@ -161,7 +194,7 @@ Phaser.Point.prototype = {
/**
* Copies the x and y properties from this Point to any given object.
* @method copyTo
* @method Phaser.Point#copyTo
* @param {any} dest - The object to copy to.
* @return {Object} The dest object.
**/
@@ -176,10 +209,10 @@ Phaser.Point.prototype = {
/**
* Returns the distance of this Point object to the given object (can be a Circle, Point or anything with x/y properties)
* @method distance
* @param {object} dest The target object. Must have visible x and y properties that represent the center of the object.
* @param {bool} [optional] round Round the distance to the nearest integer (default false)
* @return {Number} The distance between this Point object and the destination Point object.
* @method Phaser.Point#distance
* @param {object} dest - The target object. Must have visible x and y properties that represent the center of the object.
* @param {boolean} [round] - Round the distance to the nearest integer (default false).
* @return {number} The distance between this Point object and the destination Point object.
*/
distance: function (dest, round) {
@@ -189,9 +222,9 @@ Phaser.Point.prototype = {
/**
* Determines whether the given objects x/y values are equal to this Point object.
* @method equals
* @param {Phaser.Point} a The first object to compare.
* @return {bool} A value of true if the Points are equal, otherwise false.
* @method Phaser.Point#equals
* @param {Phaser.Point} a - The first object to compare.
* @return {boolean} A value of true if the Points are equal, otherwise false.
*/
equals: function (a) {
return (a.x == this.x && a.y == this.y);
@@ -199,13 +232,13 @@ Phaser.Point.prototype = {
/**
* Rotates this Point around the x/y coordinates given to the desired angle.
* @method rotate
* @param {Number} x The x coordinate of the anchor point
* @param {Number} y The y coordinate of the anchor point
* @param {Number} angle The angle in radians (unless asDegrees is true) to rotate the Point to.
* @param {bool} asDegrees Is the given rotation in radians (false) or degrees (true)?
* @param {Number} distance An optional distance constraint between the Point and the anchor.
* @return {Phaser.Point} The modified point object
* @method Phaser.Point#rotate
* @param {number} x - The x coordinate of the anchor point
* @param {number} y - The y coordinate of the anchor point
* @param {number} angle - The angle in radians (unless asDegrees is true) to rotate the Point to.
* @param {boolean} asDegrees - Is the given rotation in radians (false) or degrees (true)?
* @param {number} [distance] - An optional distance constraint between the Point and the anchor.
* @return {Phaser.Point} The modified point object.
*/
rotate: function (x, y, angle, asDegrees, distance) {
return Phaser.Point.rotate(this, x, y, angle, asDegrees, distance);
@@ -213,8 +246,8 @@ Phaser.Point.prototype = {
/**
* Returns a string representation of this object.
* @method toString
* @return {string} a string representation of the instance.
* @method Phaser.Point#toString
* @return {string} A string representation of the instance.
**/
toString: function () {
return '[{Point (x=' + this.x + ' y=' + this.y + ')}]';
@@ -222,14 +255,12 @@ Phaser.Point.prototype = {
};
// Statics
/**
* Adds the coordinates of two points together to create a new point.
* @method add
* @param {Phaser.Point} a The first Point object.
* @param {Phaser.Point} b The second Point object.
* @param {Phaser.Point} out Optional Point to store the value in, if not supplied a new Point object will be created.
* @method Phaser.Point.add
* @param {Phaser.Point} a - The first Point object.
* @param {Phaser.Point} b - The second Point object.
* @param {Phaser.Point} [out] - Optional Point to store the value in, if not supplied a new Point object will be created.
* @return {Phaser.Point} The new Point object.
*/
Phaser.Point.add = function (a, b, out) {
@@ -245,10 +276,10 @@ Phaser.Point.add = function (a, b, out) {
/**
* Subtracts the coordinates of two points to create a new point.
* @method subtract
* @param {Phaser.Point} a The first Point object.
* @param {Phaser.Point} b The second Point object.
* @param {Phaser.Point} out Optional Point to store the value in, if not supplied a new Point object will be created.
* @method Phaser.Point.subtract
* @param {Phaser.Point} a - The first Point object.
* @param {Phaser.Point} b - The second Point object.
* @param {Phaser.Point} [out] - Optional Point to store the value in, if not supplied a new Point object will be created.
* @return {Phaser.Point} The new Point object.
*/
Phaser.Point.subtract = function (a, b, out) {
@@ -264,10 +295,10 @@ Phaser.Point.subtract = function (a, b, out) {
/**
* Multiplies the coordinates of two points to create a new point.
* @method subtract
* @param {Phaser.Point} a The first Point object.
* @param {Phaser.Point} b The second Point object.
* @param {Phaser.Point} out Optional Point to store the value in, if not supplied a new Point object will be created.
* @method Phaser.Point.multiply
* @param {Phaser.Point} a - The first Point object.
* @param {Phaser.Point} b - The second Point object.
* @param {Phaser.Point} [out] - Optional Point to store the value in, if not supplied a new Point object will be created.
* @return {Phaser.Point} The new Point object.
*/
Phaser.Point.multiply = function (a, b, out) {
@@ -283,10 +314,10 @@ Phaser.Point.multiply = function (a, b, out) {
/**
* Divides the coordinates of two points to create a new point.
* @method subtract
* @param {Phaser.Point} a The first Point object.
* @param {Phaser.Point} b The second Point object.
* @param {Phaser.Point} out Optional Point to store the value in, if not supplied a new Point object will be created.
* @method Phaser.Point.divide
* @param {Phaser.Point} a - The first Point object.
* @param {Phaser.Point} b - The second Point object.
* @param {Phaser.Point} [out] - Optional Point to store the value in, if not supplied a new Point object will be created.
* @return {Phaser.Point} The new Point object.
*/
Phaser.Point.divide = function (a, b, out) {
@@ -302,22 +333,22 @@ Phaser.Point.divide = function (a, b, out) {
/**
* Determines whether the two given Point objects are equal. They are considered equal if they have the same x and y values.
* @method equals
* @param {Phaser.Point} a The first Point object.
* @param {Phaser.Point} b The second Point object.
* @return {bool} A value of true if the Points are equal, otherwise false.
* @method Phaser.Point.equals
* @param {Phaser.Point} a - The first Point object.
* @param {Phaser.Point} b - The second Point object.
* @return {boolean} A value of true if the Points are equal, otherwise false.
*/
Phaser.Point.equals = function (a, b) {
return (a.x == b.x && a.y == b.y);
};
/**
* Returns the distance of this Point object to the given object (can be a Circle, Point or anything with x/y properties)
* @method distance
* @param {object} a The target object. Must have visible x and y properties that represent the center of the object.
* @param {object} b The target object. Must have visible x and y properties that represent the center of the object.
* @param {bool} [optional] round Round the distance to the nearest integer (default false)
* @return {Number} The distance between this Point object and the destination Point object.
* Returns the distance of this Point object to the given object (can be a Circle, Point or anything with x/y properties).
* @method Phaser.Point.distance
* @param {object} a - The target object. Must have visible x and y properties that represent the center of the object.
* @param {object} b - The target object. Must have visible x and y properties that represent the center of the object.
* @param {boolean} [round] - Round the distance to the nearest integer (default false).
* @return {number} The distance between this Point object and the destination Point object.
*/
Phaser.Point.distance = function (a, b, round) {
@@ -336,14 +367,14 @@ Phaser.Point.distance = function (a, b, round) {
/**
* Rotates a Point around the x/y coordinates given to the desired angle.
* @method rotate
* @param {Phaser.Point} a The Point object to rotate.
* @param {Number} x The x coordinate of the anchor point
* @param {Number} y The y coordinate of the anchor point
* @param {Number} angle The angle in radians (unless asDegrees is true) to rotate the Point to.
* @param {bool} asDegrees Is the given rotation in radians (false) or degrees (true)?
* @param {Number} distance An optional distance constraint between the Point and the anchor.
* @return {Phaser.Point} The modified point object
* @method Phaser.Point.rotate
* @param {Phaser.Point} a - The Point object to rotate.
* @param {number} x - The x coordinate of the anchor point
* @param {number} y - The y coordinate of the anchor point
* @param {number} angle - The angle in radians (unless asDegrees is true) to rotate the Point to.
* @param {boolean} asDegrees - Is the given rotation in radians (false) or degrees (true)?
* @param {number} distance - An optional distance constraint between the Point and the anchor.
* @return {Phaser.Point} The modified point object.
*/
Phaser.Point.rotate = function (a, x, y, angle, asDegrees, distance) {
@@ -364,5 +395,3 @@ Phaser.Point.rotate = function (a, x, y, angle, asDegrees, distance) {
return a.setTo(x + distance * Math.cos(angle), y + distance * Math.sin(angle));
};
+220 -251
View File
@@ -1,13 +1,19 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Creates a new Rectangle object with the top-left corner specified by the x and y parameters and with the specified width and height parameters. If you call this function without parameters, a Rectangle with x, y, width, and height properties set to 0 is created.
*
* @class Rectangle
* @class Phaser.Rectangle
* @constructor
* @param {Number} x The x coordinate of the top-left corner of the Rectangle.
* @param {Number} y The y coordinate of the top-left corner of the Rectangle.
* @param {Number} width The width of the Rectangle in pixels.
* @param {Number} height The height of the Rectangle in pixels.
* @return {Rectangle} This Rectangle object
* @param {number} x - The x coordinate of the top-left corner of the Rectangle.
* @param {number} y - The y coordinate of the top-left corner of the Rectangle.
* @param {number} width - The width of the Rectangle in pixels.
* @param {number} height - The height of the Rectangle in pixels.
* @return {Rectangle} This Rectangle object.
**/
Phaser.Rectangle = function (x, y, width, height) {
@@ -17,31 +23,23 @@ Phaser.Rectangle = function (x, y, width, height) {
height = height || 0;
/**
* @property x
* @type Number
* @default 0
*/
* @property {number} x - Description.
*/
this.x = x;
/**
* @property y
* @type Number
* @default 0
*/
* @property {number} y - Description.
*/
this.y = y;
/**
* @property width
* @type Number
* @default 0
*/
* @property {number} width - Description.
*/
this.width = width;
/**
* @property height
* @type Number
* @default 0
*/
* @property {number} height - Description.
*/
this.height = height;
};
@@ -50,9 +48,9 @@ Phaser.Rectangle.prototype = {
/**
* Adjusts the location of the Rectangle object, as determined by its top-left corner, by the specified amounts.
* @method offset
* @param {Number} dx Moves the x value of the Rectangle object by this amount.
* @param {Number} dy Moves the y value of the Rectangle object by this amount.
* @method Phaser.Rectangle#offset
* @param {number} dx - Moves the x value of the Rectangle object by this amount.
* @param {number} dy - Moves the y value of the Rectangle object by this amount.
* @return {Rectangle} This Rectangle object.
**/
offset: function (dx, dy) {
@@ -66,8 +64,8 @@ Phaser.Rectangle.prototype = {
/**
* Adjusts the location of the Rectangle object using a Point object as a parameter. This method is similar to the Rectangle.offset() method, except that it takes a Point object as a parameter.
* @method offsetPoint
* @param {Point} point A Point object to use to offset this Rectangle object.
* @method Phaser.Rectangle#offsetPoint
* @param {Point} point - A Point object to use to offset this Rectangle object.
* @return {Rectangle} This Rectangle object.
**/
offsetPoint: function (point) {
@@ -76,11 +74,11 @@ Phaser.Rectangle.prototype = {
/**
* Sets the members of Rectangle to the specified values.
* @method setTo
* @param {Number} x The x coordinate of the top-left corner of the Rectangle.
* @param {Number} y The y coordinate of the top-left corner of the Rectangle.
* @param {Number} width The width of the Rectangle in pixels.
* @param {Number} height The height of the Rectangle in pixels.
* @method Phaser.Rectangle#setTo
* @param {number} x - The x coordinate of the top-left corner of the Rectangle.
* @param {number} y - The y coordinate of the top-left corner of the Rectangle.
* @param {number} width - The width of the Rectangle in pixels.
* @param {number} height - The height of the Rectangle in pixels.
* @return {Rectangle} This Rectangle object
**/
setTo: function (x, y, width, height) {
@@ -96,7 +94,7 @@ Phaser.Rectangle.prototype = {
/**
* Runs Math.floor() on both the x and y values of this Rectangle.
* @method floor
* @method Phaser.Rectangle#floor
**/
floor: function () {
@@ -105,9 +103,22 @@ Phaser.Rectangle.prototype = {
},
/**
* Runs Math.floor() on the x, y, width and height values of this Rectangle.
* @method Phaser.Rectangle#floorAll
**/
floorAll: function () {
this.x = Math.floor(this.x);
this.y = Math.floor(this.y);
this.width = Math.floor(this.width);
this.height = Math.floor(this.height);
},
/**
* Copies the x, y, width and height properties from any given object to this Rectangle.
* @method copyFrom
* @method Phaser.Rectangle#copyFrom
* @param {any} source - The object to copy from.
* @return {Rectangle} This Rectangle object.
**/
@@ -117,7 +128,7 @@ Phaser.Rectangle.prototype = {
/**
* Copies the x, y, width and height properties from this Rectangle to any given object.
* @method copyTo
* @method Phaser.Rectangle#copyTo
* @param {any} source - The object to copy to.
* @return {object} This object.
**/
@@ -134,9 +145,9 @@ Phaser.Rectangle.prototype = {
/**
* Increases the size of the Rectangle object by the specified amounts. The center point of the Rectangle object stays the same, and its size increases to the left and right by the dx value, and to the top and the bottom by the dy value.
* @method inflate
* @param {Number} dx The amount to be added to the left side of the Rectangle.
* @param {Number} dy The amount to be added to the bottom side of the Rectangle.
* @method Phaser.Rectangle#inflate
* @param {number} dx - The amount to be added to the left side of the Rectangle.
* @param {number} dy - The amount to be added to the bottom side of the Rectangle.
* @return {Phaser.Rectangle} This Rectangle object.
*/
inflate: function (dx, dy) {
@@ -145,9 +156,9 @@ Phaser.Rectangle.prototype = {
/**
* The size of the Rectangle object, expressed as a Point object with the values of the width and height properties.
* @method size
* @param {Phaser.Point} output Optional Point object. If given the values will be set into the object, otherwise a brand new Point object will be created and returned.
* @return {Phaser.Point} The size of the Rectangle object
* @method Phaser.Rectangle#size
* @param {Phaser.Point} [output] - Optional Point object. If given the values will be set into the object, otherwise a brand new Point object will be created and returned.
* @return {Phaser.Point} The size of the Rectangle object.
*/
size: function (output) {
return Phaser.Rectangle.size(this, output);
@@ -155,9 +166,9 @@ Phaser.Rectangle.prototype = {
/**
* Returns a new Rectangle object with the same values for the x, y, width, and height properties as the original Rectangle object.
* @method clone
* @param {Phaser.Rectangle} output Optional Rectangle object. If given the values will be set into the object, otherwise a brand new Rectangle object will be created and returned.
* @return {Phaser.Rectangle}
* @method Phaser.Rectangle#clone
* @param {Phaser.Rectangle} [output] - Optional Rectangle object. If given the values will be set into the object, otherwise a brand new Rectangle object will be created and returned.
* @return {Phaser.Rectangle}
*/
clone: function (output) {
return Phaser.Rectangle.clone(this, output);
@@ -165,10 +176,10 @@ Phaser.Rectangle.prototype = {
/**
* Determines whether the specified coordinates are contained within the region defined by this Rectangle object.
* @method contains
* @param {Number} x The x coordinate of the point to test.
* @param {Number} y The y coordinate of the point to test.
* @return {bool} A value of true if the Rectangle object contains the specified point; otherwise false.
* @method Phaser.Rectangle#contains
* @param {number} x - The x coordinate of the point to test.
* @param {number} y - The y coordinate of the point to test.
* @return {boolean} A value of true if the Rectangle object contains the specified point; otherwise false.
*/
contains: function (x, y) {
return Phaser.Rectangle.contains(this, x, y);
@@ -177,9 +188,9 @@ Phaser.Rectangle.prototype = {
/**
* Determines whether the first Rectangle object is fully contained within the second Rectangle object.
* A Rectangle object is said to contain another if the second Rectangle object falls entirely within the boundaries of the first.
* @method containsRect
* @param {Phaser.Rectangle} b The second Rectangle object.
* @return {bool} A value of true if the Rectangle object contains the specified point; otherwise false.
* @method Phaser.Rectangle#containsRect
* @param {Phaser.Rectangle} b - The second Rectangle object.
* @return {boolean} A value of true if the Rectangle object contains the specified point; otherwise false.
*/
containsRect: function (b) {
return Phaser.Rectangle.containsRect(this, b);
@@ -188,9 +199,9 @@ Phaser.Rectangle.prototype = {
/**
* Determines whether the two Rectangles are equal.
* This method compares the x, y, width and height properties of each Rectangle.
* @method equals
* @param {Phaser.Rectangle} b The second Rectangle object.
* @return {bool} A value of true if the two Rectangles have exactly the same values for the x, y, width and height properties; otherwise false.
* @method Phaser.Rectangle#equals
* @param {Phaser.Rectangle} b - The second Rectangle object.
* @return {boolean} A value of true if the two Rectangles have exactly the same values for the x, y, width and height properties; otherwise false.
*/
equals: function (b) {
return Phaser.Rectangle.equals(this, b);
@@ -198,9 +209,9 @@ Phaser.Rectangle.prototype = {
/**
* If the Rectangle object specified in the toIntersect parameter intersects with this Rectangle object, returns the area of intersection as a Rectangle object. If the Rectangles do not intersect, this method returns an empty Rectangle object with its properties set to 0.
* @method intersection
* @param {Phaser.Rectangle} b The second Rectangle object.
* @param {Phaser.Rectangle} output Optional Rectangle object. If given the intersection values will be set into this object, otherwise a brand new Rectangle object will be created and returned.
* @method Phaser.Rectangle#intersection
* @param {Phaser.Rectangle} b - The second Rectangle object.
* @param {Phaser.Rectangle} out - Optional Rectangle object. If given the intersection values will be set into this object, otherwise a brand new Rectangle object will be created and returned.
* @return {Phaser.Rectangle} A Rectangle object that equals the area of intersection. If the Rectangles do not intersect, this method returns an empty Rectangle object; that is, a Rectangle with its x, y, width, and height properties set to 0.
*/
intersection: function (b, out) {
@@ -210,10 +221,10 @@ Phaser.Rectangle.prototype = {
/**
* Determines whether the two Rectangles intersect with each other.
* This method checks the x, y, width, and height properties of the Rectangles.
* @method intersects
* @param {Phaser.Rectangle} b The second Rectangle object.
* @param {Number} tolerance A tolerance value to allow for an intersection test with padding, default to 0
* @return {bool} A value of true if the specified object intersects with this Rectangle object; otherwise false.
* @method Phaser.Rectangle#intersects
* @param {Phaser.Rectangle} b - The second Rectangle object.
* @param {number} tolerance - A tolerance value to allow for an intersection test with padding, default to 0.
* @return {boolean} A value of true if the specified object intersects with this Rectangle object; otherwise false.
*/
intersects: function (b, tolerance) {
return Phaser.Rectangle.intersects(this, b, tolerance);
@@ -221,13 +232,13 @@ Phaser.Rectangle.prototype = {
/**
* Determines whether the object specified intersects (overlaps) with the given values.
* @method intersectsRaw
* @param {Number} left
* @param {Number} right
* @param {Number} top
* @param {Number} bottomt
* @param {Number} tolerance A tolerance value to allow for an intersection test with padding, default to 0
* @return {bool} A value of true if the specified object intersects with the Rectangle; otherwise false.
* @method Phaser.Rectangle#intersectsRaw
* @param {number} left - Description.
* @param {number} right - Description.
* @param {number} top - Description.
* @param {number} bottomt - Description.
* @param {number} tolerance - A tolerance value to allow for an intersection test with padding, default to 0
* @return {boolean} A value of true if the specified object intersects with the Rectangle; otherwise false.
*/
intersectsRaw: function (left, right, top, bottom, tolerance) {
return Phaser.Rectangle.intersectsRaw(this, left, right, top, bottom, tolerance);
@@ -235,9 +246,9 @@ Phaser.Rectangle.prototype = {
/**
* Adds two Rectangles together to create a new Rectangle object, by filling in the horizontal and vertical space between the two Rectangles.
* @method union
* @param {Phaser.Rectangle} b The second Rectangle object.
* @param {Phaser.Rectangle} output Optional Rectangle object. If given the new values will be set into this object, otherwise a brand new Rectangle object will be created and returned.
* @method Phaser.Rectangle#union
* @param {Phaser.Rectangle} b - The second Rectangle object.
* @param {Phaser.Rectangle} [out] - Optional Rectangle object. If given the new values will be set into this object, otherwise a brand new Rectangle object will be created and returned.
* @return {Phaser.Rectangle} A Rectangle object that is the union of the two Rectangles.
*/
union: function (b, out) {
@@ -246,8 +257,8 @@ Phaser.Rectangle.prototype = {
/**
* Returns a string representation of this object.
* @method toString
* @return {string} a string representation of the instance.
* @method Phaser.Rectangle#toString
* @return {string} A string representation of the instance.
**/
toString: function () {
return "[{Rectangle (x=" + this.x + " y=" + this.y + " width=" + this.width + " height=" + this.height + " empty=" + this.empty + ")}]";
@@ -255,50 +266,43 @@ Phaser.Rectangle.prototype = {
};
// Getters / Setters
/**
* @name Phaser.Rectangle#halfWidth
* @property {number} halfWidth - Half of the width of the Rectangle.
* @readonly
*/
Object.defineProperty(Phaser.Rectangle.prototype, "halfWidth", {
/**
* Half of the width of the Rectangle
* @property halfWidth
* @type Number
**/
get: function () {
return Math.round(this.width / 2);
}
});
/**
* @name Phaser.Rectangle#halfHeight
* @property {number} halfHeight - Half of the height of the Rectangle.
* @readonly
*/
Object.defineProperty(Phaser.Rectangle.prototype, "halfHeight", {
/**
* Half of the height of the Rectangle
* @property halfHeight
* @type Number
**/
get: function () {
return Math.round(this.height / 2);
}
});
/**
* The sum of the y and height properties. Changing the bottom property of a Rectangle object has no effect on the x, y and width properties, but does change the height property.
* @name Phaser.Rectangle#bottom
* @property {number} bottom - The sum of the y and height properties.
*/
Object.defineProperty(Phaser.Rectangle.prototype, "bottom", {
/**
* The sum of the y and height properties. Changing the bottom property of a Rectangle object has no effect on the x, y and width properties, but does change the height property.
* @method bottom
* @return {Number}
**/
get: function () {
return this.y + this.height;
},
/**
* The sum of the y and height properties. Changing the bottom property of a Rectangle object has no effect on the x, y and width properties, but does change the height property.
* @method bottom
* @param {Number} value
**/
set: function (value) {
if (value <= this.y) {
this.height = 0;
@@ -309,21 +313,17 @@ Object.defineProperty(Phaser.Rectangle.prototype, "bottom", {
});
/**
* The location of the Rectangles bottom right corner as a Point object.
* @name Phaser.Rectangle#bottom
* @property {Phaser.Point} bottomRight - Gets or sets the location of the Rectangles bottom right corner as a Point object.
*/
Object.defineProperty(Phaser.Rectangle.prototype, "bottomRight", {
/**
* Get the location of the Rectangles bottom right corner as a Point object.
* @return {Phaser.Point} The new Point object.
*/
get: function () {
return new Phaser.Point(this.right, this.bottom);
},
/**
* Sets the bottom-right corner of the Rectangle, determined by the values of the given Point object.
* @method bottomRight
* @param {Point} value
**/
set: function (value) {
this.right = value.x;
this.bottom = value.y;
@@ -331,23 +331,17 @@ Object.defineProperty(Phaser.Rectangle.prototype, "bottomRight", {
});
/**
* The x coordinate of the left of the Rectangle. Changing the left property of a Rectangle object has no effect on the y and height properties. However it does affect the width property, whereas changing the x value does not affect the width property.
* @name Phaser.Rectangle#left
* @property {number} left - The x coordinate of the left of the Rectangle.
*/
Object.defineProperty(Phaser.Rectangle.prototype, "left", {
/**
* The x coordinate of the left of the Rectangle. Changing the left property of a Rectangle object has no effect on the y and height properties. However it does affect the width property, whereas changing the x value does not affect the width property.
* @method left
* @ return {number}
**/
get: function () {
return this.x;
},
/**
* The x coordinate of the left of the Rectangle. Changing the left property of a Rectangle object has no effect on the y and height properties.
* However it does affect the width, whereas changing the x value does not affect the width property.
* @method left
* @param {Number} value
**/
set: function (value) {
if (value >= this.right) {
this.width = 0;
@@ -359,24 +353,17 @@ Object.defineProperty(Phaser.Rectangle.prototype, "left", {
});
/**
* The sum of the x and width properties. Changing the right property of a Rectangle object has no effect on the x, y and height properties, however it does affect the width property.
* @name Phaser.Rectangle#right
* @property {number} right - The sum of the x and width properties.
*/
Object.defineProperty(Phaser.Rectangle.prototype, "right", {
/**
* The sum of the x and width properties. Changing the right property of a Rectangle object has no effect on the x, y and height properties.
* However it does affect the width property.
* @method right
* @return {Number}
**/
get: function () {
return this.x + this.width;
},
/**
* The sum of the x and width properties. Changing the right property of a Rectangle object has no effect on the x, y and height properties.
* However it does affect the width property.
* @method right
* @param {Number} value
**/
set: function (value) {
if (value <= this.x) {
this.width = 0;
@@ -387,94 +374,80 @@ Object.defineProperty(Phaser.Rectangle.prototype, "right", {
});
/**
* The volume of the Rectangle derived from width * height.
* @name Phaser.Rectangle#volume
* @property {number} volume - The volume of the Rectangle derived from width * height.
* @readonly
*/
Object.defineProperty(Phaser.Rectangle.prototype, "volume", {
/**
* The volume of the Rectangle derived from width * height
* @method volume
* @return {Number}
**/
get: function () {
return this.width * this.height;
}
});
/**
* The perimeter size of the Rectangle. This is the sum of all 4 sides.
* @name Phaser.Rectangle#perimeter
* @property {number} perimeter - The perimeter size of the Rectangle. This is the sum of all 4 sides.
* @readonly
*/
Object.defineProperty(Phaser.Rectangle.prototype, "perimeter", {
/**
* The perimeter size of the Rectangle. This is the sum of all 4 sides.
* @method perimeter
* @return {Number}
**/
get: function () {
return (this.width * 2) + (this.height * 2);
}
});
/**
* The x coordinate of the center of the Rectangle.
* @name Phaser.Rectangle#centerX
* @property {number} centerX - The x coordinate of the center of the Rectangle.
*/
Object.defineProperty(Phaser.Rectangle.prototype, "centerX", {
/**
* The x coordinate of the center of the Rectangle.
* @method centerX
* @return {Number}
**/
get: function () {
return this.x + this.halfWidth;
},
/**
* The x coordinate of the center of the Rectangle.
* @method centerX
* @param {Number} value
**/
set: function (value) {
this.x = value - this.halfWidth;
}
});
/**
* The y coordinate of the center of the Rectangle.
* @name Phaser.Rectangle#centerY
* @property {number} centerY - The y coordinate of the center of the Rectangle.
*/
Object.defineProperty(Phaser.Rectangle.prototype, "centerY", {
/**
* The y coordinate of the center of the Rectangle.
* @method centerY
* @return {Number}
**/
get: function () {
return this.y + this.halfHeight;
},
/**
* The y coordinate of the center of the Rectangle.
* @method centerY
* @param {Number} value
**/
set: function (value) {
this.y = value - this.halfHeight;
}
});
/**
* The y coordinate of the top of the Rectangle. Changing the top property of a Rectangle object has no effect on the x and width properties.
* However it does affect the height property, whereas changing the y value does not affect the height property.
* @name Phaser.Rectangle#top
* @property {number} top - The y coordinate of the top of the Rectangle.
*/
Object.defineProperty(Phaser.Rectangle.prototype, "top", {
/**
* The y coordinate of the top of the Rectangle. Changing the top property of a Rectangle object has no effect on the x and width properties.
* However it does affect the height property, whereas changing the y value does not affect the height property.
* @method top
* @return {Number}
**/
get: function () {
return this.y;
},
/**
* The y coordinate of the top of the Rectangle. Changing the top property of a Rectangle object has no effect on the x and width properties.
* However it does affect the height property, whereas changing the y value does not affect the height property.
* @method top
* @param {Number} value
**/
set: function (value) {
if (value >= this.bottom) {
this.height = 0;
@@ -486,21 +459,17 @@ Object.defineProperty(Phaser.Rectangle.prototype, "top", {
});
/**
* The location of the Rectangles top left corner as a Point object.
* @name Phaser.Rectangle#topLeft
* @property {Phaser.Point} topLeft - The location of the Rectangles top left corner as a Point object.
*/
Object.defineProperty(Phaser.Rectangle.prototype, "topLeft", {
/**
* Get the location of the Rectangles top left corner as a Point object.
* @return {Phaser.Point} The new Point object.
*/
get: function () {
return new Phaser.Point(this.x, this.y);
},
/**
* The location of the Rectangles top-left corner, determined by the x and y coordinates of the Point.
* @method topLeft
* @param {Point} value
**/
set: function (value) {
this.x = value.x;
this.y = value.y;
@@ -508,36 +477,30 @@ Object.defineProperty(Phaser.Rectangle.prototype, "topLeft", {
});
/**
* Determines whether or not this Rectangle object is empty. A Rectangle object is empty if its width or height is less than or equal to 0.
* If set to true then all of the Rectangle properties are set to 0.
* @name Phaser.Rectangle#empty
* @property {boolean} empty - Gets or sets the Rectangles empty state.
*/
Object.defineProperty(Phaser.Rectangle.prototype, "empty", {
/**
* Determines whether or not this Rectangle object is empty.
* @method isEmpty
* @return {bool} A value of true if the Rectangle objects width or height is less than or equal to 0; otherwise false.
**/
get: function () {
return (!this.width || !this.height);
},
/**
* Sets all of the Rectangle object's properties to 0. A Rectangle object is empty if its width or height is less than or equal to 0.
* @method setEmpty
* @return {Rectangle} This Rectangle object
**/
set: function (value) {
this.setTo(0, 0, 0, 0);
}
});
// Statics
/**
* Increases the size of the Rectangle object by the specified amounts. The center point of the Rectangle object stays the same, and its size increases to the left and right by the dx value, and to the top and the bottom by the dy value.
* @method inflate
* @param {Phaser.Rectangle} a The Rectangle object.
* @param {Number} dx The amount to be added to the left side of the Rectangle.
* @param {Number} dy The amount to be added to the bottom side of the Rectangle.
* @method Phaser.Rectangle.inflate
* @param {Phaser.Rectangle} a - The Rectangle object.
* @param {number} dx - The amount to be added to the left side of the Rectangle.
* @param {number} dy - The amount to be added to the bottom side of the Rectangle.
* @return {Phaser.Rectangle} This Rectangle object.
*/
Phaser.Rectangle.inflate = function (a, dx, dy) {
@@ -550,20 +513,20 @@ Phaser.Rectangle.inflate = function (a, dx, dy) {
/**
* Increases the size of the Rectangle object. This method is similar to the Rectangle.inflate() method except it takes a Point object as a parameter.
* @method inflatePoint
* @param {Phaser.Rectangle} a The Rectangle object.
* @param {Phaser.Point} point The x property of this Point object is used to increase the horizontal dimension of the Rectangle object. The y property is used to increase the vertical dimension of the Rectangle object.
* @method Phaser.Rectangle.inflatePoint
* @param {Phaser.Rectangle} a - The Rectangle object.
* @param {Phaser.Point} point - The x property of this Point object is used to increase the horizontal dimension of the Rectangle object. The y property is used to increase the vertical dimension of the Rectangle object.
* @return {Phaser.Rectangle} The Rectangle object.
*/
Phaser.Rectangle.inflatePoint = function (a, point) {
return Phaser.Phaser.Rectangle.inflate(a, point.x, point.y);
return Phaser.Rectangle.inflate(a, point.x, point.y);
};
/**
* The size of the Rectangle object, expressed as a Point object with the values of the width and height properties.
* @method size
* @param {Phaser.Rectangle} a The Rectangle object.
* @param {Phaser.Point} output Optional Point object. If given the values will be set into the object, otherwise a brand new Point object will be created and returned.
* @method Phaser.Rectangle.size
* @param {Phaser.Rectangle} a - The Rectangle object.
* @param {Phaser.Point} [output] - Optional Point object. If given the values will be set into the object, otherwise a brand new Point object will be created and returned.
* @return {Phaser.Point} The size of the Rectangle object
*/
Phaser.Rectangle.size = function (a, output) {
@@ -573,9 +536,9 @@ Phaser.Rectangle.size = function (a, output) {
/**
* Returns a new Rectangle object with the same values for the x, y, width, and height properties as the original Rectangle object.
* @method clone
* @param {Phaser.Rectangle} a The Rectangle object.
* @param {Phaser.Rectangle} output Optional Rectangle object. If given the values will be set into the object, otherwise a brand new Rectangle object will be created and returned.
* @method Phaser.Rectangle.clone
* @param {Phaser.Rectangle} a - The Rectangle object.
* @param {Phaser.Rectangle} [output] - Optional Rectangle object. If given the values will be set into the object, otherwise a brand new Rectangle object will be created and returned.
* @return {Phaser.Rectangle}
*/
Phaser.Rectangle.clone = function (a, output) {
@@ -585,34 +548,38 @@ Phaser.Rectangle.clone = function (a, output) {
/**
* Determines whether the specified coordinates are contained within the region defined by this Rectangle object.
* @method contains
* @param {Phaser.Rectangle} a The Rectangle object.
* @param {Number} x The x coordinate of the point to test.
* @param {Number} y The y coordinate of the point to test.
* @return {bool} A value of true if the Rectangle object contains the specified point; otherwise false.
* @method Phaser.Rectangle.contains
* @param {Phaser.Rectangle} a - The Rectangle object.
* @param {number} x - The x coordinate of the point to test.
* @param {number} y - The y coordinate of the point to test.
* @return {boolean} A value of true if the Rectangle object contains the specified point; otherwise false.
*/
Phaser.Rectangle.contains = function (a, x, y) {
return (x >= a.x && x <= a.right && y >= a.y && y <= a.bottom);
};
Phaser.Rectangle.containsRaw = function (rx, ry, rw, rh, x, y) {
return (x >= rx && x <= (rx + rw) && y >= ry && y <= (ry + rh));
};
/**
* Determines whether the specified point is contained within the rectangular region defined by this Rectangle object. This method is similar to the Rectangle.contains() method, except that it takes a Point object as a parameter.
* @method containsPoint
* @param {Phaser.Rectangle} a The Rectangle object.
* @param {Phaser.Point} point The point object being checked. Can be Point or any object with .x and .y values.
* @return {bool} A value of true if the Rectangle object contains the specified point; otherwise false.
* @method Phaser.Rectangle.containsPoint
* @param {Phaser.Rectangle} a - The Rectangle object.
* @param {Phaser.Point} point - The point object being checked. Can be Point or any object with .x and .y values.
* @return {boolean} A value of true if the Rectangle object contains the specified point; otherwise false.
*/
Phaser.Rectangle.containsPoint = function (a, point) {
return Phaser.Phaser.Rectangle.contains(a, point.x, point.y);
return Phaser.Rectangle.contains(a, point.x, point.y);
};
/**
* Determines whether the first Rectangle object is fully contained within the second Rectangle object.
* A Rectangle object is said to contain another if the second Rectangle object falls entirely within the boundaries of the first.
* @method containsRect
* @param {Phaser.Rectangle} a The first Rectangle object.
* @param {Phaser.Rectangle} b The second Rectangle object.
* @return {bool} A value of true if the Rectangle object contains the specified point; otherwise false.
* @method Phaser.Rectangle.containsRect
* @param {Phaser.Rectangle} a - The first Rectangle object.
* @param {Phaser.Rectangle} b - The second Rectangle object.
* @return {boolean} A value of true if the Rectangle object contains the specified point; otherwise false.
*/
Phaser.Rectangle.containsRect = function (a, b) {
@@ -629,10 +596,10 @@ Phaser.Rectangle.containsRect = function (a, b) {
/**
* Determines whether the two Rectangles are equal.
* This method compares the x, y, width and height properties of each Rectangle.
* @method equals
* @param {Phaser.Rectangle} a The first Rectangle object.
* @param {Phaser.Rectangle} b The second Rectangle object.
* @return {bool} A value of true if the two Rectangles have exactly the same values for the x, y, width and height properties; otherwise false.
* @method Phaser.Rectangle.equals
* @param {Phaser.Rectangle} a - The first Rectangle object.
* @param {Phaser.Rectangle} b - The second Rectangle object.
* @return {boolean} A value of true if the two Rectangles have exactly the same values for the x, y, width and height properties; otherwise false.
*/
Phaser.Rectangle.equals = function (a, b) {
return (a.x == b.x && a.y == b.y && a.width == b.width && a.height == b.height);
@@ -640,10 +607,10 @@ Phaser.Rectangle.equals = function (a, b) {
/**
* If the Rectangle object specified in the toIntersect parameter intersects with this Rectangle object, returns the area of intersection as a Rectangle object. If the Rectangles do not intersect, this method returns an empty Rectangle object with its properties set to 0.
* @method intersection
* @param {Phaser.Rectangle} a The first Rectangle object.
* @param {Phaser.Rectangle} b The second Rectangle object.
* @param {Phaser.Rectangle} output Optional Rectangle object. If given the intersection values will be set into this object, otherwise a brand new Rectangle object will be created and returned.
* @method Phaser.Rectangle.intersection
* @param {Phaser.Rectangle} a - The first Rectangle object.
* @param {Phaser.Rectangle} b - The second Rectangle object.
* @param {Phaser.Rectangle} [out] - Optional Rectangle object. If given the intersection values will be set into this object, otherwise a brand new Rectangle object will be created and returned.
* @return {Phaser.Rectangle} A Rectangle object that equals the area of intersection. If the Rectangles do not intersect, this method returns an empty Rectangle object; that is, a Rectangle with its x, y, width, and height properties set to 0.
*/
Phaser.Rectangle.intersection = function (a, b, out) {
@@ -665,29 +632,31 @@ Phaser.Rectangle.intersection = function (a, b, out) {
/**
* Determines whether the two Rectangles intersect with each other.
* This method checks the x, y, width, and height properties of the Rectangles.
* @method intersects
* @param {Phaser.Rectangle} a The first Rectangle object.
* @param {Phaser.Rectangle} b The second Rectangle object.
* @param {Number} tolerance A tolerance value to allow for an intersection test with padding, default to 0
* @return {bool} A value of true if the specified object intersects with this Rectangle object; otherwise false.
* @method Phaser.Rectangle.intersects
* @param {Phaser.Rectangle} a - The first Rectangle object.
* @param {Phaser.Rectangle} b - The second Rectangle object.
* @return {boolean} A value of true if the specified object intersects with this Rectangle object; otherwise false.
*/
Phaser.Rectangle.intersects = function (a, b, tolerance) {
Phaser.Rectangle.intersects = function (a, b) {
tolerance = tolerance || 0;
return (a.x < b.right && b.x < a.right && a.y < b.bottom && b.y < a.bottom);
return !(a.x > b.right + tolerance || a.right < b.x - tolerance || a.y > b.bottom + tolerance || a.bottom < b.y - tolerance);
// return (a.x <= b.right && b.x <= a.right && a.y <= b.bottom && b.y <= a.bottom);
// return (a.left <= b.right && b.left <= a.right && a.top <= b.bottom && b.top <= a.bottom);
// return !(a.x > b.right + tolerance || a.right < b.x - tolerance || a.y > b.bottom + tolerance || a.bottom < b.y - tolerance);
};
/**
* Determines whether the object specified intersects (overlaps) with the given values.
* @method intersectsRaw
* @param {Number} left
* @param {Number} right
* @param {Number} top
* @param {Number} bottomt
* @param {Number} tolerance A tolerance value to allow for an intersection test with padding, default to 0
* @return {bool} A value of true if the specified object intersects with the Rectangle; otherwise false.
* @method Phaser.Rectangle.intersectsRaw
* @param {number} left - Description.
* @param {number} right - Description.
* @param {number} top - Description.
* @param {number} bottom - Description.
* @param {number} tolerance - A tolerance value to allow for an intersection test with padding, default to 0
* @return {boolean} A value of true if the specified object intersects with the Rectangle; otherwise false.
*/
Phaser.Rectangle.intersectsRaw = function (a, left, right, top, bottom, tolerance) {
@@ -699,16 +668,16 @@ Phaser.Rectangle.intersectsRaw = function (a, left, right, top, bottom, toleranc
/**
* Adds two Rectangles together to create a new Rectangle object, by filling in the horizontal and vertical space between the two Rectangles.
* @method union
* @param {Phaser.Rectangle} a The first Rectangle object.
* @param {Phaser.Rectangle} b The second Rectangle object.
* @param {Phaser.Rectangle} output Optional Rectangle object. If given the new values will be set into this object, otherwise a brand new Rectangle object will be created and returned.
* @method Phaser.Rectangle.union
* @param {Phaser.Rectangle} a - The first Rectangle object.
* @param {Phaser.Rectangle} b - The second Rectangle object.
* @param {Phaser.Rectangle} [out] - Optional Rectangle object. If given the new values will be set into this object, otherwise a brand new Rectangle object will be created and returned.
* @return {Phaser.Rectangle} A Rectangle object that is the union of the two Rectangles.
*/
Phaser.Rectangle.union = function (a, b, out) {
if (typeof out === "undefined") { out = new Phaser.Rectangle(); }
return out.setTo(Math.min(a.x, b.x), Math.min(a.y, b.y), Math.max(a.right, b.right), Math.max(a.bottom, b.bottom));
return out.setTo(Math.min(a.x, b.x), Math.min(a.y, b.y), Math.max(a.right, b.right) - Math.min(a.left, b.left), Math.max(a.bottom, b.bottom) - Math.min(a.top, b.top));
};
+278 -167
View File
@@ -1,265 +1,363 @@
/**
* Phaser.Input
*
* A game specific Input manager that looks after the mouse, keyboard and touch objects.
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Constructor for Phaser Input.
* @class Phaser.Input
* @classdesc A game specific Input manager that looks after the mouse, keyboard and touch objects.
* This is updated by the core game loop.
* @constructor
* @param {Phaser.Game} game - Current game instance.
*/
Phaser.Input = function (game) {
/**
* @property {Phaser.Game} game - A reference to the currently running game.
*/
this.game = game;
/**
* @property {Description} hitCanvas - Description.
* @default
*/
this.hitCanvas = null;
/**
* @property {Description} hitContext - Description.
* @default
*/
this.hitContext = null;
};
/**
* @constant
* @type {number}
*/
Phaser.Input.MOUSE_OVERRIDES_TOUCH = 0;
/**
* @constant
* @type {number}
*/
Phaser.Input.TOUCH_OVERRIDES_MOUSE = 1;
/**
* @constant
* @type {number}
*/
Phaser.Input.MOUSE_TOUCH_COMBINE = 2;
Phaser.Input.prototype = {
/**
* @property {Phaser.Game} game
*/
game: null,
/**
* How often should the input pointers be checked for updates?
* A value of 0 means every single frame (60fps), a value of 1 means every other frame (30fps) and so on.
* @type {number}
* @property {number} pollRate
* @default
*/
pollRate: 0,
/**
* @property {number} _pollCounter - Description.
* @private
* @default
*/
_pollCounter: 0,
/**
* A vector object representing the previous position of the Pointer.
* @property vector
* @type {Vec2}
**/
* @property {Vec2} vector
* @private
* @default
*/
_oldPosition: null,
/**
* X coordinate of the most recent Pointer event
* @type {Number}
* @property {number} _x
* @private
* @default
*/
_x: 0,
/**
* X coordinate of the most recent Pointer event
* @type {Number}
* Y coordinate of the most recent Pointer event
* @property {number} _y
* @private
* @default
*/
_y: 0,
/**
* You can disable all Input by setting Input.disabled: true. While set all new input related events will be ignored.
* If you need to disable just one type of input, for example mouse, use Input.mouse.disabled: true instead
* @type {bool}
* @property {boolean} disabled
* @default
*/
disabled: false,
/**
* Controls the expected behaviour when using a mouse and touch together on a multi-input device
* Controls the expected behaviour when using a mouse and touch together on a multi-input device.
* @property {Description} multiInputOverride
*/
multiInputOverride: Phaser.Input.MOUSE_TOUCH_COMBINE,
/**
* A vector object representing the current position of the Pointer.
* @property vector
* @type {Vec2}
**/
* @property {Phaser.Point} position
* @default
*/
position: null,
/**
* A vector object representing the speed of the Pointer. Only really useful in single Pointer games,
* otherwise see the Pointer objects directly.
* @property vector
* @type {Vec2}
**/
* @property {Phaser.Point} speed
* @default
*/
speed: null,
/**
* A Circle object centered on the x/y screen coordinates of the Input.
* Default size of 44px (Apples recommended "finger tip" size) but can be changed to anything
* @property circle
* @type {Circle}
**/
* Default size of 44px (Apples recommended "finger tip" size) but can be changed to anything.
* @property {Phaser.Circle} circle
* @default
*/
circle: null,
/**
* The scale by which all input coordinates are multiplied, calculated by the StageScaleMode.
* In an un-scaled game the values will be x: 1 and y: 1.
* @type {Vec2}
* @property {Phaser.Point} scale
* @default
*/
scale: null,
/**
* The maximum number of Pointers allowed to be active at any one time.
* For lots of games it's useful to set this to 1
* @type {Number}
* For lots of games it's useful to set this to 1.
* @property {number} maxPointers
* @default
*/
maxPointers: 10,
/**
* The current number of active Pointers.
* @type {Number}
* @property {number} currentPointers
* @default
*/
currentPointers: 0,
/**
* The number of milliseconds that the Pointer has to be pressed down and then released to be considered a tap or click
* @property tapRate
* @type {Number}
**/
* The number of milliseconds that the Pointer has to be pressed down and then released to be considered a tap or clicke
* @property {number} tapRate
* @default
*/
tapRate: 200,
/**
* The number of milliseconds between taps of the same Pointer for it to be considered a double tap / click
* @property doubleTapRate
* @type {Number}
**/
* @property {number} doubleTapRate
* @default
*/
doubleTapRate: 300,
/**
* The number of milliseconds that the Pointer has to be pressed down for it to fire a onHold event
* @property holdRate
* @type {Number}
**/
* @property {number} holdRate
* @default
*/
holdRate: 2000,
/**
* The number of milliseconds below which the Pointer is considered justPressed
* @property justPressedRate
* @type {Number}
**/
* @property {number} justPressedRate
* @default
*/
justPressedRate: 200,
/**
* The number of milliseconds below which the Pointer is considered justReleased
* @property justReleasedRate
* @type {Number}
**/
* The number of milliseconds below which the Pointer is considered justReleased
* @property {number} justReleasedRate
* @default
*/
justReleasedRate: 200,
/**
* Sets if the Pointer objects should record a history of x/y coordinates they have passed through.
* The history is cleared each time the Pointer is pressed down.
* The history is updated at the rate specified in Input.pollRate
* @property recordPointerHistory
* @type {bool}
**/
* @property {boolean} recordPointerHistory
* @default
*/
recordPointerHistory: false,
/**
* The rate in milliseconds at which the Pointer objects should update their tracking history
* @property recordRate
* @type {Number}
* @property {number} recordRate
* @default
*/
recordRate: 100,
/**
* The total number of entries that can be recorded into the Pointer objects tracking history.
* If the Pointer is tracking one event every 100ms, then a trackLimit of 100 would store the last 10 seconds worth of history.
* @property recordLimit
* @type {Number}
* @property {number} recordLimit
* @default
*/
recordLimit: 100,
/**
* A Pointer object
* @property pointer1
* @type {Pointer}
**/
* @property {Phaser.Pointer} pointer1
*/
pointer1: null,
/**
* A Pointer object
* @property pointer2
* @type {Pointer}
**/
* @property {Phaser.Pointer} pointer2
*/
pointer2: null,
/**
* A Pointer object
* @property pointer3
* @type {Pointer}
**/
* A Pointer object
* @property {Phaser.Pointer} pointer3
*/
pointer3: null,
/**
* A Pointer object
* @property pointer4
* @type {Pointer}
**/
* @property {Phaser.Pointer} pointer4
*/
pointer4: null,
/**
* A Pointer object
* @property pointer5
* @type {Pointer}
**/
* @property {Phaser.Pointer} pointer5
*/
pointer5: null,
/**
* A Pointer object
* @property pointer6
* @type {Pointer}
**/
* @property {Phaser.Pointer} pointer6
*/
pointer6: null,
/**
* A Pointer object
* @property pointer7
* @type {Pointer}
**/
* A Pointer object
* @property {Phaser.Pointer} pointer7
*/
pointer7: null,
/**
* A Pointer object
* @property pointer8
* @type {Pointer}
**/
* @property {Phaser.Pointer} pointer8
*/
pointer8: null,
/**
* A Pointer object
* @property pointer9
* @type {Pointer}
**/
* @property {Phaser.Pointer} pointer9
*/
pointer9: null,
/**
* A Pointer object
* @property pointer10
* @type {Pointer}
**/
* A Pointer object.
* @property {Phaser.Pointer} pointer10
*/
pointer10: null,
/**
* The most recently active Pointer object.
* When you've limited max pointers to 1 this will accurately be either the first finger touched or mouse.
* @property activePointer
* @type {Pointer}
**/
* @property {Phaser.Pointer} activePointer
* @default
*/
activePointer: null,
/**
* The mouse has its own unique Phaser.Pointer object which you can use if making a desktop specific game.
* @property {Pointer} mousePointer
* @default
*/
mousePointer: null,
/**
* The Mouse Input manager.
* @property {Phaser.Mouse} mouse - The Mouse Input manager.
* @default
*/
mouse: null,
/**
* The Keyboard Input manager.
* @property {Phaser.Keyboard} keyboard - The Keyboard Input manager.
* @default
*/
keyboard: null,
/**
* The Touch Input manager.
* @property {Phaser.Touch} touch - the Touch Input manager.
* @default
*/
touch: null,
/**
* The MSPointer Input manager.
* @property {Phaser.MSPointer} mspointer - The MSPointer Input manager.
* @default
*/
mspointer: null,
/**
* A Signal that is dispatched each time a pointer is pressed down.
* @property {Phaser.Signal} onDown
* @default
*/
onDown: null,
/**
* A Signal that is dispatched each time a pointer is released.
* @property {Phaser.Signal} onUp
* @default
*/
onUp: null,
/**
* A Signal that is dispatched each time a pointer is tapped.
* @property {Phaser.Signal} onTap
* @default
*/
onTap: null,
/**
* A Signal that is dispatched each time a pointer is held down.
* @property {Phaser.Signal} onHold
* @default
*/
onHold: null,
// A linked list of interactive objects, the InputHandler components (belong to Sprites) register themselves with this
/**
* A linked list of interactive objects, the InputHandler components (belonging to Sprites) register themselves with this.
* @property {Phaser.LinkedList} interactiveItems
*/
interactiveItems: new Phaser.LinkedList(),
/**
* Starts the Input Manager running
* @method start
**/
* Starts the Input Manager running.
* @method Phaser.Input#boot
* @protected
*/
boot: function () {
this.mousePointer = new Phaser.Pointer(this.game, 0);
@@ -297,14 +395,27 @@ Phaser.Input.prototype = {
this.mspointer.start();
this.mousePointer.active = true;
},
/**
* Stops all of the Input Managers from running.
* @method Phaser.Input#destroy
*/
destroy: function () {
this.mouse.stop();
this.keyboard.stop();
this.touch.stop();
this.mspointer.stop();
},
/**
* Add a new Pointer object to the Input Manager. By default Input creates 2 pointer objects for you. If you need more
* use this to create a new one, up to a maximum of 10.
* @method addPointer
* @return {Pointer} A reference to the new Pointer object
**/
* Add a new Pointer object to the Input Manager. By default Input creates 3 pointer objects: mousePointer, pointer1 and pointer2.
* If you need more then use this to create a new one, up to a maximum of 10.
* @method Phaser.Input#addPointer
* @return {Phaser.Pointer} A reference to the new Pointer object that was created.
*/
addPointer: function () {
var next = 0;
@@ -332,8 +443,9 @@ Phaser.Input.prototype = {
/**
* Updates the Input Manager. Called by the core Game loop.
* @method update
**/
* @method Phaser.Input#update
* @protected
*/
update: function () {
if (this.pollRate > 0 && this._pollCounter < this.pollRate)
@@ -365,9 +477,9 @@ Phaser.Input.prototype = {
/**
* Reset all of the Pointers and Input states
* @method reset
* @param hard {bool} A soft reset (hard = false) won't reset any signals that might be bound. A hard reset will.
**/
* @method Phaser.Input#reset
* @param {boolean} hard - A soft reset (hard = false) won't reset any Signals that might be bound. A hard reset will.
*/
reset: function (hard) {
if (this.game.isBooted == false)
@@ -409,6 +521,12 @@ Phaser.Input.prototype = {
},
/**
* Resets the speed and old position properties.
* @method Phaser.Input#resetSpeed
* @param {number} x - Sets the oldPosition.x value.
* @param {number} y - Sets the oldPosition.y value.
*/
resetSpeed: function (x, y) {
this._oldPosition.setTo(x, y);
@@ -417,11 +535,11 @@ Phaser.Input.prototype = {
},
/**
* Find the first free Pointer object and start it, passing in the event data.
* @method startPointer
* @param {Any} event The event data from the Touch event
* @return {Pointer} The Pointer object that was started or null if no Pointer object is available
**/
* Find the first free Pointer object and start it, passing in the event data. This is called automatically by Phaser.Touch and Phaser.MSPointer.
* @method Phaser.Input#startPointer
* @param {Any} event - The event data from the Touch event.
* @return {Phaser.Pointer} The Pointer object that was started or null if no Pointer object is available.
*/
startPointer: function (event) {
if (this.maxPointers < 10 && this.totalActivePointers == this.maxPointers)
@@ -453,11 +571,11 @@ Phaser.Input.prototype = {
},
/**
* Updates the matching Pointer object, passing in the event data.
* @method updatePointer
* @param {Any} event The event data from the Touch event
* @return {Pointer} The Pointer object that was updated or null if no Pointer object is available
**/
* Updates the matching Pointer object, passing in the event data. This is called automatically and should not normally need to be invoked.
* @method Phaser.Input#updatePointer
* @param {Any} event - The event data from the Touch event.
* @return {Phaser.Pointer} The Pointer object that was updated or null if no Pointer object is available.
*/
updatePointer: function (event) {
if (this.pointer1.active && this.pointer1.identifier == event.identifier)
@@ -485,10 +603,10 @@ Phaser.Input.prototype = {
/**
* Stops the matching Pointer object, passing in the event data.
* @method stopPointer
* @param {Any} event The event data from the Touch event
* @return {Pointer} The Pointer object that was stopped or null if no Pointer object is available
**/
* @method Phaser.Input#stopPointer
* @param {Any} event - The event data from the Touch event.
* @return {Phaser.Pointer} The Pointer object that was stopped or null if no Pointer object is available.
*/
stopPointer: function (event) {
if (this.pointer1.active && this.pointer1.identifier == event.identifier)
@@ -516,10 +634,10 @@ Phaser.Input.prototype = {
/**
* Get the next Pointer object whos active property matches the given state
* @method getPointer
* @param {bool} state The state the Pointer should be in (false for inactive, true for active)
* @return {Pointer} A Pointer object or null if no Pointer object matches the requested state.
**/
* @method Phaser.Input#getPointer
* @param {boolean} state - The state the Pointer should be in (false for inactive, true for active).
* @return {Phaser.Pointer} A Pointer object or null if no Pointer object matches the requested state.
*/
getPointer: function (state) {
state = state || false;
@@ -548,11 +666,11 @@ Phaser.Input.prototype = {
},
/**
* Get the Pointer object whos identified property matches the given identifier value
* @method getPointerFromIdentifier
* @param {Number} identifier The Pointer.identifier value to search for
* @return {Pointer} A Pointer object or null if no Pointer object matches the requested identifier.
**/
* Get the Pointer object whos identified property matches the given identifier value.
* @method Phaser.Input#getPointerFromIdentifier
* @param {number} identifier - The Pointer.identifier value to search for.
* @return {Phaser.Pointer} A Pointer object or null if no Pointer object matches the requested identifier.
*/
getPointerFromIdentifier: function (identifier) {
if (this.pointer1.identifier == identifier)
@@ -576,40 +694,17 @@ Phaser.Input.prototype = {
return null;
},
/**
* Get the distance between two Pointer objects
* @method getDistance
* @param {Pointer} pointer1
* @param {Pointer} pointer2
**/
getDistance: function (pointer1, pointer2) {
// return Phaser.Vec2Utils.distance(pointer1.position, pointer2.position);
},
/**
* Get the angle between two Pointer objects
* @method getAngle
* @param {Pointer} pointer1
* @param {Pointer} pointer2
**/
getAngle: function (pointer1, pointer2) {
// return Phaser.Vec2Utils.angle(pointer1.position, pointer2.position);
}
};
// Getters / Setters
/**
* The X coordinate of the most recently active pointer. This value takes game scaling into account automatically. See Pointer.screenX/clientX for source values.
* @name Phaser.Input#x
* @property {number} x - The X coordinate of the most recently active pointer.
*/
Object.defineProperty(Phaser.Input.prototype, "x", {
/**
* The X coordinate of the most recently active pointer.
* This value takes game scaling into account automatically. See Pointer.screenX/clientX for source values.
* @property x
* @type {Number}
**/
get: function () {
return this._x;
},
@@ -620,14 +715,13 @@ Object.defineProperty(Phaser.Input.prototype, "x", {
});
/**
* The Y coordinate of the most recently active pointer. This value takes game scaling into account automatically. See Pointer.screenY/clientY for source values.
* @name Phaser.Input#y
* @property {number} y - The Y coordinate of the most recently active pointer.
*/
Object.defineProperty(Phaser.Input.prototype, "y", {
/**
* The Y coordinate of the most recently active pointer.
* This value takes game scaling into account automatically. See Pointer.screenY/clientY for source values.
* @property y
* @type {Number}
**/
get: function () {
return this._y;
},
@@ -638,6 +732,11 @@ Object.defineProperty(Phaser.Input.prototype, "y", {
});
/**
* @name Phaser.Input#pollLocked
* @property {boolean} pollLocked - True if the Input is currently poll rate locked.
* @readonly
*/
Object.defineProperty(Phaser.Input.prototype, "pollLocked", {
get: function () {
@@ -646,26 +745,28 @@ Object.defineProperty(Phaser.Input.prototype, "pollLocked", {
});
/**
* The total number of inactive Pointers
* @name Phaser.Input#totalInactivePointers
* @property {number} totalInactivePointers - The total number of inactive Pointers.
* @readonly
*/
Object.defineProperty(Phaser.Input.prototype, "totalInactivePointers", {
/**
* Get the total number of inactive Pointers
* @method totalInactivePointers
* @return {Number} The number of Pointers currently inactive
**/
get: function () {
return 10 - this.currentPointers;
}
});
/**
* The total number of active Pointers
* @name Phaser.Input#totalActivePointers
* @property {number} totalActivePointers - The total number of active Pointers.
* @readonly
*/
Object.defineProperty(Phaser.Input.prototype, "totalActivePointers", {
/**
* Recalculates the total number of active Pointers
* @method totalActivePointers
* @return {Number} The number of Pointers currently active
**/
get: function () {
this.currentPointers = 0;
@@ -684,6 +785,11 @@ Object.defineProperty(Phaser.Input.prototype, "totalActivePointers", {
});
/**
* The world X coordinate of the most recently active pointer.
* @name Phaser.Input#worldX
* @property {number} worldX - The world X coordinate of the most recently active pointer.
*/
Object.defineProperty(Phaser.Input.prototype, "worldX", {
get: function () {
@@ -692,6 +798,11 @@ Object.defineProperty(Phaser.Input.prototype, "worldX", {
});
/**
* The world Y coordinate of the most recently active pointer.
* @name Phaser.Input#worldY
* @property {number} worldY - The world Y coordinate of the most recently active pointer.
*/
Object.defineProperty(Phaser.Input.prototype, "worldY", {
get: function () {
+301 -100
View File
@@ -1,81 +1,208 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Constructor for Phaser InputHandler.
* @class Phaser.InputHandler
* @classdesc Description.
* @constructor
* @param {Phaser.Sprite} game - Description.
*/
Phaser.InputHandler = function (sprite) {
this.game = sprite.game;
/**
* @property {Phaser.Sprite} sprite - Description.
*/
this.sprite = sprite;
/**
* @property {Phaser.Game} game - A reference to the currently running game.
*/
this.game = sprite.game;
/**
* @property {boolean} enabled - Description.
* @default
*/
this.enabled = false;
// Linked list references
/**
* @property {Description} parent - Description.
* @default
*/
this.parent = null;
/**
* @property {Description} next - Description.
* @default
*/
this.next = null;
/**
* @property {Description} prev - Description.
* @default
*/
this.prev = null;
/**
* @property {Description} last - Description.
* @default
*/
this.last = this;
/**
* @property {Description} first - Description.
* @default
*/
this.first = this;
/**
* The PriorityID controls which Sprite receives an Input event first if they should overlap.
*/
/**
* @property {number} priorityID - The PriorityID controls which Sprite receives an Input event first if they should overlap.
* @default
*/
this.priorityID = 0;
/**
* @property {boolean} useHandCursor - Description.
* @default
*/
this.useHandCursor = false;
/**
* @property {boolean} isDragged - Description.
* @default
*/
this.isDragged = false;
/**
* @property {boolean} allowHorizontalDrag - Description.
* @default
*/
this.allowHorizontalDrag = true;
/**
* @property {boolean} allowVerticalDrag - Description.
* @default
*/
this.allowVerticalDrag = true;
/**
* @property {boolean} bringToTop - Description.
* @default
*/
this.bringToTop = false;
/**
* @property {Description} snapOffset - Description.
* @default
*/
this.snapOffset = null;
/**
* @property {boolean} snapOnDrag - Description.
* @default
*/
this.snapOnDrag = false;
/**
* @property {boolean} snapOnRelease - Description.
* @default
*/
this.snapOnRelease = false;
/**
* @property {number} snapX - Description.
* @default
*/
this.snapX = 0;
/**
* @property {number} snapY - Description.
* @default
*/
this.snapY = 0;
/**
* Should we use pixel perfect hit detection? Warning: expensive. Only enable if you really need it!
* @default false
*/
/**
* @property {number} pixelPerfect - Should we use pixel perfect hit detection? Warning: expensive. Only enable if you really need it!
* @default
*/
this.pixelPerfect = false;
/**
* The alpha tolerance threshold. If the alpha value of the pixel matches or is above this value, it's considered a hit.
* @default 255
* @property {number} pixelPerfectAlpha - The alpha tolerance threshold. If the alpha value of the pixel matches or is above this value, it's considered a hit.
* @default
*/
this.pixelPerfectAlpha = 255;
/**
* Is this sprite allowed to be dragged by the mouse? true = yes, false = no
* @default false
* @property {boolean} draggable - Is this sprite allowed to be dragged by the mouse? true = yes, false = no
* @default
*/
this.draggable = false;
/**
* A region of the game world within which the sprite is restricted during drag
* @default null
* @property {Description} boundsRect - A region of the game world within which the sprite is restricted during drag.
* @default
*/
this.boundsRect = null;
/**
* An Sprite the bounds of which this sprite is restricted during drag
* @default null
* @property {Description} boundsSprite - A Sprite the bounds of which this sprite is restricted during drag.
* @default
*/
this.boundsSprite = null;
/**
* If this object is set to consume the pointer event then it will stop all propogation from this object on.
* For example if you had a stack of 6 sprites with the same priority IDs and one consumed the event, none of the others would receive it.
* @type {bool}
* @property {boolean} consumePointerEvent
* @default
*/
this.consumePointerEvent = false;
/**
* @property {Phaser.Point} _tempPoint - Description.
* @private
*/
this._tempPoint = new Phaser.Point;
this._pointerData = [];
this._pointerData.push({
id: 0,
x: 0,
y: 0,
isDown: false,
isUp: false,
isOver: false,
isOut: false,
timeOver: 0,
timeOut: 0,
timeDown: 0,
timeUp: 0,
downDuration: 0,
isDragged: false
});
};
Phaser.InputHandler.prototype = {
/**
* Description.
* @method Phaser.InputHandler#start
* @param {number} priority - Description.
* @param {boolean} useHandCursor - Description.
* @return {Phaser.Sprite} Description.
*/
start: function (priority, useHandCursor) {
priority = priority || 0;
useHandCursor = useHandCursor || false;
if (typeof useHandCursor == 'undefined') { useHandCursor = false; }
// Turning on
if (this.enabled == false)
@@ -84,11 +211,10 @@ Phaser.InputHandler.prototype = {
this.game.input.interactiveItems.add(this);
this.useHandCursor = useHandCursor;
this.priorityID = priority;
this._pointerData = [];
for (var i = 0; i < 10; i++)
{
this._pointerData.push({
this._pointerData[i] = {
id: i,
x: 0,
y: 0,
@@ -102,7 +228,7 @@ Phaser.InputHandler.prototype = {
timeUp: 0,
downDuration: 0,
isDragged: false
});
};
}
this.snapOffset = new Phaser.Point;
@@ -124,6 +250,10 @@ Phaser.InputHandler.prototype = {
},
/**
* Description.
* @method Phaser.InputHandler#reset
*/
reset: function () {
this.enabled = false;
@@ -148,6 +278,10 @@ Phaser.InputHandler.prototype = {
}
},
/**
* Description.
* @method Phaser.InputHandler#stop
*/
stop: function () {
// Turning off
@@ -165,23 +299,29 @@ Phaser.InputHandler.prototype = {
},
/**
* Clean up memory.
*/
* Clean up memory.
* @method Phaser.InputHandler#destroy
*/
destroy: function () {
if (this.enabled)
{
this.enabled = false;
this.game.input.interactiveItems.remove(this);
this.stop();
// Null everything
this.sprite = null;
// etc
}
},
/**
* The x coordinate of the Input pointer, relative to the top-left of the parent Sprite.
* This value is only set when the pointer is over this Sprite.
* @type {number}
* @method Phaser.InputHandler#pointerX
* @param {Pointer} pointer
* @return {number} The x coordinate of the Input pointer.
*/
pointerX: function (pointer) {
@@ -194,7 +334,9 @@ Phaser.InputHandler.prototype = {
/**
* The y coordinate of the Input pointer, relative to the top-left of the parent Sprite
* This value is only set when the pointer is over this Sprite.
* @type {number}
* @method Phaser.InputHandler#pointerY
* @param {Pointer} pointer
* @return {number} The y coordinate of the Input pointer.
*/
pointerY: function (pointer) {
@@ -205,10 +347,11 @@ Phaser.InputHandler.prototype = {
},
/**
* If the Pointer is touching the touchscreen, or the mouse button is held down, isDown is set to true
* @property isDown
* @type {bool}
**/
* If the Pointer is touching the touchscreen, or the mouse button is held down, isDown is set to true.
* @method Phaser.InputHandler#pointerDown
* @param {Pointer} pointer
* @return {boolean}
*/
pointerDown: function (pointer) {
pointer = pointer || 0;
@@ -219,9 +362,10 @@ Phaser.InputHandler.prototype = {
/**
* If the Pointer is not touching the touchscreen, or the mouse button is up, isUp is set to true
* @property isUp
* @type {bool}
**/
* @method Phaser.InputHandler#pointerUp
* @param {Pointer} pointer
* @return {boolean}
*/
pointerUp: function (pointer) {
pointer = pointer || 0;
@@ -232,9 +376,10 @@ Phaser.InputHandler.prototype = {
/**
* A timestamp representing when the Pointer first touched the touchscreen.
* @property timeDown
* @type {Number}
**/
* @method Phaser.InputHandler#pointerTimeDown
* @param {Pointer} pointer
* @return {number}
*/
pointerTimeDown: function (pointer) {
pointer = pointer || 0;
@@ -245,9 +390,10 @@ Phaser.InputHandler.prototype = {
/**
* A timestamp representing when the Pointer left the touchscreen.
* @property timeUp
* @type {Number}
**/
* @method Phaser.InputHandler#pointerTimeUp
* @param {Pointer} pointer
* @return {number}
*/
pointerTimeUp: function (pointer) {
pointer = pointer || 0;
@@ -257,10 +403,11 @@ Phaser.InputHandler.prototype = {
},
/**
* Is the Pointer over this Sprite
* @property isOver
* @type {bool}
**/
* Is the Pointer over this Sprite?
* @method Phaser.InputHandler#pointerOver
* @param {Pointer} pointer
* @return {bool
*/
pointerOver: function (pointer) {
pointer = pointer || 0;
@@ -270,10 +417,11 @@ Phaser.InputHandler.prototype = {
},
/**
* Is the Pointer outside of this Sprite
* @property isOut
* @type {bool}
**/
* Is the Pointer outside of this Sprite?
* @method Phaser.InputHandler#pointerOut
* @param {Pointer} pointer
* @return {boolean}
*/
pointerOut: function (pointer) {
pointer = pointer || 0;
@@ -284,9 +432,10 @@ Phaser.InputHandler.prototype = {
/**
* A timestamp representing when the Pointer first touched the touchscreen.
* @property timeDown
* @type {Number}
**/
* @method Phaser.InputHandler#pointerTimeOver
* @param {Pointer} pointer
* @return {number}
*/
pointerTimeOver: function (pointer) {
pointer = pointer || 0;
@@ -297,9 +446,10 @@ Phaser.InputHandler.prototype = {
/**
* A timestamp representing when the Pointer left the touchscreen.
* @property timeUp
* @type {Number}
**/
* @method Phaser.InputHandler#pointerTimeOut
* @param {Pointer} pointer
* @return {number}
*/
pointerTimeOut: function (pointer) {
pointer = pointer || 0;
@@ -310,7 +460,9 @@ Phaser.InputHandler.prototype = {
/**
* Is this sprite being dragged by the mouse or not?
* @default false
* @method Phaser.InputHandler#pointerTimeOut
* @param {Pointer} pointer
* @return {number}
*/
pointerDragged: function (pointer) {
@@ -322,6 +474,9 @@ Phaser.InputHandler.prototype = {
/**
* Checks if the given pointer is over this Sprite.
* @method Phaser.InputHandler#checkPointerOver
* @param {Pointer} pointer
* @return {boolean}
*/
checkPointerOver: function (pointer) {
@@ -329,46 +484,42 @@ Phaser.InputHandler.prototype = {
{
this.sprite.getLocalUnmodifiedPosition(this._tempPoint, pointer.x, pointer.y);
// Check against bounds first (move these to private vars)
var x1 = -(this.sprite.texture.frame.width) * this.sprite.anchor.x;
var y1;
if (this._tempPoint.x > x1 && this._tempPoint.x < x1 + this.sprite.texture.frame.width)
if (this._tempPoint.x >= 0 && this._tempPoint.x <= this.sprite.currentFrame.width && this._tempPoint.y >= 0 && this._tempPoint.y <= this.sprite.currentFrame.height)
{
y1 = -(this.sprite.texture.frame.height) * this.sprite.anchor.y;
if (this._tempPoint.y > y1 && this._tempPoint.y < y1 + this.sprite.texture.frame.height)
if (this.pixelPerfect)
{
if (this.pixelPerfect)
{
return this.checkPixel(this._tempPoint.x, this._tempPoint.y);
}
else
{
return true;
}
return this.checkPixel(this._tempPoint.x, this._tempPoint.y);
}
else
{
return true;
}
}
}
else
{
return false;
}
return false;
},
/**
* Description.
* @method Phaser.InputHandler#checkPixel
* @param {Description} x - Description.
* @param {Description} y - Description.
* @return {boolean}
*/
checkPixel: function (x, y) {
x += (this.sprite.texture.frame.width * this.sprite.anchor.x);
y += (this.sprite.texture.frame.height * this.sprite.anchor.y);
// Grab a pixel from our image into the hitCanvas and then test it
if (this.sprite.texture.baseTexture.source)
{
this.game.input.hitContext.clearRect(0, 0, 1, 1);
// This will fail if the image is part of a texture atlas - need to modify the x/y values here
x += this.sprite.texture.frame.x;
y += this.sprite.texture.frame.y;
this.game.input.hitContext.drawImage(this.sprite.texture.baseTexture.source, x, y, 1, 1, 0, 0, 1, 1);
var rgb = this.game.input.hitContext.getImageData(0, 0, 1, 1);
@@ -384,11 +535,13 @@ Phaser.InputHandler.prototype = {
},
/**
* Update
* Update.
* @method Phaser.InputHandler#update
* @param {Pointer} pointer
*/
update: function (pointer) {
if (this.enabled == false || this.sprite.visible == false)
if (this.enabled == false || this.sprite.visible == false || (this.sprite.group && this.sprite.group.visible == false))
{
this._pointerOutHandler(pointer);
return false;
@@ -414,6 +567,12 @@ Phaser.InputHandler.prototype = {
}
},
/**
* Description.
* @method Phaser.InputHandler#_pointerOverHandler
* @private
* @param {Pointer} pointer
*/
_pointerOverHandler: function (pointer) {
if (this._pointerData[pointer.id].isOver == false)
@@ -433,6 +592,12 @@ Phaser.InputHandler.prototype = {
}
},
/**
* Description.
* @method Phaser.InputHandler#_pointerOutHandler
* @private
* @param {Pointer} pointer
*/
_pointerOutHandler: function (pointer) {
this._pointerData[pointer.id].isOver = false;
@@ -444,10 +609,19 @@ Phaser.InputHandler.prototype = {
this.game.stage.canvas.style.cursor = "default";
}
this.sprite.events.onInputOut.dispatch(this.sprite, pointer);
if (this.sprite && this.sprite.events)
{
this.sprite.events.onInputOut.dispatch(this.sprite, pointer);
}
},
/**
* Description.
* @method Phaser.InputHandler#_touchedHandler
* @private
* @param {Pointer} pointer
*/
_touchedHandler: function (pointer) {
if (this._pointerData[pointer.id].isDown == false && this._pointerData[pointer.id].isOver == true)
@@ -474,6 +648,12 @@ Phaser.InputHandler.prototype = {
},
/**
* Description.
* @method Phaser.InputHandler#_releasedHandler
* @private
* @param {Pointer} pointer
*/
_releasedHandler: function (pointer) {
// If was previously touched by this Pointer, check if still is AND still over this item
@@ -510,6 +690,9 @@ Phaser.InputHandler.prototype = {
/**
* Updates the Pointer drag on this Sprite.
* @method Phaser.InputHandler#updateDrag
* @param {Pointer} pointer
* @return {boolean}
*/
updateDrag: function (pointer) {
@@ -541,8 +724,8 @@ Phaser.InputHandler.prototype = {
if (this.snapOnDrag)
{
this.sprite.x = Math.floor(this.sprite.x / this.snapX) * this.snapX;
this.sprite.y = Math.floor(this.sprite.y / this.snapY) * this.snapY;
this.sprite.x = Math.round(this.sprite.x / this.snapX) * this.snapX;
this.sprite.y = Math.round(this.sprite.y / this.snapY) * this.snapY;
}
return true;
@@ -551,8 +734,10 @@ Phaser.InputHandler.prototype = {
/**
* Returns true if the pointer has entered the Sprite within the specified delay time (defaults to 500ms, half a second)
* @param delay The time below which the pointer is considered as just over.
* @returns {bool}
* @method Phaser.InputHandler#justOver
* @param {Pointer} pointer
* @param {number} delay - The time below which the pointer is considered as just over.
* @return {boolean}
*/
justOver: function (pointer, delay) {
@@ -565,8 +750,10 @@ Phaser.InputHandler.prototype = {
/**
* Returns true if the pointer has left the Sprite within the specified delay time (defaults to 500ms, half a second)
* @param delay The time below which the pointer is considered as just out.
* @returns {bool}
* @method Phaser.InputHandler#justOut
* @param {Pointer} pointer
* @param {number} delay - The time below which the pointer is considered as just out.
* @return {boolean}
*/
justOut: function (pointer, delay) {
@@ -579,8 +766,10 @@ Phaser.InputHandler.prototype = {
/**
* Returns true if the pointer has entered the Sprite within the specified delay time (defaults to 500ms, half a second)
* @param delay The time below which the pointer is considered as just over.
* @returns {bool}
* @method Phaser.InputHandler#justPressed
* @param {Pointer} pointer
* @param {number} delay - The time below which the pointer is considered as just over.
* @return {boolean}
*/
justPressed: function (pointer, delay) {
@@ -593,8 +782,10 @@ Phaser.InputHandler.prototype = {
/**
* Returns true if the pointer has left the Sprite within the specified delay time (defaults to 500ms, half a second)
* @param delay The time below which the pointer is considered as just out.
* @returns {bool}
* @method Phaser.InputHandler#justReleased
* @param {Pointer} pointer
* @param {number} delay - The time below which the pointer is considered as just out.
* @return {boolean}
*/
justReleased: function (pointer, delay) {
@@ -607,7 +798,9 @@ Phaser.InputHandler.prototype = {
/**
* If the pointer is currently over this Sprite this returns how long it has been there for in milliseconds.
* @returns {number} The number of milliseconds the pointer has been over the Sprite, or -1 if not over.
* @method Phaser.InputHandler#overDuration
* @param {Pointer} pointer
* @return {number} The number of milliseconds the pointer has been over the Sprite, or -1 if not over.
*/
overDuration: function (pointer) {
@@ -624,7 +817,9 @@ Phaser.InputHandler.prototype = {
/**
* If the pointer is currently over this Sprite this returns how long it has been there for in milliseconds.
* @returns {number} The number of milliseconds the pointer has been pressed down on the Sprite, or -1 if not over.
* @method Phaser.InputHandler#downDuration
* @param {Pointer} pointer
* @return {number} The number of milliseconds the pointer has been pressed down on the Sprite, or -1 if not over.
*/
downDuration: function (pointer) {
@@ -641,7 +836,7 @@ Phaser.InputHandler.prototype = {
/**
* Make this Sprite draggable by the mouse. You can also optionally set mouseStartDragCallback and mouseStopDragCallback
*
* @method Phaser.InputHandler#enableDrag
* @param lockCenter If false the Sprite will drag from where you click it minus the dragOffset. If true it will center itself to the tip of the mouse pointer.
* @param bringToTop If true the Sprite will be bought to the top of the rendering list in its current Group.
* @param pixelPerfect If true it will use a pixel perfect test to see if you clicked the Sprite. False uses the bounding box.
@@ -682,6 +877,7 @@ Phaser.InputHandler.prototype = {
/**
* Stops this sprite from being able to be dragged. If it is currently the target of an active drag it will be stopped immediately. Also disables any set callbacks.
* @method Phaser.InputHandler#disableDrag
*/
disableDrag: function () {
@@ -701,6 +897,7 @@ Phaser.InputHandler.prototype = {
/**
* Called by Pointer when drag starts on this Sprite. Should not usually be called directly.
* @method Phaser.InputHandler#startDrag
*/
startDrag: function (pointer) {
@@ -731,6 +928,7 @@ Phaser.InputHandler.prototype = {
/**
* Called by Pointer when drag is stopped on this Sprite. Should not usually be called directly.
* @method Phaser.InputHandler#stopDrag
*/
stopDrag: function (pointer) {
@@ -740,8 +938,8 @@ Phaser.InputHandler.prototype = {
if (this.snapOnRelease)
{
this.sprite.x = Math.floor(this.sprite.x / this.snapX) * this.snapX;
this.sprite.y = Math.floor(this.sprite.y / this.snapY) * this.snapY;
this.sprite.x = Math.round(this.sprite.x / this.snapX) * this.snapX;
this.sprite.y = Math.round(this.sprite.y / this.snapY) * this.snapY;
}
this.sprite.events.onDragStop.dispatch(this.sprite, pointer);
@@ -751,7 +949,7 @@ Phaser.InputHandler.prototype = {
/**
* Restricts this sprite to drag movement only on the given axis. Note: If both are set to false the sprite will never move!
*
* @method Phaser.InputHandler#setDragLock
* @param allowHorizontal To enable the sprite to be dragged horizontally set to true, otherwise false
* @param allowVertical To enable the sprite to be dragged vertically set to true, otherwise false
*/
@@ -768,7 +966,7 @@ Phaser.InputHandler.prototype = {
/**
* Make this Sprite snap to the given grid either during drag or when it's released.
* For example 16x16 as the snapX and snapY would make the sprite snap to every 16 pixels.
*
* @method Phaser.InputHandler#enableSnap
* @param snapX The width of the grid cell in pixels
* @param snapY The height of the grid cell in pixels
* @param onDrag If true the sprite will snap to the grid while being dragged
@@ -788,6 +986,7 @@ Phaser.InputHandler.prototype = {
/**
* Stops the sprite from snapping to a grid during drag or release.
* @method Phaser.InputHandler#disableSnap
*/
disableSnap: function () {
@@ -798,6 +997,7 @@ Phaser.InputHandler.prototype = {
/**
* Bounds Rect check for the sprite drag
* @method Phaser.InputHandler#checkBoundsRect
*/
checkBoundsRect: function () {
@@ -822,7 +1022,8 @@ Phaser.InputHandler.prototype = {
},
/**
* Parent Sprite Bounds check for the sprite drag
* Parent Sprite Bounds check for the sprite drag.
* @method Phaser.InputHandler#checkBoundsSprite
*/
checkBoundsSprite: function () {
+171
View File
@@ -0,0 +1,171 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* @class Phaser.Key
* @classdesc If you need more fine-grained control over the handling of specific keys you can create and use Phaser.Key objects.
* @constructor
* @param {Phaser.Game} game - Current game instance.
* @param {number} keycode - The key code this Key is responsible for.
*/
Phaser.Key = function (game, keycode) {
/**
* @property {Phaser.Game} game - A reference to the currently running game.
*/
this.game = game;
/**
* @property {boolean} isDown - The "down" state of the key.
* @default
*/
this.isDown = false;
/**
* @property {boolean} isUp - The "up" state of the key.
* @default
*/
this.isUp = false;
/**
* @property {boolean} altKey - The down state of the ALT key, if pressed at the same time as this key.
* @default
*/
this.altKey = false;
/**
* @property {boolean} ctrlKey - The down state of the CTRL key, if pressed at the same time as this key.
* @default
*/
this.ctrlKey = false;
/**
* @property {boolean} shiftKey - The down state of the SHIFT key, if pressed at the same time as this key.
* @default
*/
this.shiftKey = false;
/**
* @property {number} timeDown - The timestamp when the key was last pressed down.
* @default
*/
this.timeDown = 0;
/**
* If the key is down this value holds the duration of that key press and is constantly updated.
* If the key is up it holds the duration of the previous down session.
* @property {number} duration - The number of milliseconds this key has been held down for.
* @default
*/
this.duration = 0;
/**
* @property {number} timeUp - The timestamp when the key was last released.
* @default
*/
this.timeUp = 0;
/**
* @property {number} repeats - If a key is held down this holds down the number of times the key has 'repeated'.
* @default
*/
this.repeats = 0;
/**
* @property {number} keyCode - The keycode of this key.
*/
this.keyCode = keycode;
/**
* @property {Phaser.Signal} onDown - This Signal is dispatched every time this Key is pressed down. It is only dispatched once (until the key is released again).
*/
this.onDown = new Phaser.Signal();
/**
* @property {Phaser.Signal} onUp - This Signal is dispatched every time this Key is pressed down. It is only dispatched once (until the key is released again).
*/
this.onUp = new Phaser.Signal();
};
Phaser.Key.prototype = {
/**
* Called automatically by Phaser.Keyboard.
* @method Phaser.Key#processKeyDown
* @param {KeyboardEvent} event.
* @protected
*/
processKeyDown: function (event) {
this.altKey = event.altKey;
this.ctrlKey = event.ctrlKey;
this.shiftKey = event.shiftKey;
if (this.isDown)
{
// Key was already held down, this must be a repeat rate based event
this.duration = event.timeStamp - this.timeDown;
this.repeats++;
}
else
{
this.isDown = true;
this.isUp = false;
this.timeDown = event.timeStamp;
this.duration = 0;
this.repeats = 0;
this.onDown.dispatch(this);
}
},
/**
* Called automatically by Phaser.Keyboard.
* @method Phaser.Key#processKeyUp
* @param {KeyboardEvent} event.
* @protected
*/
processKeyUp: function (event) {
this.isDown = false;
this.isUp = true;
this.timeUp = event.timeStamp;
this.onUp.dispatch(this);
},
/**
* Returns the "just pressed" state of the Key. Just pressed is considered true if the key was pressed down within the duration given (default 250ms)
* @method Phaser.Key#justPressed
* @param {number} [duration=250] - The duration below which the key is considered as being just pressed.
* @return {boolean} True if the key is just pressed otherwise false.
*/
justPressed: function (duration) {
if (typeof duration === "undefined") { duration = 250; }
return (this.isDown && this.duration < duration);
},
/**
* Returns the "just released" state of the Key. Just released is considered as being true if the key was released within the duration given (default 250ms)
* @method Phaser.Key#justPressed
* @param {number} [duration=250] - The duration below which the key is considered as being just released.
* @return {boolean} True if the key is just released otherwise false.
*/
justReleased: function (duration) {
if (typeof duration === "undefined") { duration = 250; }
return (this.isDown == false && (this.game.time.now - this.timeUp < duration));
}
};
+227 -40
View File
@@ -1,34 +1,161 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Phaser - Keyboard constructor.
*
* @class Phaser.Keyboard
* @classdesc A Keyboard object Description.
* @constructor
* @param {Phaser.Game} game - A reference to the currently running game.
*/
Phaser.Keyboard = function (game) {
/**
* @property {Phaser.Game} game - Local reference to game.
*/
this.game = game;
/**
* @property {Description} _keys - Description.
* @private
*/
this._keys = {};
/**
* @property {Description} _hotkeys - Description.
* @private
*/
this._hotkeys = {};
/**
* @property {Description} _capture - Description.
* @private
*/
this._capture = {};
/**
* You can disable all Keyboard Input by setting disabled to true. While true all new input related events will be ignored.
* @property {boolean} disabled - The disabled state of the Keyboard.
* @default
*/
this.disabled = false;
/**
* @property {function} _onKeyDown
* @private
* @default
*/
this._onKeyDown = null;
/**
* @property {function} _onKeyUp
* @private
* @default
*/
this._onKeyUp = null;
/**
* @property {Object} callbackContext - The context under which the callbacks are run.
*/
this.callbackContext = this;
/**
* @property {function} onDownCallback - This callback is invoked every time a key is pressed down.
*/
this.onDownCallback = null;
/**
* @property {function} onUpCallback - This callback is invoked every time a key is released.
*/
this.onUpCallback = null;
};
Phaser.Keyboard.prototype = {
game: null,
/**
* Add callbacks to the Keyboard handler so that each time a key is pressed down or releases the callbacks are activated.
* @method Phaser.Keyboard#addCallbacks
* @param {Object} context - The context under which the callbacks are run.
* @param {function} onDown - This callback is invoked every time a key is pressed down.
* @param {function} [onUp=null] - This callback is invoked every time a key is released.
*/
addCallbacks: function (context, onDown, onUp) {
this.callbackContext = context;
this.onDownCallback = onDown;
if (typeof onUp !== 'undefined')
{
this.onUpCallback = onUp;
}
},
/**
* You can disable all Input by setting disabled = true. While set all new input related events will be ignored.
* @type {bool}
* If you need more fine-grained control over a Key you can create a new Phaser.Key object via this method.
* The Key object can then be polled, have events attached to it, etc.
*
* @method Phaser.Keyboard#addKey
* @param {number} keycode - The keycode of the key, i.e. Phaser.Keyboard.UP or Phaser.Keyboard.SPACE_BAR
* @return {Phaser.Key} The Key object which you can store locally and reference directly.
*/
disabled: false,
addKey: function (keycode) {
_onKeyDown: null,
_onKeyUp: null,
this._hotkeys[keycode] = new Phaser.Key(this.game, keycode);
return this._hotkeys[keycode];
},
/**
* Removes a Key object from the Keyboard manager.
*
* @method Phaser.Keyboard#removeKey
* @param {number} keycode - The keycode of the key to remove, i.e. Phaser.Keyboard.UP or Phaser.Keyboard.SPACE_BAR
*/
removeKey: function (keycode) {
delete (this._hotkeys[keycode]);
},
/**
* Creates and returns an object containing 4 hotkeys for Up, Down, Left and Right.
*
* @method Phaser.Keyboard#createCursorKeys
* @return {object} An object containing properties: up, down, left and right. Which can be polled like any other Phaser.Key object.
*/
createCursorKeys: function () {
return {
up: this.addKey(Phaser.Keyboard.UP),
down: this.addKey(Phaser.Keyboard.DOWN),
left: this.addKey(Phaser.Keyboard.LEFT),
right: this.addKey(Phaser.Keyboard.RIGHT)
}
},
/**
* Starts the Keyboard event listeners running (keydown and keyup). They are attached to the document.body.
* This is called automatically by Phaser.Input and should not normally be invoked directly.
*
* @method Phaser.Keyboard#start
*/
start: function () {
var _this = this;
this._onKeyDown = function (event) {
return _this.onKeyDown(event);
return _this.processKeyDown(event);
};
this._onKeyUp = function (event) {
return _this.onKeyUp(event);
return _this.processKeyUp(event);
};
document.body.addEventListener('keydown', this._onKeyDown, false);
@@ -36,6 +163,11 @@ Phaser.Keyboard.prototype = {
},
/**
* Stops the Keyboard event listeners from running (keydown and keyup). They are removed from the document.body.
*
* @method Phaser.Keyboard#stop
*/
stop: function () {
document.body.removeEventListener('keydown', this._onKeyDown);
@@ -48,6 +180,7 @@ Phaser.Keyboard.prototype = {
* There are some keys this can be annoying for, like the arrow keys or space bar, which make the browser window scroll.
* You can use addKeyCapture to consume the keyboard event for specific keys so it doesn't bubble up to the the browser.
* Pass in either a single keycode or an array/hash of keycodes.
* @method Phaser.Keyboard#addKeyCapture
* @param {Any} keycode
*/
addKeyCapture: function (keycode) {
@@ -66,7 +199,9 @@ Phaser.Keyboard.prototype = {
},
/**
* @param {Number} keycode
* Removes an existing key capture.
* @method Phaser.Keyboard#removeKeyCapture
* @param {number} keycode
*/
removeKeyCapture: function (keycode) {
@@ -74,6 +209,10 @@ Phaser.Keyboard.prototype = {
},
/**
* Clear all set key captures.
* @method Phaser.Keyboard#clearCaptures
*/
clearCaptures: function () {
this._capture = {};
@@ -81,9 +220,12 @@ Phaser.Keyboard.prototype = {
},
/**
* Process the keydown event.
* @method Phaser.Keyboard#processKeyDown
* @param {KeyboardEvent} event
* @protected
*/
onKeyDown: function (event) {
processKeyDown: function (event) {
if (this.game.input.disabled || this.disabled)
{
@@ -95,26 +237,51 @@ Phaser.Keyboard.prototype = {
event.preventDefault();
}
if (!this._keys[event.keyCode])
if (this.onDownCallback)
{
this._keys[event.keyCode] = {
isDown: true,
timeDown: this.game.time.now,
timeUp: 0
};
this.onDownCallback.call(this.callbackContext, event);
}
if (this._keys[event.keyCode] && this._keys[event.keyCode].isDown)
{
// Key already down and still down, so update
this._keys[event.keyCode].duration = this.game.time.now - this._keys[event.keyCode].timeDown;
}
else
{
this._keys[event.keyCode].isDown = true;
this._keys[event.keyCode].timeDown = this.game.time.now;
if (!this._keys[event.keyCode])
{
// Not used this key before, so register it
this._keys[event.keyCode] = {
isDown: true,
timeDown: this.game.time.now,
timeUp: 0,
duration: 0
};
}
else
{
// Key used before but freshly down
this._keys[event.keyCode].isDown = true;
this._keys[event.keyCode].timeDown = this.game.time.now;
this._keys[event.keyCode].duration = 0;
}
}
if (this._hotkeys[event.keyCode])
{
this._hotkeys[event.keyCode].processKeyDown(event);
}
},
/**
* Process the keyup event.
* @method Phaser.Keyboard#processKeyUp
* @param {KeyboardEvent} event
* @protected
*/
onKeyUp: function (event) {
processKeyUp: function (event) {
if (this.game.input.disabled || this.disabled)
{
@@ -126,22 +293,38 @@ Phaser.Keyboard.prototype = {
event.preventDefault();
}
if (!this._keys[event.keyCode])
if (this.onUpCallback)
{
this._keys[event.keyCode] = {
isDown: false,
timeDown: 0,
timeUp: this.game.time.now
};
this.onUpCallback.call(this.callbackContext, event);
}
else
if (this._hotkeys[event.keyCode])
{
this._hotkeys[event.keyCode].processKeyUp(event);
}
if (this._keys[event.keyCode])
{
this._keys[event.keyCode].isDown = false;
this._keys[event.keyCode].timeUp = this.game.time.now;
}
else
{
// Not used this key before, so register it
this._keys[event.keyCode] = {
isDown: false,
timeDown: this.game.time.now,
timeUp: this.game.time.now,
duration: 0
};
}
},
/**
* Reset the "isDown" state of all keys.
* @method Phaser.Keyboard#reset
*/
reset: function () {
for (var key in this._keys)
@@ -151,16 +334,18 @@ Phaser.Keyboard.prototype = {
},
/**
* @param {Number} keycode
* @param {Number} [duration]
* @return {bool}
/**
* Returns the "just pressed" state of the key. Just pressed is considered true if the key was pressed down within the duration given (default 250ms)
* @method Phaser.Keyboard#justPressed
* @param {number} keycode - The keycode of the key to remove, i.e. Phaser.Keyboard.UP or Phaser.Keyboard.SPACE_BAR
* @param {number} [duration=250] - The duration below which the key is considered as being just pressed.
* @return {boolean} True if the key is just pressed otherwise false.
*/
justPressed: function (keycode, duration) {
if (typeof duration === "undefined") { duration = 250; }
if (this._keys[keycode] && this._keys[keycode].isDown === true && (this.game.time.now - this._keys[keycode].timeDown < duration))
if (this._keys[keycode] && this._keys[keycode].isDown && this._keys[keycode].duration < duration)
{
return true;
}
@@ -169,10 +354,12 @@ Phaser.Keyboard.prototype = {
},
/**
* @param {Number} keycode
* @param {Number} [duration]
* @return {bool}
/**
* Returns the "just released" state of the Key. Just released is considered as being true if the key was released within the duration given (default 250ms)
* @method Phaser.Keyboard#justPressed
* @param {number} keycode - The keycode of the key to remove, i.e. Phaser.Keyboard.UP or Phaser.Keyboard.SPACE_BAR
* @param {number} [duration=250] - The duration below which the key is considered as being just released.
* @return {boolean} True if the key is just released otherwise false.
*/
justReleased: function (keycode, duration) {
@@ -187,9 +374,11 @@ Phaser.Keyboard.prototype = {
},
/**
* @param {Number} keycode
* @return {bool}
/**
* Returns true of the key is currently pressed down. Note that it can only detect key presses on the web browser.
* @method Phaser.Keyboard#isDown
* @param {number} keycode - The keycode of the key to remove, i.e. Phaser.Keyboard.UP or Phaser.Keyboard.SPACE_BAR
* @return {boolean} True if the key is currently down.
*/
isDown: function (keycode) {
@@ -204,8 +393,6 @@ Phaser.Keyboard.prototype = {
};
// Statics
Phaser.Keyboard.A = "A".charCodeAt(0);
Phaser.Keyboard.B = "B".charCodeAt(0);
Phaser.Keyboard.C = "C".charCodeAt(0);
+72 -21
View File
@@ -1,38 +1,86 @@
/**
* Phaser.MSPointer
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Phaser - MSPointer constructor.
*
* The MSPointer class handles touch interactions with the game and the resulting Pointer objects.
* @class Phaser.MSPointer
* @classdesc The MSPointer class handles touch interactions with the game and the resulting Pointer objects.
* It will work only in Internet Explorer 10 and Windows Store or Windows Phone 8 apps using JavaScript.
* http://msdn.microsoft.com/en-us/library/ie/hh673557(v=vs.85).aspx
* @constructor
* @param {Phaser.Game} game - A reference to the currently running game.
*/
Phaser.MSPointer = function (game) {
/**
* @property {Phaser.Game} game - Local reference to game.
*/
this.game = game;
/**
* @property {Phaser.Game} callbackContext - Description.
*/
this.callbackContext = this.game;
/**
* @property {Description} mouseDownCallback - Description.
* @default
*/
this.mouseDownCallback = null;
/**
* @property {Description} mouseMoveCallback - Description.
* @default
*/
this.mouseMoveCallback = null;
/**
* @property {Description} mouseUpCallback - Description.
* @default
*/
this.mouseUpCallback = null;
/**
* You can disable all Input by setting disabled = true. While set all new input related events will be ignored.
* @property {boolean} disabled
*/
this.disabled = false;
/**
* Description.
* @property {Description} _onMSPointerDown
* @private
* @default
*/
this._onMSPointerDown = null;
/**
* Description.
* @property {Description} _onMSPointerMove
* @private
* @default
*/
this._onMSPointerMove = null;
/**
* Description.
* @property {Description} _onMSPointerUp
* @private
* @default
*/
this._onMSPointerUp = null;
};
Phaser.MSPointer.prototype = {
game: null,
/**
* You can disable all Input by setting disabled = true. While set all new input related events will be ignored.
* @type {bool}
*/
disabled: false,
_onMSPointerDown: null,
_onMSPointerMove: null,
_onMSPointerUp: null,
/**
* Starts the event listeners running
* @method start
* Starts the event listeners running.
* @method Phaser.MSPointer#start
*/
start: function () {
@@ -64,7 +112,8 @@ Phaser.MSPointer.prototype = {
},
/**
* @method onPointerDown
* Description.
* @method Phaser.MSPointer#onPointerDown
* @param {Any} event
**/
onPointerDown: function (event) {
@@ -82,7 +131,8 @@ Phaser.MSPointer.prototype = {
},
/**
* @method onPointerMove
* Description.
* @method Phaser.MSPointer#onPointerMove
* @param {Any} event
**/
onPointerMove: function (event) {
@@ -100,7 +150,8 @@ Phaser.MSPointer.prototype = {
},
/**
* @method onPointerUp
* Description.
* @method Phaser.MSPointer#onPointerUp
* @param {Any} event
**/
onPointerUp: function (event) {
@@ -118,8 +169,8 @@ Phaser.MSPointer.prototype = {
},
/**
* Stop the event listeners
* @method stop
* Stop the event listeners.
* @method Phaser.MSPointer#stop
*/
stop: function () {
+86 -18
View File
@@ -1,37 +1,86 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Phaser - Mouse constructor.
*
* @class Phaser.Mouse
* @classdesc The Mouse class
* @constructor
* @param {Phaser.Game} game - A reference to the currently running game.
*/
Phaser.Mouse = function (game) {
/**
* @property {Phaser.Game} game - Local reference to game.
*/
this.game = game;
/**
* @property {Object} callbackContext - Description.
*/
this.callbackContext = this.game;
/**
* @property {Description} mouseDownCallback - Description.
* @default
*/
this.mouseDownCallback = null;
/**
* @property {Description} mouseMoveCallback - Description.
* @default
*/
this.mouseMoveCallback = null;
/**
* @property {Description} mouseUpCallback - Description.
* @default
*/
this.mouseUpCallback = null;
/**
* You can disable all Input by setting disabled = true. While set all new input related events will be ignored.
* @property {boolean} disabled
* @default
*/
this.disabled = false;
/**
* If the mouse has been Pointer Locked successfully this will be set to true.
* @property {boolean} locked
* @default
*/
this.locked = false;
};
/**
* @constant
* @type {number}
*/
Phaser.Mouse.LEFT_BUTTON = 0;
/**
* @constant
* @type {number}
*/
Phaser.Mouse.MIDDLE_BUTTON = 1;
/**
* @constant
* @type {number}
*/
Phaser.Mouse.RIGHT_BUTTON = 2;
Phaser.Mouse.prototype = {
game: null,
/**
* You can disable all Input by setting disabled = true. While set all new input related events will be ignored.
* @type {bool}
*/
disabled: false,
/**
* If the mouse has been Pointer Locked successfully this will be set to true.
* @type {bool}
*/
locked: false,
/**
* Starts the event listeners running
* @method start
* Starts the event listeners running.
* @method Phaser.Mouse#start
*/
start: function () {
@@ -62,6 +111,8 @@ Phaser.Mouse.prototype = {
},
/**
* Description.
* @method Phaser.Mouse#onMouseDown
* @param {MouseEvent} event
*/
onMouseDown: function (event) {
@@ -83,6 +134,8 @@ Phaser.Mouse.prototype = {
},
/**
* Description
* @method Phaser.Mouse#onMouseMove
* @param {MouseEvent} event
*/
onMouseMove: function (event) {
@@ -104,6 +157,8 @@ Phaser.Mouse.prototype = {
},
/**
* Description.
* @method Phaser.Mouse#onMouseUp
* @param {MouseEvent} event
*/
onMouseUp: function (event) {
@@ -124,6 +179,10 @@ Phaser.Mouse.prototype = {
},
/**
* Description.
* @method Phaser.Mouse#requestPointerLock
*/
requestPointerLock: function () {
if (this.game.device.pointerLock)
@@ -147,6 +206,11 @@ Phaser.Mouse.prototype = {
},
/**
* Description.
* @method Phaser.Mouse#pointerLockChange
* @param {MouseEvent} event
*/
pointerLockChange: function (event) {
var element = this.game.stage.canvas;
@@ -164,6 +228,10 @@ Phaser.Mouse.prototype = {
},
/**
* Description.
* @method Phaser.Mouse#releasePointerLock
*/
releasePointerLock: function () {
document.exitPointerLock = document.exitPointerLock || document.mozExitPointerLock || document.webkitExitPointerLock;
@@ -177,8 +245,8 @@ Phaser.Mouse.prototype = {
},
/**
* Stop the event listeners
* @method stop
* Stop the event listeners.
* @method Phaser.Mouse#stop
*/
stop: function () {
+161 -116
View File
@@ -1,193 +1,231 @@
/**
* Phaser - Pointer
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Phaser - Pointer constructor.
*
* A Pointer object is used by the Mouse, Touch and MSPoint managers and represents a single finger on the touch screen.
* @class Phaser.Pointer
* @classdesc A Pointer object is used by the Mouse, Touch and MSPoint managers and represents a single finger on the touch screen.
* @constructor
* @param {Phaser.Game} game - A reference to the currently running game.
* @param {Description} id - Description.
*/
Phaser.Pointer = function (game, id) {
/**
* Local private variable to store the status of dispatching a hold event
* @property _holdSent
* @type {bool}
* @property {Phaser.Game} game - Local reference to game.
*/
this.game = game;
/**
* @property {Description} id - Description.
*/
this.id = id;
/**
* Local private variable to store the status of dispatching a hold event.
* @property {boolean} _holdSent
* @private
* @default
*/
this._holdSent = false;
/**
* Local private variable storing the short-term history of pointer movements
* @property _history
* @type {Array}
* Local private variable storing the short-term history of pointer movements.
* @property {array} _history
* @private
*/
this._history = [];
/**
* Local private variable storing the time at which the next history drop should occur
* @property _lastDrop
* @type {Number}
* @property {number} _lastDrop
* @private
* @default
*/
this._nextDrop = 0;
// Monitor events outside of a state reset loop
/**
* Monitor events outside of a state reset loop.
* @property {boolean} _stateReset
* @private
* @default
*/
this._stateReset = false;
/**
* A Vector object containing the initial position when the Pointer was engaged with the screen.
* @property positionDown
* @type {Vec2}
* @property {Vec2} positionDown
* @default
**/
this.positionDown = null;
/**
* A Vector object containing the current position of the Pointer on the screen.
* @property position
* @type {Vec2}
* @property {Vec2} position
* @default
**/
this.position = null;
/**
* A Circle object centered on the x/y screen coordinates of the Pointer.
* Default size of 44px (Apple's recommended "finger tip" size)
* @property circle
* @type {Circle}
* Default size of 44px (Apple's recommended "finger tip" size).
* @property {Circle} circle
* @default
**/
this.circle = null;
/**
*
* @property withinGame
* @type {bool}
* Description.
* @property {boolean} withinGame
*/
this.withinGame = false;
/**
* The horizontal coordinate of point relative to the viewport in pixels, excluding any scroll offset
* @property clientX
* @type {Number}
* The horizontal coordinate of point relative to the viewport in pixels, excluding any scroll offset.
* @property {number} clientX
* @default
*/
this.clientX = -1;
/**
* The vertical coordinate of point relative to the viewport in pixels, excluding any scroll offset
* @property clientY
* @type {Number}
* The vertical coordinate of point relative to the viewport in pixels, excluding any scroll offset.
* @property {number} clientY
* @default
*/
this.clientY = -1;
/**
* The horizontal coordinate of point relative to the viewport in pixels, including any scroll offset
* @property pageX
* @type {Number}
* The horizontal coordinate of point relative to the viewport in pixels, including any scroll offset.
* @property {number} pageX
* @default
*/
this.pageX = -1;
/**
* The vertical coordinate of point relative to the viewport in pixels, including any scroll offset
* @property pageY
* @type {Number}
* The vertical coordinate of point relative to the viewport in pixels, including any scroll offset.
* @property {number} pageY
* @default
*/
this.pageY = -1;
/**
* The horizontal coordinate of point relative to the screen in pixels
* @property screenX
* @type {Number}
* The horizontal coordinate of point relative to the screen in pixels.
* @property {number} screenX
* @default
*/
this.screenX = -1;
/**
* The vertical coordinate of point relative to the screen in pixels
* @property screenY
* @type {Number}
* The vertical coordinate of point relative to the screen in pixels.
* @property {number} screenY
* @default
*/
this.screenY = -1;
/**
* The horizontal coordinate of point relative to the game element. This value is automatically scaled based on game size.
* @property x
* @type {Number}
* @property {number} x
* @default
*/
this.x = -1;
/**
* The vertical coordinate of point relative to the game element. This value is automatically scaled based on game size.
* @property y
* @type {Number}
* @property {number} y
* @default
*/
this.y = -1;
/**
* If the Pointer is a mouse this is true, otherwise false
* @property isMouse
* @type {bool}
**/
* If the Pointer is a mouse this is true, otherwise false.
* @property {boolean} isMouse
* @type {boolean}
*/
this.isMouse = false;
/**
* If the Pointer is touching the touchscreen, or the mouse button is held down, isDown is set to true
* @property isDown
* @type {bool}
**/
* If the Pointer is touching the touchscreen, or the mouse button is held down, isDown is set to true.
* @property {boolean} isDown
* @default
*/
this.isDown = false;
/**
* If the Pointer is not touching the touchscreen, or the mouse button is up, isUp is set to true
* @property isUp
* @type {bool}
**/
* If the Pointer is not touching the touchscreen, or the mouse button is up, isUp is set to true.
* @property {boolean} isUp
* @default
*/
this.isUp = true;
/**
* A timestamp representing when the Pointer first touched the touchscreen.
* @property timeDown
* @type {Number}
**/
* @property {number} timeDown
* @default
*/
this.timeDown = 0;
/**
* A timestamp representing when the Pointer left the touchscreen.
* @property timeUp
* @type {Number}
**/
* @property {number} timeUp
* @default
*/
this.timeUp = 0;
/**
* A timestamp representing when the Pointer was last tapped or clicked
* @property previousTapTime
* @type {Number}
**/
* A timestamp representing when the Pointer was last tapped or clicked.
* @property {number} previousTapTime
* @default
*/
this.previousTapTime = 0;
/**
* The total number of times this Pointer has been touched to the touchscreen
* @property totalTouches
* @type {Number}
**/
* The total number of times this Pointer has been touched to the touchscreen.
* @property {number} totalTouches
* @default
*/
this.totalTouches = 0;
/**
* The number of miliseconds since the last click
* @property msSinceLastClick
* @type {Number}
**/
* The number of miliseconds since the last click.
* @property {number} msSinceLastClick
* @default
*/
this.msSinceLastClick = Number.MAX_VALUE;
/**
* The Game Object this Pointer is currently over / touching / dragging.
* @property targetObject
* @type {Any}
**/
* @property {Any} targetObject
* @default
*/
this.targetObject = null;
this.game = game;
this.id = id;
/**
* Description.
* @property {boolean} isDown - Description.
* @default
*/
this.active = false;
/**
* Description
* @property {Phaser.Point} position
*/
this.position = new Phaser.Point();
/**
* Description
* @property {Phaser.Point} positionDown
*/
this.positionDown = new Phaser.Point();
/**
* Description
* @property {Phaser.Circle} circle
*/
this.circle = new Phaser.Circle(0, 0, 44);
if (id == 0)
@@ -200,8 +238,8 @@ Phaser.Pointer = function (game, id) {
Phaser.Pointer.prototype = {
/**
* Called when the Pointer is pressed onto the touchscreen
* @method start
* Called when the Pointer is pressed onto the touchscreen.
* @method Phaser.Pointer#start
* @param {Any} event
*/
start: function (event) {
@@ -209,7 +247,7 @@ Phaser.Pointer.prototype = {
this.identifier = event.identifier;
this.target = event.target;
if (event.button)
if (typeof event.button !== 'undefined')
{
this.button = event.button;
}
@@ -243,7 +281,7 @@ Phaser.Pointer.prototype = {
this.game.input.x = this.x * this.game.input.scale.x;
this.game.input.y = this.y * this.game.input.scale.y;
this.game.input.position.setTo(this.x, this.y);
this.game.input.onDown.dispatch(this);
this.game.input.onDown.dispatch(this, event);
this.game.input.resetSpeed(this.x, this.y);
}
@@ -264,6 +302,10 @@ Phaser.Pointer.prototype = {
},
/**
* Description.
* @method Phaser.Pointer#update
*/
update: function () {
if (this.active)
@@ -299,7 +341,7 @@ Phaser.Pointer.prototype = {
/**
* Called when the Pointer is moved
* @method move
* @method Phaser.Pointer#move
* @param {Any} event
*/
move: function (event) {
@@ -309,7 +351,7 @@ Phaser.Pointer.prototype = {
return;
}
if (event.button)
if (typeof event.button !== 'undefined')
{
this.button = event.button;
}
@@ -388,8 +430,6 @@ Phaser.Pointer.prototype = {
if (this._highestRenderObject == null)
{
// console.log("HRO null");
// The pointer isn't currently over anything, check if we've got a lingering previous target
if (this.targetObject)
{
@@ -438,8 +478,8 @@ Phaser.Pointer.prototype = {
},
/**
* Called when the Pointer leaves the target area
* @method leave
* Called when the Pointer leaves the target area.
* @method Phaser.Pointer#leave
* @param {Any} event
*/
leave: function (event) {
@@ -450,8 +490,8 @@ Phaser.Pointer.prototype = {
},
/**
* Called when the Pointer leaves the touchscreen
* @method stop
* Called when the Pointer leaves the touchscreen.
* @method Phaser.Pointer#stop
* @param {Any} event
*/
stop: function (event) {
@@ -466,7 +506,7 @@ Phaser.Pointer.prototype = {
if (this.game.input.multiInputOverride == Phaser.Input.MOUSE_OVERRIDES_TOUCH || this.game.input.multiInputOverride == Phaser.Input.MOUSE_TOUCH_COMBINE || (this.game.input.multiInputOverride == Phaser.Input.TOUCH_OVERRIDES_MOUSE && this.game.input.currentPointers == 0))
{
this.game.input.onUp.dispatch(this);
this.game.input.onUp.dispatch(this, event);
// Was it a tap?
if (this.duration >= 0 && this.duration <= this.game.input.tapRate)
@@ -529,10 +569,10 @@ Phaser.Pointer.prototype = {
},
/**
* The Pointer is considered justPressed if the time it was pressed onto the touchscreen or clicked is less than justPressedRate
* @method justPressed
* @param {Number} [duration].
* @return {bool}
* The Pointer is considered justPressed if the time it was pressed onto the touchscreen or clicked is less than justPressedRate.
* @method Phaser.Pointer#justPressed
* @param {number} [duration]
* @return {boolean}
*/
justPressed: function (duration) {
@@ -543,10 +583,10 @@ Phaser.Pointer.prototype = {
},
/**
* The Pointer is considered justReleased if the time it left the touchscreen is less than justReleasedRate
* @method justReleased
* @param {Number} [duration].
* @return {bool}
* The Pointer is considered justReleased if the time it left the touchscreen is less than justReleasedRate.
* @method Phaser.Pointer#justReleased
* @param {number} [duration]
* @return {boolean}
*/
justReleased: function (duration) {
@@ -558,7 +598,7 @@ Phaser.Pointer.prototype = {
/**
* Resets the Pointer properties. Called by InputManager.reset when you perform a State change.
* @method reset
* @method Phaser.Pointer#reset
*/
reset: function () {
@@ -586,8 +626,8 @@ Phaser.Pointer.prototype = {
/**
* Returns a string representation of this object.
* @method toString
* @return {String} a string representation of the instance.
* @method Phaser.Pointer#toString
* @return {string} A string representation of the instance.
**/
toString: function () {
return "[{Pointer (id=" + this.id + " identifer=" + this.identifier + " active=" + this.active + " duration=" + this.duration + " withinGame=" + this.withinGame + " x=" + this.x + " y=" + this.y + " clientX=" + this.clientX + " clientY=" + this.clientY + " screenX=" + this.screenX + " screenY=" + this.screenY + " pageX=" + this.pageX + " pageY=" + this.pageY + ")}]";
@@ -595,13 +635,14 @@ Phaser.Pointer.prototype = {
};
/**
* How long the Pointer has been depressed on the touchscreen. If not currently down it returns -1.
* @name Phaser.Pointer#duration
* @property {number} duration - How long the Pointer has been depressed on the touchscreen. If not currently down it returns -1.
* @readonly
*/
Object.defineProperty(Phaser.Pointer.prototype, "duration", {
/**
* How long the Pointer has been depressed on the touchscreen. If not currently down it returns -1.
* @property duration
* @type {Number}
**/
get: function () {
if (this.isUp)
@@ -615,12 +656,14 @@ Object.defineProperty(Phaser.Pointer.prototype, "duration", {
});
/**
* Gets the X value of this Pointer in world coordinates based on the world camera.
* @name Phaser.Pointer#worldX
* @property {number} duration - The X value of this Pointer in world coordinates based on the world camera.
* @readonly
*/
Object.defineProperty(Phaser.Pointer.prototype, "worldX", {
/**
* Gets the X value of this Pointer in world coordinates based on the given camera.
* @param {Camera} [camera]
*/
get: function () {
return this.game.world.camera.x + this.x;
@@ -629,12 +672,14 @@ Object.defineProperty(Phaser.Pointer.prototype, "worldX", {
});
/**
* Gets the Y value of this Pointer in world coordinates based on the world camera.
* @name Phaser.Pointer#worldY
* @property {number} duration - The Y value of this Pointer in world coordinates based on the world camera.
* @readonly
*/
Object.defineProperty(Phaser.Pointer.prototype, "worldY", {
/**
* Gets the Y value of this Pointer in world coordinates based on the given camera.
* @param {Camera} [camera]
*/
get: function () {
return this.game.world.camera.y + this.y;
+103 -60
View File
@@ -1,49 +1,93 @@
/**
* Phaser - Touch
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Phaser.Touch handles touch events with your game. Note: Android 2.x only supports 1 touch event at once, no multi-touch.
*
* The Touch class handles touch interactions with the game and the resulting Pointer objects.
* http://www.w3.org/TR/touch-events/
* https://developer.mozilla.org/en-US/docs/DOM/TouchList
* http://www.html5rocks.com/en/mobile/touchandmouse/
* Note: Android 2.x only supports 1 touch event at once, no multi-touch
* @class Phaser.Touch
* @classdesc The Touch class handles touch interactions with the game and the resulting Pointer objects.
* @constructor
* @param {Phaser.Game} game - A reference to the currently running game.
*/
Phaser.Touch = function (game) {
this.game = game;
/**
* @property {Phaser.Game} game - Local reference to game.
*/
this.game = game;
/**
* You can disable all Input by setting disabled = true. While set all new input related events will be ignored.
* @method Phaser.Touch#disabled
* @return {boolean}
*/
this.disabled = false;
/**
* @property {Phaser.Game} callbackContext - Description.
*/
this.callbackContext = this.game;
/**
* @property {Phaser.Game} touchStartCallback - Description.
* @default
*/
this.touchStartCallback = null;
/**
* @property {Phaser.Game} touchMoveCallback - Description.
* @default
*/
this.touchMoveCallback = null;
/**
* @property {Phaser.Game} touchEndCallback - Description.
* @default
*/
this.touchEndCallback = null;
/**
* @property {Phaser.Game} touchEnterCallback - Description.
* @default
*/
this.touchEnterCallback = null;
/**
* @property {Phaser.Game} touchLeaveCallback - Description.
* @default
*/
this.touchLeaveCallback = null;
/**
* @property {Description} touchCancelCallback - Description.
* @default
*/
this.touchCancelCallback = null;
/**
* @property {boolean} preventDefault - Description.
* @default
*/
this.preventDefault = true;
this._onTouchStart = null;
this._onTouchMove = null;
this._onTouchEnd = null;
this._onTouchEnter = null;
this._onTouchLeave = null;
this._onTouchCancel = null;
this._onTouchMove = null;
};
Phaser.Touch.prototype = {
game: null,
/**
* You can disable all Input by setting disabled = true. While set all new input related events will be ignored.
* @type {bool}
*/
disabled: false,
_onTouchStart: null,
_onTouchMove: null,
_onTouchEnd: null,
_onTouchEnter: null,
_onTouchLeave: null,
_onTouchCancel: null,
_onTouchMove: null,
/**
* Starts the event listeners running
* @method start
* Starts the event listeners running.
* @method Phaser.Touch#start
*/
start: function () {
@@ -86,10 +130,9 @@ Phaser.Touch.prototype = {
},
/**
* Consumes all touchmove events on the document (only enable this if you know you need it!)
* @method consumeTouchMove
* @param {Any} event
**/
* Consumes all touchmove events on the document (only enable this if you know you need it!).
* @method Phaser.Touch#consumeTouchMove
*/
consumeDocumentTouches: function () {
this._documentTouchMove = function (event) {
@@ -100,11 +143,11 @@ Phaser.Touch.prototype = {
},
/**
*
* @method onTouchStart
/**
* Description.
* @method Phaser.Touch#onTouchStart
* @param {Any} event
**/
*/
onTouchStart: function (event) {
if (this.touchStartCallback)
@@ -132,12 +175,12 @@ Phaser.Touch.prototype = {
},
/**
* Touch cancel - touches that were disrupted (perhaps by moving into a plugin or browser chrome)
* Occurs for example on iOS when you put down 4 fingers and the app selector UI appears
* @method onTouchCancel
/**
* Touch cancel - touches that were disrupted (perhaps by moving into a plugin or browser chrome).
* Occurs for example on iOS when you put down 4 fingers and the app selector UI appears.
* @method Phaser.Touch#onTouchCancel
* @param {Any} event
**/
*/
onTouchCancel: function (event) {
if (this.touchCancelCallback)
@@ -164,12 +207,12 @@ Phaser.Touch.prototype = {
},
/**
* For touch enter and leave its a list of the touch points that have entered or left the target
* Doesn't appear to be supported by most browsers on a canvas element yet
* @method onTouchEnter
/**
* For touch enter and leave its a list of the touch points that have entered or left the target.
* Doesn't appear to be supported by most browsers on a canvas element yet.
* @method Phaser.Touch#onTouchEnter
* @param {Any} event
**/
*/
onTouchEnter: function (event) {
if (this.touchEnterCallback)
@@ -194,12 +237,12 @@ Phaser.Touch.prototype = {
},
/**
* For touch enter and leave its a list of the touch points that have entered or left the target
* Doesn't appear to be supported by most browsers on a canvas element yet
* @method onTouchLeave
/**
* For touch enter and leave its a list of the touch points that have entered or left the target.
* Doesn't appear to be supported by most browsers on a canvas element yet.
* @method Phaser.Touch#onTouchLeave
* @param {Any} event
**/
*/
onTouchLeave: function (event) {
if (this.touchLeaveCallback)
@@ -219,11 +262,11 @@ Phaser.Touch.prototype = {
},
/**
*
* @method onTouchMove
/**
* Description.
* @method Phaser.Touch#onTouchMove
* @param {Any} event
**/
*/
onTouchMove: function (event) {
if (this.touchMoveCallback)
@@ -243,11 +286,11 @@ Phaser.Touch.prototype = {
},
/**
*
* @method onTouchEnd
/**
* Description.
* @method Phaser.Touch#onTouchEnd
* @param {Any} event
**/
*/
onTouchEnd: function (event) {
if (this.touchEndCallback)
@@ -270,9 +313,9 @@ Phaser.Touch.prototype = {
},
/**
* Stop the event listeners
* @method stop
/**
* Stop the event listeners.
* @method Phaser.Touch#stop
*/
stop: function () {
+325 -145
View File
@@ -1,60 +1,72 @@
/**
* Cache
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Phaser.Cache constructor.
*
* A game only has one instance of a Cache and it is used to store all externally loaded assets such
* @class Phaser.Cache
* @classdesc A game only has one instance of a Cache and it is used to store all externally loaded assets such
* as images, sounds and data files as a result of Loader calls. Cache items use string based keys for look-up.
*
* @package Phaser.Cache
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license https://github.com/photonstorm/phaser/blob/master/license.txt MIT License
* @constructor
* @param {Phaser.Game} game - A reference to the currently running game.
*/
Phaser.Cache = function (game) {
/**
* Local reference to Game.
*/
* @property {Phaser.Game} game - Local reference to game.
*/
this.game = game;
/**
* Canvas key-value container.
* @type {object}
* @private
*/
/**
* @property {object} game - Canvas key-value container.
* @private
*/
this._canvases = {};
/**
* Image key-value container.
* @type {object}
*/
* @property {object} _images - Image key-value container.
* @private
*/
this._images = {};
/**
* RenderTexture key-value container.
* @type {object}
*/
* @property {object} _textures - RenderTexture key-value container.
* @private
*/
this._textures = {};
/**
* Sound key-value container.
* @type {object}
*/
* @property {object} _sounds - Sound key-value container.
* @private
*/
this._sounds = {};
/**
* Text key-value container.
* @type {object}
*/
* @property {object} _text - Text key-value container.
* @private
*/
this._text = {};
/**
* Tilemap key-value container.
* @type {object}
*/
* @property {object} _tilemaps - Tilemap key-value container.
* @private
*/
this._tilemaps = {};
/**
* @property {object} _tilesets - Tileset key-value container.
* @private
*/
this._tilesets = {};
this.addDefaultImage();
/**
* @property {Phaser.Signal} onSoundUnlock - Description.
*/
this.onSoundUnlock = new Phaser.Signal;
};
@@ -62,11 +74,12 @@ Phaser.Cache = function (game) {
Phaser.Cache.prototype = {
/**
* Add a new canvas.
* @param key {string} Asset key for this canvas.
* @param canvas {HTMLCanvasElement} Canvas DOM element.
* @param context {CanvasRenderingContext2D} Render context of this canvas.
*/
* Add a new canvas object in to the cache.
* @method Phaser.Cache#addCanvas
* @param {string} key - Asset key for this canvas.
* @param {HTMLCanvasElement} canvas - Canvas DOM element.
* @param {CanvasRenderingContext2D} context - Render context of this canvas.
*/
addCanvas: function (key, canvas, context) {
this._canvases[key] = { canvas: canvas, context: context };
@@ -74,27 +87,31 @@ Phaser.Cache.prototype = {
},
/**
* Add a new canvas.
* @param key {string} Asset key for this canvas.
* @param canvas {RenderTexture} A RenderTexture.
*/
* Add a new Phaser.RenderTexture in to the cache.
*
* @method Phaser.Cache#addRenderTexture
* @param {string} key - The unique key by which you will reference this object.
* @param {Phaser.Texture} textue - The texture to use as the base of the RenderTexture.
*/
addRenderTexture: function (key, texture) {
var frame = new Phaser.Animation.Frame(0, 0, 0, texture.width, texture.height, '', '');
var frame = new Phaser.Frame(0, 0, 0, texture.width, texture.height, '', '');
this._textures[key] = { texture: texture, frame: frame };
},
/**
* Add a new sprite sheet.
* @param key {string} Asset key for the sprite sheet.
* @param url {string} URL of this sprite sheet file.
* @param data {object} Extra sprite sheet data.
* @param frameWidth {number} Width of the sprite sheet.
* @param frameHeight {number} Height of the sprite sheet.
* @param frameMax {number} How many frames stored in the sprite sheet.
*/
* Add a new sprite sheet in to the cache.
*
* @method Phaser.Cache#addSpriteSheet
* @param {string} key - The unique key by which you will reference this object.
* @param {string} url - URL of this sprite sheet file.
* @param {object} data - Extra sprite sheet data.
* @param {number} frameWidth - Width of the sprite sheet.
* @param {number} frameHeight - Height of the sprite sheet.
* @param {number} frameMax - How many frames stored in the sprite sheet.
*/
addSpriteSheet: function (key, url, data, frameWidth, frameHeight, frameMax) {
this._images[key] = { url: url, data: data, spriteSheet: true, frameWidth: frameWidth, frameHeight: frameHeight };
@@ -102,33 +119,61 @@ Phaser.Cache.prototype = {
PIXI.BaseTextureCache[key] = new PIXI.BaseTexture(data);
PIXI.TextureCache[key] = new PIXI.Texture(PIXI.BaseTextureCache[key]);
this._images[key].frameData = Phaser.Animation.Parser.spriteSheet(this.game, key, frameWidth, frameHeight, frameMax);
this._images[key].frameData = Phaser.AnimationParser.spriteSheet(this.game, key, frameWidth, frameHeight, frameMax);
},
/**
* Add a new tilemap.
* @param key {string} Asset key for the texture atlas.
* @param url {string} URL of this texture atlas file.
* @param data {object} Extra texture atlas data.
* @param atlasData {object} Texture atlas frames data.
*/
addTilemap: function (key, url, data, mapData, format) {
* Add a new tile set in to the cache.
*
* @method Phaser.Cache#addTileset
* @param {string} key - The unique key by which you will reference this object.
* @param {string} url - URL of this tile set file.
* @param {object} data - Extra tile set data.
* @param {number} tileWidth - Width of the sprite sheet.
* @param {number} tileHeight - Height of the sprite sheet.
* @param {number} tileMax - How many tiles stored in the sprite sheet.
* @param {number} [tileMargin=0] - If the tiles have been drawn with a margin, specify the amount here.
* @param {number} [tileSpacing=0] - If the tiles have been drawn with spacing between them, specify the amount here.
*/
addTileset: function (key, url, data, tileWidth, tileHeight, tileMax, tileMargin, tileSpacing) {
this._tilemaps[key] = { url: url, data: data, spriteSheet: true, mapData: mapData, format: format };
this._tilesets[key] = { url: url, data: data, tileWidth: tileWidth, tileHeight: tileHeight, tileMargin: tileMargin, tileSpacing: tileSpacing };
PIXI.BaseTextureCache[key] = new PIXI.BaseTexture(data);
PIXI.TextureCache[key] = new PIXI.Texture(PIXI.BaseTextureCache[key]);
this._tilesets[key].tileData = Phaser.TilemapParser.tileset(this.game, key, tileWidth, tileHeight, tileMax, tileMargin, tileSpacing);
},
/**
* Add a new texture atlas.
* @param key {string} Asset key for the texture atlas.
* @param url {string} URL of this texture atlas file.
* @param data {object} Extra texture atlas data.
* @param atlasData {object} Texture atlas frames data.
*/
* Add a new tilemap.
*
* @method Phaser.Cache#addTilemap
* @param {string} key - The unique key by which you will reference this object.
* @param {string} url - URL of the tilemap image.
* @param {object} mapData - The tilemap data object.
* @param {number} format - The format of the tilemap data.
*/
addTilemap: function (key, url, mapData, format) {
this._tilemaps[key] = { url: url, data: mapData, format: format };
this._tilemaps[key].layers = Phaser.TilemapParser.parse(this.game, mapData, format);
},
/**
* Add a new texture atlas.
*
* @method Phaser.Cache#addTextureAtlas
* @param {string} key - The unique key by which you will reference this object.
* @param {string} url - URL of this texture atlas file.
* @param {object} data - Extra texture atlas data.
* @param {object} atlasData - Texture atlas frames data.
* @param {number} format - The format of the texture atlas.
*/
addTextureAtlas: function (key, url, data, atlasData, format) {
this._images[key] = { url: url, data: data, spriteSheet: true };
@@ -138,26 +183,28 @@ Phaser.Cache.prototype = {
if (format == Phaser.Loader.TEXTURE_ATLAS_JSON_ARRAY)
{
this._images[key].frameData = Phaser.Animation.Parser.JSONData(this.game, atlasData, key);
this._images[key].frameData = Phaser.AnimationParser.JSONData(this.game, atlasData, key);
}
else if (format == Phaser.Loader.TEXTURE_ATLAS_JSON_HASH)
{
this._images[key].frameData = Phaser.Animation.Parser.JSONDataHash(this.game, atlasData, key);
this._images[key].frameData = Phaser.AnimationParser.JSONDataHash(this.game, atlasData, key);
}
else if (format == Phaser.Loader.TEXTURE_ATLAS_XML_STARLING)
{
this._images[key].frameData = Phaser.Animation.Parser.XMLData(this.game, atlasData, key);
this._images[key].frameData = Phaser.AnimationParser.XMLData(this.game, atlasData, key);
}
},
/**
* Add a new Bitmap Font.
* @param key {string} Asset key for the font texture.
* @param url {string} URL of this font xml file.
* @param data {object} Extra font data.
* @param xmlData {object} Texture atlas frames data.
*/
* Add a new Bitmap Font.
*
* @method Phaser.Cache#addBitmapFont
* @param {string} key - The unique key by which you will reference this object.
* @param {string} url - URL of this font xml file.
* @param {object} data - Extra font data.
* @param xmlData {object} Texture atlas frames data.
*/
addBitmapFont: function (key, url, data, xmlData) {
this._images[key] = { url: url, data: data, spriteSheet: true };
@@ -165,40 +212,59 @@ Phaser.Cache.prototype = {
PIXI.BaseTextureCache[key] = new PIXI.BaseTexture(data);
PIXI.TextureCache[key] = new PIXI.Texture(PIXI.BaseTextureCache[key]);
Phaser.Loader.Parser.bitmapFont(this.game, xmlData, key);
// this._images[key].frameData = Phaser.Animation.Parser.XMLData(this.game, xmlData, key);
Phaser.LoaderParser.bitmapFont(this.game, xmlData, key);
// this._images[key].frameData = Phaser.AnimationParser.XMLData(this.game, xmlData, key);
},
/**
* Adds a default image to be used when a key is wrong / missing.
* Is mapped to the key __default
*/
* Adds a default image to be used when a key is wrong / missing. Is mapped to the key __default.
*
* @method Phaser.Cache#addDefaultImage
*/
addDefaultImage: function () {
this._images['__default'] = { url: null, data: null, spriteSheet: false };
this._images['__default'].frame = new Phaser.Animation.Frame(0, 0, 0, 32, 32, '', '');
var img = new Image();
img.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJ9JREFUeNq01ssOwyAMRFG46v//Mt1ESmgh+DFmE2GPOBARKb2NVjo+17PXLD8a1+pl5+A+wSgFygymWYHBb0FtsKhJDdZlncG2IzJ4ayoMDv20wTmSMzClEgbWYNTAkQ0Z+OJ+A/eWnAaR9+oxCF4Os0H8htsMUp+pwcgBBiMNnAwF8GqIgL2hAzaGFFgZauDPKABmowZ4GL369/0rwACp2yA/ttmvsQAAAABJRU5ErkJggg==";
var base = new PIXI.BaseTexture();
base.width = 32;
base.height = 32;
base.hasLoaded = true; // avoids a hanging event listener
this._images['__default'] = { url: null, data: img, spriteSheet: false };
this._images['__default'].frame = new Phaser.Frame(0, 0, 0, 32, 32, '', '');
PIXI.BaseTextureCache['__default'] = base;
PIXI.TextureCache['__default'] = new PIXI.Texture(base);
PIXI.BaseTextureCache['__default'] = new PIXI.BaseTexture(img);
PIXI.TextureCache['__default'] = new PIXI.Texture(PIXI.BaseTextureCache['__default']);
},
/**
* Add a new image.
* @param key {string} Asset key for the image.
* @param url {string} URL of this image file.
* @param data {object} Extra image data.
*/
* Add a new text data.
*
* @method Phaser.Cache#addText
* @param {string} key - Asset key for the text data.
* @param {string} url - URL of this text data file.
* @param {object} data - Extra text data.
*/
addText: function (key, url, data) {
this._text[key] = {
url: url,
data: data
};
},
/**
* Add a new image.
*
* @method Phaser.Cache#addImage
* @param {string} key - The unique key by which you will reference this object.
* @param {string} url - URL of this image file.
* @param {object} data - Extra image data.
*/
addImage: function (key, url, data) {
this._images[key] = { url: url, data: data, spriteSheet: false };
this._images[key].frame = new Phaser.Animation.Frame(0, 0, 0, data.width, data.height, '', '');
this._images[key].frame = new Phaser.Frame(0, 0, 0, data.width, data.height, key, this.game.rnd.uuid());
PIXI.BaseTextureCache[key] = new PIXI.BaseTexture(data);
PIXI.TextureCache[key] = new PIXI.Texture(PIXI.BaseTextureCache[key]);
@@ -206,11 +272,15 @@ Phaser.Cache.prototype = {
},
/**
* Add a new sound.
* @param key {string} Asset key for the sound.
* @param url {string} URL of this sound file.
* @param data {object} Extra sound data.
*/
* Add a new sound.
*
* @method Phaser.Cache#addSound
* @param {string} key - Asset key for the sound.
* @param {string} url - URL of this sound file.
* @param {object} data - Extra sound data.
* @param {boolean} webAudio - True if the file is using web audio.
* @param {boolean} audioTag - True if the file is using legacy HTML audio.
*/
addSound: function (key, url, data, webAudio, audioTag) {
webAudio = webAudio || true;
@@ -228,6 +298,11 @@ Phaser.Cache.prototype = {
},
/**
* Reload a sound.
* @method Phaser.Cache#reloadSound
* @param {string} key - Asset key for the sound.
*/
reloadSound: function (key) {
var _this = this;
@@ -244,6 +319,11 @@ Phaser.Cache.prototype = {
}
},
/**
* Description.
* @method Phaser.Cache#reloadSoundComplete
* @param {string} key - Asset key for the sound.
*/
reloadSoundComplete: function (key) {
if (this._sounds[key])
@@ -254,6 +334,11 @@ Phaser.Cache.prototype = {
},
/**
* Description.
* @method Phaser.Cache#updateSound
* @param {string} key - Asset key for the sound.
*/
updateSound: function (key, property, value) {
if (this._sounds[key])
@@ -265,8 +350,10 @@ Phaser.Cache.prototype = {
/**
* Add a new decoded sound.
* @param key {string} Asset key for the sound.
* @param data {object} Extra sound data.
*
* @method Phaser.Cache#decodedSound
* @param {string} key - Asset key for the sound.
* @param {object} data - Extra sound data.
*/
decodedSound: function (key, data) {
@@ -277,23 +364,10 @@ Phaser.Cache.prototype = {
},
/**
* Add a new text data.
* @param key {string} Asset key for the text data.
* @param url {string} URL of this text data file.
* @param data {object} Extra text data.
*/
addText: function (key, url, data) {
this._text[key] = {
url: url,
data: data
};
},
/**
* Get canvas by key.
* @param key Asset key of the canvas you want.
* Get acanvas object from the cache by its key.
*
* @method Phaser.Cache#getCanvas
* @param {string} key - Asset key of the canvas you want.
* @return {object} The canvas you want.
*/
getCanvas: function (key) {
@@ -308,7 +382,9 @@ Phaser.Cache.prototype = {
/**
* Checks if an image key exists.
* @param key Asset key of the image you want.
*
* @method Phaser.Cache#checkImageKey
* @param {string} key - Asset key of the image you want.
* @return {boolean} True if the key exists, otherwise false.
*/
checkImageKey: function (key) {
@@ -324,7 +400,9 @@ Phaser.Cache.prototype = {
/**
* Get image data by key.
* @param key Asset key of the image you want.
*
* @method Phaser.Cache#getImage
* @param {string} key - Asset key of the image you want.
* @return {object} The image data you want.
*/
getImage: function (key) {
@@ -337,12 +415,50 @@ Phaser.Cache.prototype = {
return null;
},
/**
* Get tile set image data by key.
*
* @method Phaser.Cache#getTileSetImage
* @param {string} key - Asset key of the image you want.
* @return {object} The image data you want.
*/
getTilesetImage: function (key) {
if (this._tilesets[key])
{
return this._tilesets[key].data;
}
return null;
},
/**
* Get tile set image data by key.
*
* @method Phaser.Cache#getTileset
* @param {string} key - Asset key of the image you want.
* @return {Phaser.Tileset} The tileset data. The tileset image is in the data property, the tile data in tileData.
*/
getTileset: function (key) {
if (this._tilesets[key])
{
return this._tilesets[key].tileData;
}
return null;
},
/**
* Get tilemap data by key.
* @param key Asset key of the tilemap you want.
* @return {object} The tilemap data. The tileset image is in the data property, the map data in mapData.
*
* @method Phaser.Cache#getTilemap
* @param {string} key - Asset key of the tilemap you want.
* @return {Object} The tilemap data. The tileset image is in the data property, the map data in mapData.
*/
getTilemap: function (key) {
getTilemapData: function (key) {
if (this._tilemaps[key])
{
@@ -354,8 +470,10 @@ Phaser.Cache.prototype = {
/**
* Get frame data by key.
* @param key Asset key of the frame data you want.
* @return {object} The frame data you want.
*
* @method Phaser.Cache#getFrameData
* @param {string} key - Asset key of the frame data you want.
* @return {Phaser.FrameData} The frame data you want.
*/
getFrameData: function (key) {
@@ -369,8 +487,10 @@ Phaser.Cache.prototype = {
/**
* Get a single frame out of a frameData set by key.
* @param key Asset key of the frame data you want.
* @return {object} The frame data you want.
*
* @method Phaser.Cache#getFrameByIndex
* @param {string} key - Asset key of the frame data you want.
* @return {Phaser.Frame} The frame data you want.
*/
getFrameByIndex: function (key, frame) {
@@ -384,8 +504,10 @@ Phaser.Cache.prototype = {
/**
* Get a single frame out of a frameData set by key.
* @param key Asset key of the frame data you want.
* @return {object} The frame data you want.
*
* @method Phaser.Cache#getFrameByName
* @param {string} key - Asset key of the frame data you want.
* @return {Phaser.Frame} The frame data you want.
*/
getFrameByName: function (key, frame) {
@@ -399,8 +521,10 @@ Phaser.Cache.prototype = {
/**
* Get a single frame by key. You'd only do this to get the default Frame created for a non-atlas/spritesheet image.
* @param key Asset key of the frame data you want.
* @return {object} The frame data you want.
*
* @method Phaser.Cache#getFrame
* @param {string} key - Asset key of the frame data you want.
* @return {Phaser.Frame} The frame data you want.
*/
getFrame: function (key) {
@@ -414,8 +538,10 @@ Phaser.Cache.prototype = {
/**
* Get a single frame by key. You'd only do this to get the default Frame created for a non-atlas/spritesheet image.
* @param key Asset key of the frame data you want.
* @return {object} The frame data you want.
*
* @method Phaser.Cache#getTextureFrame
* @param {string} key - Asset key of the frame data you want.
* @return {Phaser.Frame} The frame data you want.
*/
getTextureFrame: function (key) {
@@ -429,8 +555,10 @@ Phaser.Cache.prototype = {
/**
* Get a RenderTexture by key.
* @param key Asset key of the RenderTexture you want.
* @return {object} The RenderTexture you want.
*
* @method Phaser.Cache#getTexture
* @param {string} key - Asset key of the RenderTexture you want.
* @return {Phaser.RenderTexture} The RenderTexture you want.
*/
getTexture: function (key) {
@@ -445,8 +573,10 @@ Phaser.Cache.prototype = {
/**
* Get sound by key.
* @param key Asset key of the sound you want.
* @return {object} The sound you want.
*
* @method Phaser.Cache#getSound
* @param {string} key - Asset key of the sound you want.
* @return {Phaser.Sound} The sound you want.
*/
getSound: function (key) {
@@ -461,7 +591,9 @@ Phaser.Cache.prototype = {
/**
* Get sound data by key.
* @param key Asset key of the sound you want.
*
* @method Phaser.Cache#getSoundData
* @param {string} key - Asset key of the sound you want.
* @return {object} The sound data you want.
*/
getSoundData: function (key) {
@@ -476,9 +608,11 @@ Phaser.Cache.prototype = {
},
/**
* Check whether an asset is decoded sound.
* @param key Asset key of the sound you want.
* @return {object} The sound data you want.
* Check if the given sound has finished decoding.
*
* @method Phaser.Cache#isSoundDecoded
* @param {string} key - Asset key of the sound you want.
* @return {boolean} The decoded state of the Sound object.
*/
isSoundDecoded: function (key) {
@@ -490,9 +624,11 @@ Phaser.Cache.prototype = {
},
/**
* Check whether an asset is decoded sound.
* @param key Asset key of the sound you want.
* @return {object} The sound data you want.
* Check if the given sound is ready for playback. A sound is considered ready when it has finished decoding and the device is no longer touch locked.
*
* @method Phaser.Cache#isSoundReady
* @param {string} key - Asset key of the sound you want.
* @return {boolean} True if the sound is decoded and the device is not touch locked.
*/
isSoundReady: function (key) {
@@ -501,9 +637,11 @@ Phaser.Cache.prototype = {
},
/**
* Check whether an asset is sprite sheet.
* @param key Asset key of the sprite sheet you want.
* @return {object} The sprite sheet data you want.
* Check whether an image asset is sprite sheet or not.
*
* @method Phaser.Cache#isSpriteSheet
* @param {string} key - Asset key of the sprite sheet you want.
* @return {boolean} True if the image is a sprite sheet.
*/
isSpriteSheet: function (key) {
@@ -518,7 +656,9 @@ Phaser.Cache.prototype = {
/**
* Get text data by key.
* @param key Asset key of the text data you want.
*
* @method Phaser.Cache#getText
* @param {string} key - Asset key of the text data you want.
* @return {object} The text data you want.
*/
getText: function (key) {
@@ -532,6 +672,14 @@ Phaser.Cache.prototype = {
},
/**
* Get the cache keys from a given array of objects.
* Normally you don't call this directly but instead use getImageKeys, getSoundKeys, etc.
*
* @method Phaser.Cache#getKeys
* @param {Array} array - An array of items to return the keys for.
* @return {Array} The array of item keys.
*/
getKeys: function (array) {
var output = [];
@@ -550,6 +698,8 @@ Phaser.Cache.prototype = {
/**
* Returns an array containing all of the keys of Images in the Cache.
*
* @method Phaser.Cache#getImageKeys
* @return {Array} The string based keys in the Cache.
*/
getImageKeys: function () {
@@ -558,6 +708,8 @@ Phaser.Cache.prototype = {
/**
* Returns an array containing all of the keys of Sounds in the Cache.
*
* @method Phaser.Cache#getSoundKeys
* @return {Array} The string based keys in the Cache.
*/
getSoundKeys: function () {
@@ -566,30 +718,58 @@ Phaser.Cache.prototype = {
/**
* Returns an array containing all of the keys of Text Files in the Cache.
*
* @method Phaser.Cache#getTextKeys
* @return {Array} The string based keys in the Cache.
*/
getTextKeys: function () {
return this.getKeys(this._text);
},
/**
* Removes a canvas from the cache.
*
* @method Phaser.Cache#removeCanvas
* @param {string} key - Key of the asset you want to remove.
*/
removeCanvas: function (key) {
delete this._canvases[key];
},
/**
* Removes an image from the cache.
*
* @method Phaser.Cache#removeImage
* @param {string} key - Key of the asset you want to remove.
*/
removeImage: function (key) {
delete this._images[key];
},
/**
* Removes a sound from the cache.
*
* @method Phaser.Cache#removeSound
* @param {string} key - Key of the asset you want to remove.
*/
removeSound: function (key) {
delete this._sounds[key];
},
/**
* Removes a text from the cache.
*
* @method Phaser.Cache#removeText
* @param {string} key - Key of the asset you want to remove.
*/
removeText: function (key) {
delete this._text[key];
},
/**
* Clean up cache memory.
* Clears the cache. Removes every local cache object reference.
*
* @method Phaser.Cache#destroy
*/
destroy: function () {
+324 -151
View File
@@ -1,71 +1,85 @@
/**
* Phaser.Loader
*
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Phaser loader constructor.
* The Loader handles loading all external content such as Images, Sounds, Texture Atlases and data files.
* It uses a combination of Image() loading and xhr and provides progress and completion callbacks.
* @class Phaser.Loader
* @classdesc The Loader handles loading all external content such as Images, Sounds, Texture Atlases and data files.
* It uses a combination of Image() loading and xhr and provides progress and completion callbacks.
* @constructor
* @param {Phaser.Game} game - A reference to the currently running game.
*/
Phaser.Loader = function (game) {
/**
* Local reference to Game.
* @property {Phaser.Game} game - Local reference to game.
*/
this.game = game;
/**
* Array stores assets keys. So you can get that asset by its unique key.
*/
* @property {array} _keys - Array stores assets keys. So you can get that asset by its unique key.
* @private
*/
this._keys = [];
/**
* Contains all the assets file infos.
*/
* @property {Description} _fileList - Contains all the assets file infos.
* @private
*/
this._fileList = {};
/**
* Indicates assets loading progress. (from 0 to 100)
* @type {number}
* @property {number} _progressChunk - Indicates assets loading progress. (from 0 to 100)
* @private
* @default
*/
this._progressChunk = 0;
/**
* An XMLHttpRequest object used for loading text and audio data
* @type {XMLHttpRequest}
* @property {XMLHttpRequest} - An XMLHttpRequest object used for loading text and audio data.
* @private
*/
this._xhr = new XMLHttpRequest();
/**
* Length of assets queue.
* @type {number}
/**
* @property {number} - Length of assets queue.
* @default
*/
this.queueSize = 0;
/**
* True if the Loader is in the process of loading the queue.
* @type {bool}
* @property {boolean} isLoading - True if the Loader is in the process of loading the queue.
* @default
*/
this.isLoading = false;
/**
* True if all assets in the queue have finished loading.
* @type {bool}
* @property {boolean} hasLoaded - True if all assets in the queue have finished loading.
* @default
*/
this.hasLoaded = false;
/**
* The Load progress percentage value (from 0 to 100)
* @type {number}
* @property {number} progress - The Load progress percentage value (from 0 to 100)
* @default
*/
this.progress = 0;
/**
* You can optionally link a sprite to the preloader.
* If you do so the Sprite's width or height will be cropped based on the percentage loaded.
* @property {Description} preloadSprite
* @default
*/
this.preloadSprite = null;
/**
* The crossOrigin value applied to loaded images
* @type {string}
* @property {string} crossOrigin - The crossOrigin value applied to loaded images
*/
this.crossOrigin = '';
@@ -73,29 +87,62 @@ Phaser.Loader = function (game) {
* If you want to append a URL before the path of any asset you can set this here.
* Useful if you need to allow an asset url to be configured outside of the game code.
* MUST have / on the end of it!
* @type {string}
* @property {string} baseURL
* @default
*/
this.baseURL = '';
/**
* Event Signals
*/
* @property {Phaser.Signal} onFileComplete - Event signal.
*/
this.onFileComplete = new Phaser.Signal;
/**
* @property {Phaser.Signal} onFileError - Event signal.
*/
this.onFileError = new Phaser.Signal;
/**
* @property {Phaser.Signal} onLoadStart - Event signal.
*/
this.onLoadStart = new Phaser.Signal;
/**
* @property {Phaser.Signal} onLoadComplete - Event signal.
*/
this.onLoadComplete = new Phaser.Signal;
};
/**
* TextureAtlas data format constants
*/
* @constant
* @type {number}
*/
Phaser.Loader.TEXTURE_ATLAS_JSON_ARRAY = 0;
/**
* @constant
* @type {number}
*/
Phaser.Loader.TEXTURE_ATLAS_JSON_HASH = 1;
/**
* @constant
* @type {number}
*/
Phaser.Loader.TEXTURE_ATLAS_XML_STARLING = 2;
Phaser.Loader.prototype = {
/**
* You can set a Sprite to be a "preload" sprite by passing it to this method.
* A "preload" sprite will have its width or height crop adjusted based on the percentage of the loader in real-time.
* This allows you to easily make loading bars for games.
*
* @method Phaser.Loader#setPreloadSprite
* @param {Phaser.Sprite} sprite - The sprite that will be cropped during the load.
* @param {number} [direction=0] - A value of zero means the sprite width will be cropped, a value of 1 means its height will be cropped.
*/
setPreloadSprite: function (sprite, direction) {
direction = direction || 0;
@@ -105,22 +152,25 @@ Phaser.Loader.prototype = {
if (direction == 0)
{
// Horizontal crop
this.preloadSprite.crop = new Phaser.Rectangle(0, 0, 0, sprite.height);
this.preloadSprite.crop = new Phaser.Rectangle(0, 0, 1, sprite.height);
}
else
{
// Vertical crop
this.preloadSprite.crop = new Phaser.Rectangle(0, 0, sprite.width, 0);
this.preloadSprite.crop = new Phaser.Rectangle(0, 0, sprite.width, 1);
}
sprite.crop = this.preloadSprite.crop;
sprite.cropEnabled = true;
},
/**
* Check whether asset exists with a specific key.
* @param key {string} Key of the asset you want to check.
* @return {bool} Return true if exists, otherwise return false.
*
* @method Phaser.Loader#checkKeyExists
* @param {string} key - Key of the asset you want to check.
* @return {boolean} Return true if exists, otherwise return false.
*/
checkKeyExists: function (key) {
@@ -136,8 +186,10 @@ Phaser.Loader.prototype = {
},
/**
* Reset loader, this will remove all loaded assets.
*/
* Reset loader, this will remove all loaded assets.
*
* @method Phaser.Loader#reset
*/
reset: function () {
this.preloadSprite = null;
@@ -147,7 +199,14 @@ Phaser.Loader.prototype = {
},
/**
* Internal function that adds a new entry to the file list.
* Internal function that adds a new entry to the file list. Do not call directly.
*
* @method Phaser.Loader#addToFileList
* @param {Description} type - Description.
* @param {string} key - Description.
* @param {string} url - URL of Description.
* @param {Description} properties - Description.
* @protected
*/
addToFileList: function (type, key, url, properties) {
@@ -178,9 +237,11 @@ Phaser.Loader.prototype = {
/**
* Add an image to the Loader.
* @param key {string} Unique asset key of this image file.
* @param url {string} URL of image file.
* @param overwrite {boolean} If an entry with a matching key already exists this will over-write it
*
* @method Phaser.Loader#image
* @param {string} key - Unique asset key of this image file.
* @param {string} url - URL of image file.
* @param {boolean} overwrite - If an entry with a matching key already exists this will over-write it
*/
image: function (key, url, overwrite) {
@@ -191,12 +252,17 @@ Phaser.Loader.prototype = {
this.addToFileList('image', key, url);
}
return this;
},
/**
* Add a text file to the Loader.
* @param key {string} Unique asset key of the text file.
* @param url {string} URL of the text file.
*
* @method Phaser.Loader#text
* @param {string} key - Unique asset key of the text file.
* @param {string} url - URL of the text file.
* @param {boolean} overwrite - True if Description.
*/
text: function (key, url, overwrite) {
@@ -207,15 +273,19 @@ Phaser.Loader.prototype = {
this.addToFileList('text', key, url);
}
return this;
},
/**
* Add a new sprite sheet loading request.
* @param key {string} Unique asset key of the sheet file.
* @param url {string} URL of sheet file.
* @param frameWidth {number} Width of each single frame.
* @param frameHeight {number} Height of each single frame.
* @param frameMax {number} How many frames in this sprite sheet.
* Add a new sprite sheet to the loader.
*
* @method Phaser.Loader#spritesheet
* @param {string} key - Unique asset key of the sheet file.
* @param {string} url - URL of the sheet file.
* @param {number} frameWidth - Width of each single frame.
* @param {number} frameHeight - Height of each single frame.
* @param {number} [frameMax=-1] - How many frames in this sprite sheet. If not specified it will divide the whole image into frames.
*/
spritesheet: function (key, url, frameWidth, frameHeight, frameMax) {
@@ -226,13 +296,44 @@ Phaser.Loader.prototype = {
this.addToFileList('spritesheet', key, url, { frameWidth: frameWidth, frameHeight: frameHeight, frameMax: frameMax });
}
return this;
},
/**
* Add a new audio file loading request.
* @param key {string} Unique asset key of the audio file.
* @param urls {Array} An array containing the URLs of the audio files, i.e.: [ 'jump.mp3', 'jump.ogg', 'jump.m4a' ]
* @param autoDecode {bool} When using Web Audio the audio files can either be decoded at load time or run-time. They can't be played until they are decoded, but this let's you control when that happens. Decoding is a non-blocking async process.
* Add a new tile set to the loader. These are used in the rendering of tile maps.
*
* @method Phaser.Loader#tileset
* @param {string} key - Unique asset key of the tileset file.
* @param {string} url - URL of the tileset.
* @param {number} tileWidth - Width of each single tile in pixels.
* @param {number} tileHeight - Height of each single tile in pixels.
* @param {number} [tileMax=-1] - How many tiles in this tileset. If not specified it will divide the whole image into tiles.
* @param {number} [tileMargin=0] - If the tiles have been drawn with a margin, specify the amount here.
* @param {number} [tileSpacing=0] - If the tiles have been drawn with spacing between them, specify the amount here.
*/
tileset: function (key, url, tileWidth, tileHeight, tileMax, tileMargin, tileSpacing) {
if (typeof tileMax === "undefined") { tileMax = -1; }
if (typeof tileMargin === "undefined") { tileMargin = 0; }
if (typeof tileSpacing === "undefined") { tileSpacing = 0; }
if (this.checkKeyExists(key) === false)
{
this.addToFileList('tileset', key, url, { tileWidth: tileWidth, tileHeight: tileHeight, tileMax: tileMax, tileMargin: tileMargin, tileSpacing: tileSpacing });
}
return this;
},
/**
* Add a new audio file to the loader.
*
* @method Phaser.Loader#audio
* @param {string} key - Unique asset key of the audio file.
* @param {Array} urls - An array containing the URLs of the audio files, i.e.: [ 'jump.mp3', 'jump.ogg', 'jump.m4a' ].
* @param {boolean} autoDecode - When using Web Audio the audio files can either be decoded at load time or run-time. They can't be played until they are decoded, but this let's you control when that happens. Decoding is a non-blocking async process.
*/
audio: function (key, urls, autoDecode) {
@@ -243,28 +344,39 @@ Phaser.Loader.prototype = {
this.addToFileList('audio', key, urls, { buffer: null, autoDecode: autoDecode });
}
return this;
},
/**
* Add a new tilemap loading request.
* @param key {string} Unique asset key of the tilemap data.
* @param tilesetURL {string} The url of the tile set image file.
* @param [mapDataURL] {string} The url of the map data file (csv/json)
* @param [mapData] {object} An optional JSON data object (can be given in place of a URL).
* @param [format] {string} The format of the map data.
*
* @method Phaser.Loader#tilemap
* @param {string} key - Unique asset key of the tilemap data.
* @param {string} tilesetURL - The url of the tile set image file.
* @param {string} [mapDataURL] - The url of the map data file (csv/json)
* @param {object} [mapData] - An optional JSON data object (can be given in place of a URL).
* @param {string} [format] - The format of the map data.
*/
tilemap: function (key, tilesetURL, mapDataURL, mapData, format) {
tilemap: function (key, mapDataURL, mapData, format) {
if (typeof mapDataURL === "undefined") { mapDataURL = null; }
if (typeof mapData === "undefined") { mapData = null; }
if (typeof format === "undefined") { format = Phaser.Tilemap.CSV; }
if (mapDataURL == null && mapData == null)
{
console.warn('Phaser.Loader.tilemap - Both mapDataURL and mapData are null. One must be set.');
return this;
}
if (this.checkKeyExists(key) === false)
{
// A URL to a json/csv file has been given
if (mapDataURL)
{
this.addToFileList('tilemap', key, tilesetURL, { mapDataURL: mapDataURL, format: format });
this.addToFileList('tilemap', key, mapDataURL, { format: format });
}
else
{
@@ -275,7 +387,7 @@ Phaser.Loader.prototype = {
break;
// An xml string or object has been given
case Phaser.Tilemap.JSON:
case Phaser.Tilemap.TILED_JSON:
if (typeof mapData === 'string')
{
@@ -284,19 +396,23 @@ Phaser.Loader.prototype = {
break;
}
this.addToFileList('tilemap', key, tilesetURL, { mapDataURL: null, mapData: mapData, format: format });
this.game.cache.addTilemap(key, null, mapData, format);
}
}
return this;
},
/**
* Add a new bitmap font loading request.
* @param key {string} Unique asset key of the bitmap font.
* @param textureURL {string} The url of the font image file.
* @param [xmlURL] {string} The url of the font data file (xml/fnt)
* @param [xmlData] {object} An optional XML data object.
*
* @method Phaser.Loader#bitmapFont
* @param {string} key - Unique asset key of the bitmap font.
* @param {string} textureURL - The url of the font image file.
* @param {string} [xmlURL] - The url of the font data file (xml/fnt)
* @param {object} [xmlData] - An optional XML data object.
*/
bitmapFont: function (key, textureURL, xmlURL, xmlData) {
@@ -347,33 +463,61 @@ Phaser.Loader.prototype = {
}
}
},
atlasJSONArray: function (key, textureURL, atlasURL, atlasData) {
this.atlas(key, textureURL, atlasURL, atlasData, Phaser.Loader.TEXTURE_ATLAS_JSON_ARRAY);
},
atlasJSONHash: function (key, textureURL, atlasURL, atlasData) {
this.atlas(key, textureURL, atlasURL, atlasData, Phaser.Loader.TEXTURE_ATLAS_JSON_HASH);
},
atlasXML: function (key, textureURL, atlasURL, atlasData) {
this.atlas(key, textureURL, atlasURL, atlasData, Phaser.Loader.TEXTURE_ATLAS_XML_STARLING);
return this;
},
/**
* Add a new texture atlas loading request.
* @param key {string} Unique asset key of the texture atlas file.
* @param textureURL {string} The url of the texture atlas image file.
* @param [atlasURL] {string} The url of the texture atlas data file (json/xml)
* @param [atlasData] {object} A JSON or XML data object.
* @param [format] {number} A value describing the format of the data.
* Add a new texture atlas to the loader. This atlas uses the JSON Array data format.
*
* @method Phaser.Loader#atlasJSONArray
* @param {string} key - Unique asset key of the bitmap font.
* @param {Description} atlasURL - The url of the Description.
* @param {Description} atlasData - Description.
*/
atlasJSONArray: function (key, textureURL, atlasURL, atlasData) {
return this.atlas(key, textureURL, atlasURL, atlasData, Phaser.Loader.TEXTURE_ATLAS_JSON_ARRAY);
},
/**
* Add a new texture atlas to the loader. This atlas uses the JSON Hash data format.
*
* @method Phaser.Loader#atlasJSONHash
* @param {string} key - Unique asset key of the bitmap font.
* @param {Description} atlasURL - The url of the Description.
* @param {Description} atlasData - Description.
*/
atlasJSONHash: function (key, textureURL, atlasURL, atlasData) {
return this.atlas(key, textureURL, atlasURL, atlasData, Phaser.Loader.TEXTURE_ATLAS_JSON_HASH);
},
/**
* Add a new texture atlas to the loader. This atlas uses the Starling XML data format.
*
* @method Phaser.Loader#atlasXML
* @param {string} key - Unique asset key of the bitmap font.
* @param {Description} atlasURL - The url of the Description.
* @param {Description} atlasData - Description.
*/
atlasXML: function (key, textureURL, atlasURL, atlasData) {
return this.atlas(key, textureURL, atlasURL, atlasData, Phaser.Loader.TEXTURE_ATLAS_XML_STARLING);
},
/**
* Add a new texture atlas to the loader.
*
* @method Phaser.Loader#atlas
* @param {string} key - Unique asset key of the texture atlas file.
* @param {string} textureURL - The url of the texture atlas image file.
* @param {string} [atlasURL] - The url of the texture atlas data file (json/xml). You don't need this if you are passing an atlasData object instead.
* @param {object} [atlasData] - A JSON or XML data object. You don't need this if the data is being loaded from a URL.
* @param {number} [format] - A value describing the format of the data, the default is Phaser.Loader.TEXTURE_ATLAS_JSON_ARRAY.
*/
atlas: function (key, textureURL, atlasURL, atlasData, format) {
@@ -444,12 +588,16 @@ Phaser.Loader.prototype = {
}
return this;
},
/**
* Remove loading request of a file.
* @param key {string} Key of the file you want to remove.
*/
* Remove loading request of a file.
*
* @method Phaser.Loader#removeFile
* @param key {string} Key of the file you want to remove.
*/
removeFile: function (key) {
delete this._fileList[key];
@@ -457,8 +605,10 @@ Phaser.Loader.prototype = {
},
/**
* Remove all file loading requests.
*/
* Remove all file loading requests.
*
* @method Phaser.Loader#removeAll
*/
removeAll: function () {
this._fileList = {};
@@ -466,8 +616,10 @@ Phaser.Loader.prototype = {
},
/**
* Load assets.
*/
* Start loading the assets. Normally you don't need to call this yourself as the StateManager will do so.
*
* @method Phaser.Loader#start
*/
start: function () {
if (this.isLoading)
@@ -497,6 +649,8 @@ Phaser.Loader.prototype = {
/**
* Load files. Private method ONLY used by loader.
*
* @method Phaser.Loader#loadFile
* @private
*/
loadFile: function () {
@@ -511,7 +665,7 @@ Phaser.Loader.prototype = {
case 'spritesheet':
case 'textureatlas':
case 'bitmapfont':
case 'tilemap':
case 'tileset':
file.data = new Image();
file.data.name = file.key;
file.data.onload = function () {
@@ -574,6 +728,29 @@ Phaser.Loader.prototype = {
break;
case 'tilemap':
this._xhr.open("GET", this.baseURL + file.url, true);
this._xhr.responseType = "text";
if (file.format == Phaser.Tilemap.TILED_JSON)
{
this._xhr.onload = function () {
return _this.jsonLoadComplete(file.key);
};
}
else if (file.format == Phaser.Tilemap.CSV)
{
this._xhr.onload = function () {
return _this.csvLoadComplete(file.key);
};
}
this._xhr.onerror = function () {
return _this.dataLoadError(file.key);
};
this._xhr.send();
break;
case 'text':
this._xhr.open("GET", this.baseURL + file.url, true);
this._xhr.responseType = "text";
@@ -589,6 +766,12 @@ Phaser.Loader.prototype = {
},
/**
* Private method ONLY used by loader.
* @method Phaser.Loader#getAudioURL
* @param {Description} urls - Description.
* @private
*/
getAudioURL: function (urls) {
var extension;
@@ -610,9 +793,11 @@ Phaser.Loader.prototype = {
},
/**
* Error occured when load a file.
* @param key {string} Key of the error loading file.
*/
* Error occured when load a file.
*
* @method Phaser.Loader#fileError
* @param {string} key - Key of the error loading file.
*/
fileError: function (key) {
this._fileList[key].loaded = true;
@@ -620,16 +805,18 @@ Phaser.Loader.prototype = {
this.onFileError.dispatch(key);
console.warn("Phaser.Loader error loading file: " + key);
console.warn("Phaser.Loader error loading file: " + key + ' from URL ' + this._fileList[key].url);
this.nextFile(key, false);
},
/**
* Called when a file is successfully loaded.
* @param key {string} Key of the successfully loaded file.
*/
* Called when a file is successfully loaded.
*
* @method Phaser.Loader#fileComplete
* @param {string} key - Key of the successfully loaded file.
*/
fileComplete: function (key) {
if (!this._fileList[key])
@@ -656,37 +843,9 @@ Phaser.Loader.prototype = {
this.game.cache.addSpriteSheet(file.key, file.url, file.data, file.frameWidth, file.frameHeight, file.frameMax);
break;
case 'tilemap':
case 'tileset':
if (file.mapDataURL == null)
{
this.game.cache.addTilemap(file.key, file.url, file.data, file.mapData, file.format);
}
else
{
// Load the JSON or CSV before carrying on with the next file
loadNext = false;
this._xhr.open("GET", this.baseURL + file.mapDataURL, true);
this._xhr.responseType = "text";
if (file.format == Phaser.Tilemap.JSON)
{
this._xhr.onload = function () {
return _this.jsonLoadComplete(file.key);
};
}
else if (file.format == Phaser.Tilemap.CSV)
{
this._xhr.onload = function () {
return _this.csvLoadComplete(file.key);
};
}
this._xhr.onerror = function () {
return _this.dataLoadError(file.key);
};
this._xhr.send();
}
this.game.cache.addTileset(file.key, file.url, file.data, file.tileWidth, file.tileHeight, file.tileMax, file.tileMargin, file.tileSpacing);
break;
case 'textureatlas':
@@ -777,7 +936,7 @@ Phaser.Loader.prototype = {
break;
case 'text':
file.data = this._xhr.response;
file.data = this._xhr.responseText;
this.game.cache.addText(file.key, file.url, file.data);
break;
}
@@ -790,17 +949,19 @@ Phaser.Loader.prototype = {
},
/**
* Successfully loaded a JSON file.
* @param key {string} Key of the loaded JSON file.
*/
* Successfully loaded a JSON file.
*
* @method Phaser.Loader#jsonLoadComplete
* @param {string} key - Key of the loaded JSON file.
*/
jsonLoadComplete: function (key) {
var data = JSON.parse(this._xhr.response);
var data = JSON.parse(this._xhr.responseText);
var file = this._fileList[key];
if (file.type == 'tilemap')
{
this.game.cache.addTilemap(file.key, file.url, file.data, data, file.format);
this.game.cache.addTilemap(file.key, file.url, data, file.format);
}
else
{
@@ -812,24 +973,28 @@ Phaser.Loader.prototype = {
},
/**
* Successfully loaded a CSV file.
* @param key {string} Key of the loaded CSV file.
*/
* Successfully loaded a CSV file.
*
* @method Phaser.Loader#csvLoadComplete
* @param {string} key - Key of the loaded CSV file.
*/
csvLoadComplete: function (key) {
var data = this._xhr.response;
var data = this._xhr.responseText;
var file = this._fileList[key];
this.game.cache.addTilemap(file.key, file.url, file.data, data, file.format);
this.game.cache.addTilemap(file.key, file.url, data, file.format);
this.nextFile(key, true);
},
/**
* Error occured when load a JSON.
* @param key {string} Key of the error loading JSON file.
*/
* Error occured when load a JSON.
*
* @method Phaser.Loader#dataLoadError
* @param {string} key - Key of the error loading JSON file.
*/
dataLoadError: function (key) {
var file = this._fileList[key];
@@ -842,9 +1007,15 @@ Phaser.Loader.prototype = {
},
/**
* Successfully loaded an XML file.
*
* @method Phaser.Loader#xmlLoadComplete
* @param {string} key - Key of the loaded XML file.
*/
xmlLoadComplete: function (key) {
var data = this._xhr.response;
var data = this._xhr.responseText;
var xml;
try
@@ -887,10 +1058,12 @@ Phaser.Loader.prototype = {
},
/**
* Handle loading next file.
* @param previousKey {string} Key of previous loaded asset.
* @param success {bool} Whether the previous asset loaded successfully or not.
*/
* Handle loading next file.
*
* @param previousKey {string} Key of previous loaded asset.
* @param success {boolean} Whether the previous asset loaded successfully or not.
* @private
*/
nextFile: function (previousKey, success) {
this.progress = Math.round(this.progress + this._progressChunk);
@@ -904,11 +1077,11 @@ Phaser.Loader.prototype = {
{
if (this.preloadSprite.direction == 0)
{
this.preloadSprite.crop.width = (this.preloadSprite.width / 100) * this.progress;
this.preloadSprite.crop.width = Math.floor((this.preloadSprite.width / 100) * this.progress);
}
else
{
this.preloadSprite.crop.height = (this.preloadSprite.height / 100) * this.progress;
this.preloadSprite.crop.height = Math.floor((this.preloadSprite.height / 100) * this.progress);
}
this.preloadSprite.sprite.crop = this.preloadSprite.crop;
@@ -1,8 +1,20 @@
Phaser.Loader.Parser = {
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Phaser.LoaderParser parses data objects from Phaser.Loader that need more preparation before they can be inserted into the Cache.
*
* @class Phaser.LoaderParser
*/
Phaser.LoaderParser = {
/**
* Parse frame data from an XML file.
* @param xml {object} XML data you want to parse.
* @method Phaser.LoaderParser.bitmapFont
* @param {object} xml - XML data you want to parse.
* @return {FrameData} Generated FrameData object.
*/
bitmapFont: function (game, xml, cacheKey) {
@@ -10,7 +22,7 @@ Phaser.Loader.Parser = {
// Malformed?
if (!xml.getElementsByTagName('font'))
{
console.warn("Phaser.Loader.Parser.bitmapFont: Invalid XML given, missing <font> tag");
console.warn("Phaser.LoaderParser.bitmapFont: Invalid XML given, missing <font> tag");
return;
}
+401 -143
View File
@@ -1,32 +1,88 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* A collection of mathematical methods.
*
* @class Phaser.Math
*/
Phaser.Math = {
/**
* = 2 &pi;
* @method Phaser.Math#PI2
*/
PI2: Math.PI * 2,
/**
* Two number are fuzzyEqual if their difference is less than &epsilon;.
* @method Phaser.Math#fuzzyEqual
* @param {number} a
* @param {number} b
* @param {number} epsilon
* @return {boolean} True if |a-b|<&epsilon;
*/
fuzzyEqual: function (a, b, epsilon) {
if (typeof epsilon === "undefined") { epsilon = 0.0001; }
return Math.abs(a - b) < epsilon;
},
/**
* a is fuzzyLessThan b if it is less than b + &epsilon;.
* @method Phaser.Math#fuzzyEqual
* @param {number} a
* @param {number} b
* @param {number} epsilon
* @return {boolean} True if a<b+&epsilon;
*/
fuzzyLessThan: function (a, b, epsilon) {
if (typeof epsilon === "undefined") { epsilon = 0.0001; }
return a < b + epsilon;
},
/**
* a is fuzzyGreaterThan b if it is more than b - &epsilon;.
* @method Phaser.Math#fuzzyGreaterThan
* @param {number} a
* @param {number} b
* @param {number} epsilon
* @return {boolean} True if a>b+&epsilon;
*/
fuzzyGreaterThan: function (a, b, epsilon) {
if (typeof epsilon === "undefined") { epsilon = 0.0001; }
return a > b - epsilon;
},
/**
* @method Phaser.Math#fuzzyCeil
* @param {number} val
* @param {number} epsilon
* @return {boolean} ceiling(val-&epsilon;)
*/
fuzzyCeil: function (val, epsilon) {
if (typeof epsilon === "undefined") { epsilon = 0.0001; }
return Math.ceil(val - epsilon);
},
/**
* @method Phaser.Math#fuzzyFloor
* @param {number} val
* @param {number} epsilon
* @return {boolean} floor(val-&epsilon;)
*/
fuzzyFloor: function (val, epsilon) {
if (typeof epsilon === "undefined") { epsilon = 0.0001; }
return Math.floor(val + epsilon);
},
/**
* Averages all values passed to the function and returns the result. You can pass as many parameters as you like.
* @method Phaser.Math#average
* @return {number} The average of all given values.
*/
average: function () {
var args = [];
@@ -45,10 +101,20 @@ Phaser.Math = {
},
/**
* @method Phaser.Math#truncate
* @param {number} n
* @return {number}
*/
truncate: function (n) {
return (n > 0) ? Math.floor(n) : Math.ceil(n);
},
/**
* @method Phaser.Math#shear
* @param {number} n
* @return {number} n mod 1
*/
shear: function (n) {
return n % 1;
},
@@ -56,11 +122,13 @@ Phaser.Math = {
/**
* Snap a value to nearest grid slice, using rounding.
*
* example if you have an interval gap of 5 and a position of 12... you will snap to 10. Where as 14 will snap to 15
* Example: if you have an interval gap of 5 and a position of 12... you will snap to 10 whereas 14 will snap to 15.
*
* @param input - the value to snap
* @param gap - the interval gap of the grid
* @param [start] - optional starting offset for gap
* @method Phaser.Math#snapTo
* @param {number} input - The value to snap.
* @param {number} gap - The interval gap of the grid.
* @param {number} [start] - Optional starting offset for gap.
* @return {number}
*/
snapTo: function (input, gap, start) {
@@ -80,11 +148,13 @@ Phaser.Math = {
/**
* Snap a value to nearest grid slice, using floor.
*
* example if you have an interval gap of 5 and a position of 12... you will snap to 10. As will 14 snap to 10... but 16 will snap to 15
* Example: if you have an interval gap of 5 and a position of 12... you will snap to 10. As will 14 snap to 10... but 16 will snap to 15
*
* @param input - the value to snap
* @param gap - the interval gap of the grid
* @param [start] - optional starting offset for gap
* @method Phaser.Math#snapToFloor
* @param {number} input - The value to snap.
* @param {number} gap - The interval gap of the grid.
* @param {number} [start] - Optional starting offset for gap.
* @return {number}
*/
snapToFloor: function (input, gap, start) {
@@ -104,11 +174,13 @@ Phaser.Math = {
/**
* Snap a value to nearest grid slice, using ceil.
*
* example if you have an interval gap of 5 and a position of 12... you will snap to 15. As will 14 will snap to 15... but 16 will snap to 20
* Example: if you have an interval gap of 5 and a position of 12... you will snap to 15. As will 14 will snap to 15... but 16 will snap to 20.
*
* @param input - the value to snap
* @param gap - the interval gap of the grid
* @param [start] - optional starting offset for gap
* @method Phaser.Math#snapToCeil
* @param {number} input - The value to snap.
* @param {number} gap - The interval gap of the grid.
* @param {number} [start] - Optional starting offset for gap.
* @return {number}
*/
snapToCeil: function (input, gap, start) {
@@ -128,6 +200,11 @@ Phaser.Math = {
/**
* Snaps a value to the nearest value in an array.
* @method Phaser.Math#snapToInArray
* @param {number} input
* @param {array} arr
* @param {boolean} sort - True if the array needs to be sorted.
* @return {number}
*/
snapToInArray: function (input, arr, sort) {
@@ -155,16 +232,10 @@ Phaser.Math = {
},
/**
* roundTo some place comparative to a 'base', default is 10 for decimal place
* Round to some place comparative to a 'base', default is 10 for decimal place.
*
* 'place' is represented by the power applied to 'base' to get that place
*
* @param value - the value to round
* @param place - the place to round to
* @param base - the base to round in... default is 10 for decimal
*
* e.g.
*
* 2000/7 ~= 285.714285714285714285714 ~= (bin)100011101.1011011011011011
*
* roundTo(2000/7,3) == 0
@@ -187,8 +258,14 @@ Phaser.Math = {
* roundTo(2000/7,-4,2) == 285.6875 -- 100011101.1011
* roundTo(2000/7,-5,2) == 285.71875 -- 100011101.10111
*
* note what occurs when we round to the 3rd space (8ths place), 100100000, this is to be assumed
* Note what occurs when we round to the 3rd space (8ths place), 100100000, this is to be assumed
* because we are rounding 100011.1011011011011011 which rounds up.
*
* @method Phaser.Math#roundTo
* @param {number} value - The value to round.
* @param {number} place - The place to round to.
* @param {number} base - The base to round in... default is 10 for decimal.
* @return {number}
*/
roundTo: function (value, place, base) {
@@ -201,6 +278,13 @@ Phaser.Math = {
},
/**
* @method Phaser.Math#floorTo
* @param {number} value - The value to round.
* @param {number} place - The place to round to.
* @param {number} base - The base to round in... default is 10 for decimal.
* @return {number}
*/
floorTo: function (value, place, base) {
if (typeof place === "undefined") { place = 0; }
@@ -212,6 +296,13 @@ Phaser.Math = {
},
/**
* @method Phaser.Math#ceilTo
* @param {number} value - The value to round.
* @param {number} place - The place to round to.
* @param {number} base - The base to round in... default is 10 for decimal.
* @return {number}
*/
ceilTo: function (value, place, base) {
if (typeof place === "undefined") { place = 0; }
@@ -224,34 +315,54 @@ Phaser.Math = {
},
/**
* a one dimensional linear interpolation of a value.
* A one dimensional linear interpolation of a value.
* @method Phaser.Math#interpolateFloat
* @param {number} a
* @param {number} b
* @param {number} weight
* @return {number}
*/
interpolateFloat: function (a, b, weight) {
return (b - a) * weight + a;
},
/**
* Find the angle of a segment from (x1, y1) -> (x2, y2 )
* Find the angle of a segment from (x1, y1) -> (x2, y2 ).
* @method Phaser.Math#angleBetween
* @param {number} x1
* @param {number} y1
* @param {number} x2
* @param {number} y2
* @return {number}
*/
angleBetween: function (x1, y1, x2, y2) {
return Math.atan2(y2 - y1, x2 - x1);
},
/**
* set an angle within the bounds of -PI to PI
* Set an angle within the bounds of -&pi; to&pi;.
* @method Phaser.Math#normalizeAngle
* @param {number} angle
* @param {boolean} radians - True if angle size is expressed in radians.
* @return {number}
*/
normalizeAngle: function (angle, radians) {
if (typeof radians === "undefined") { radians = true; }
var rd = (radians) ? Math.PI : 180;
return this.wrap(angle, -rd, rd);
var rd = (radians) ? GameMath.PI : 180;
return this.wrap(angle, rd, -rd);
},
/**
* closest angle between two angles from a1 to a2
* Closest angle between two angles from a1 to a2
* absolute value the return for exact angle
* @method Phaser.Math#nearestAngleBetween
* @param {number} a1
* @param {number} a2
* @param {boolean} radians - True if angle sizes are expressed in radians.
* @return {number}
*/
nearestAngleBetween: function (a1, a2, radians) {
@@ -276,7 +387,14 @@ Phaser.Math = {
},
/**
* interpolate across the shortest arc between two angles
* Interpolate across the shortest arc between two angles.
* @method Phaser.Math#interpolateAngles
* @param {number} a1 - Description.
* @param {number} a2 - Description.
* @param {number} weight - Description.
* @param {boolean} radians - True if angle sizes are expressed in radians.
* @param {Description} ease - Description.
* @return {number}
*/
interpolateAngles: function (a1, a2, weight, radians, ease) {
@@ -291,13 +409,14 @@ Phaser.Math = {
},
/**
* Generate a random bool result based on the chance value
* Generate a random bool result based on the chance value.
* <p>
* Returns true or false based on the chance value (default 50%). For example if you wanted a player to have a 30% chance
* of getting a bonus, call chanceRoll(30) - true means the chance passed, false means it failed.
* </p>
* @param chance The chance of receiving the value. A number between 0 and 100 (effectively 0% to 100%)
* @return true if the roll passed, or false
* @method Phaser.Math#chanceRoll
* @param {number} chance - The chance of receiving the value. A number between 0 and 100 (effectively 0% to 100%).
* @return {boolean} True if the roll passed, or false otherwise.
*/
chanceRoll: function (chance) {
@@ -326,11 +445,12 @@ Phaser.Math = {
},
/**
* Returns an Array containing the numbers from min to max (inclusive)
* Returns an Array containing the numbers from min to max (inclusive).
*
* @param min The minimum value the array starts with
* @param max The maximum value the array contains
* @return The array of number values
* @method Phaser.Math#numberArray
* @param {number} min - The minimum value the array starts with.
* @param {number} max - The maximum value the array contains.
* @return {array} The array of number values.
*/
numberArray: function (min, max) {
@@ -346,12 +466,13 @@ Phaser.Math = {
},
/**
* Adds the given amount to the value, but never lets the value go over the specified maximum
* Adds the given amount to the value, but never lets the value go over the specified maximum.
*
* @param value The value to add the amount to
* @param amount The amount to add to the value
* @param max The maximum the value is allowed to be
* @return The new value
* @method Phaser.Math#maxAdd
* @param {number} value - The value to add the amount to.
* @param {number} amount - The amount to add to the value.
* @param {number} max- The maximum the value is allowed to be.
* @return {number}
*/
maxAdd: function (value, amount, max) {
@@ -367,12 +488,13 @@ Phaser.Math = {
},
/**
* Subtracts the given amount from the value, but never lets the value go below the specified minimum
* Subtracts the given amount from the value, but never lets the value go below the specified minimum.
*
* @param value The base value
* @param amount The amount to subtract from the base value
* @param min The minimum the value is allowed to be
* @return The new value
* @method Phaser.Math#minSub
* @param {number} value - The base value.
* @param {number} amount - The amount to subtract from the base value.
* @param {number} min - The minimum the value is allowed to be.
* @return {number} The new value.
*/
minSub: function (value, amount, min) {
@@ -391,34 +513,41 @@ Phaser.Math = {
* Ensures that the value always stays between min and max, by wrapping the value around.
* <p>max should be larger than min, or the function will return 0</p>
*
* @method Phaser.Math#wrap
* @param value The value to wrap
* @param min The minimum the value is allowed to be
* @param max The maximum the value is allowed to be
* @return The wrapped value
* @return {number} The wrapped value
*/
wrap: function (value, min, max) {
var range = max - min;
if (range <= 0)
{
return 0;
}
var result = (value - min) % range;
if (result < 0)
{
result += range;
}
return result + min;
},
/**
* Adds value to amount and ensures that the result always stays between 0 and max, by wrapping the value around.
* <p>Values must be positive integers, and are passed through Math.abs</p>
*
* @param value The value to add the amount to
* @param amount The amount to add to the value
* @param max The maximum the value is allowed to be
* @return The wrapped value
* @method Phaser.Math#wrapValue
* @param {number} value - The value to add the amount to.
* @param {number} amount - The amount to add to the value.
* @param {number} max - The maximum the value is allowed to be.
* @return {number} The wrapped value.
*/
wrapValue: function (value, amount, max) {
@@ -433,9 +562,10 @@ Phaser.Math = {
},
/**
* Randomly returns either a 1 or -1
* Randomly returns either a 1 or -1.
*
* @return 1 or -1
* @method Phaser.Math#randomSign
* @return {number} 1 or -1
*/
randomSign: function () {
return (Math.random() > 0.5) ? 1 : -1;
@@ -444,9 +574,9 @@ Phaser.Math = {
/**
* Returns true if the number given is odd.
*
* @param n The number to check
*
* @return True if the given number is odd. False if the given number is even.
* @method Phaser.Math#isOdd
* @param {number} n - The number to check.
* @return {boolean} True if the given number is odd. False if the given number is even.
*/
isOdd: function (n) {
@@ -457,9 +587,9 @@ Phaser.Math = {
/**
* Returns true if the number given is even.
*
* @param n The number to check
*
* @return True if the given number is even. False if the given number is odd.
* @method Phaser.Math#isEven
* @param {number} n - The number to check.
* @return {boolean} True if the given number is even. False if the given number is odd.
*/
isEven: function (n) {
@@ -478,7 +608,8 @@ Phaser.Math = {
* Significantly faster version of Math.max
* See http://jsperf.com/math-s-min-max-vs-homemade/5
*
* @return The highest value from those given.
* @method Phaser.Math#max
* @return {number} The highest value from those given.
*/
max: function () {
@@ -498,7 +629,8 @@ Phaser.Math = {
* Significantly faster version of Math.min
* See http://jsperf.com/math-s-min-max-vs-homemade/5
*
* @return The lowest value from those given.
* @method Phaser.Math#min
* @return {number} The lowest value from those given.
*/
min: function () {
@@ -518,9 +650,9 @@ Phaser.Math = {
* Keeps an angle value between -180 and +180<br>
* Should be called whenever the angle is updated on the Sprite to stop it from going insane.
*
* @param angle The angle value to check
*
* @return The new angle value, returns the same as the input angle if it was within bounds
* @method Phaser.Math#wrapAngle
* @param {number} angle - The angle value to check
* @return {number} The new angle value, returns the same as the input angle if it was within bounds.
*/
wrapAngle: function (angle) {
@@ -545,63 +677,86 @@ Phaser.Math = {
},
/**
* Keeps an angle value between the given min and max values
* Keeps an angle value between the given min and max values.
*
* @param angle The angle value to check. Must be between -180 and +180
* @param min The minimum angle that is allowed (must be -180 or greater)
* @param max The maximum angle that is allowed (must be 180 or less)
* @method Phaser.Math#angleLimit
* @param {number} angle - The angle value to check. Must be between -180 and +180.
* @param {number} min - The minimum angle that is allowed (must be -180 or greater).
* @param {number} max - The maximum angle that is allowed (must be 180 or less).
*
* @return The new angle value, returns the same as the input angle if it was within bounds
* @return {number} The new angle value, returns the same as the input angle if it was within bounds
*/
angleLimit: function (angle, min, max) {
var result = angle;
if (angle > max) {
if (angle > max)
{
result = max;
} else if (angle < min) {
}
else if (angle < min)
{
result = min;
}
return result;
},
/**
* @method linearInterpolation
* @param {Any} v
* @param {Any} k
* @public
* Description.
* @method Phaser.Math#linearInterpolation
* @param {number} v
* @param {number} k
* @return {number}
*/
linearInterpolation: function (v, k) {
var m = v.length - 1;
var f = m * k;
var i = Math.floor(f);
if (k < 0) {
if (k < 0)
{
return this.linear(v[0], v[1], f);
}
if (k > 1) {
if (k > 1)
{
return this.linear(v[m], v[m - 1], m - f);
}
return this.linear(v[i], v[i + 1 > m ? m : i + 1], f - i);
},
/**
* @method bezierInterpolation
* @param {Any} v
* @param {Any} k
* @public
* Description.
* @method Phaser.Math#bezierInterpolation
* @param {number} v
* @param {number} k
* @return {number}
*/
bezierInterpolation: function (v, k) {
var b = 0;
var n = v.length - 1;
for (var i = 0; i <= n; i++) {
for (var i = 0; i <= n; i++)
{
b += Math.pow(1 - k, n - i) * Math.pow(k, i) * v[i] * this.bernstein(n, i);
}
return b;
},
/**
* @method catmullRomInterpolation
* @param {Any} v
* @param {Any} k
* @public
* Description.
* @method Phaser.Math#catmullRomInterpolation
* @param {number} v
* @param {number} k
* @return {number}
*/
catmullRomInterpolation: function (v, k) {
@@ -609,57 +764,79 @@ Phaser.Math = {
var f = m * k;
var i = Math.floor(f);
if (v[0] === v[m]) {
if (k < 0) {
if (v[0] === v[m])
{
if (k < 0)
{
i = Math.floor(f = m * (1 + k));
}
return this.catmullRom(v[(i - 1 + m) % m], v[i], v[(i + 1) % m], v[(i + 2) % m], f - i);
} else {
if (k < 0) {
}
else
{
if (k < 0)
{
return v[0] - (this.catmullRom(v[0], v[0], v[1], v[1], -f) - v[0]);
}
if (k > 1) {
if (k > 1)
{
return v[m] - (this.catmullRom(v[m], v[m], v[m - 1], v[m - 1], f - m) - v[m]);
}
return this.catmullRom(v[i ? i - 1 : 0], v[i], v[m < i + 1 ? m : i + 1], v[m < i + 2 ? m : i + 2], f - i);
}
},
/**
* @method Linear
* @param {Any} p0
* @param {Any} p1
* @param {Any} t
* @public
* Description.
* @method Phaser.Math#Linear
* @param {number} p0
* @param {number} p1
* @param {number} t
* @return {number}
*/
linear: function (p0, p1, t) {
return (p1 - p0) * t + p0;
},
/**
* @method bernstein
* @param {Any} n
* @param {Any} i
* @public
* @method Phaser.Math#bernstein
* @param {number} n
* @param {number} i
* @return {number}
*/
bernstein: function (n, i) {
return this.factorial(n) / this.factorial(i) / this.factorial(n - i);
},
/**
* @method catmullRom
* @param {Any} p0
* @param {Any} p1
* @param {Any} p2
* @param {Any} p3
* @param {Any} t
* @public
* Description.
* @method Phaser.Math#catmullRom
* @param {number} p0
* @param {number} p1
* @param {number} p2
* @param {number} p3
* @param {number} t
* @return {number}
*/
catmullRom: function (p0, p1, p2, p3, t) {
var v0 = (p2 - p0) * 0.5, v1 = (p3 - p1) * 0.5, t2 = t * t, t3 = t * t2;
return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1;
},
/**
* @method Phaser.Math#difference
* @param {number} a
* @param {number} b
* @return {number}
*/
difference: function (a, b) {
return Math.abs(a - b);
},
@@ -668,11 +845,11 @@ Phaser.Math = {
* Fetch a random entry from the given array.
* Will return null if random selection is missing, or array has no entries.
*
* @param objects An array of objects.
* @param startIndex Optional offset off the front of the array. Default value is 0, or the beginning of the array.
* @param length Optional restriction on the number of values you want to randomly select from.
*
* @return The random object that was selected.
* @method Phaser.Math#getRandom
* @param {array} objects - An array of objects.
* @param {number} startIndex - Optional offset off the front of the array. Default value is 0, or the beginning of the array.
* @param {number} length - Optional restriction on the number of values you want to randomly select from.
* @return {object} The random object that was selected.
*/
getRandom: function (objects, startIndex, length) {
@@ -701,9 +878,9 @@ Phaser.Math = {
/**
* Round down to the next whole number. E.g. floor(1.7) == 1, and floor(-2.7) == -2.
*
* @param Value Any number.
*
* @return The rounded value of that number.
* @method Phaser.Math#floor
* @param {number} Value Any number.
* @return {number} The rounded value of that number.
*/
floor: function (value) {
@@ -716,9 +893,9 @@ Phaser.Math = {
/**
* Round up to the next whole number. E.g. ceil(1.3) == 2, and ceil(-2.3) == -3.
*
* @param Value Any number.
*
* @return The rounded value of that number.
* @method Phaser.Math#ceil
* @param {number} value - Any number.
* @return {number} The rounded value of that number.
*/
ceil: function (value) {
var n = value | 0;
@@ -731,13 +908,12 @@ Phaser.Math = {
* The parameters allow you to specify the length, amplitude and frequency of the wave. Once you have called this function
* you should get the results via getSinTable() and getCosTable(). This generator is fast enough to be used in real-time.
* </p>
* @param length The length of the wave
* @param sinAmplitude The amplitude to apply to the sine table (default 1.0) if you need values between say -+ 125 then give 125 as the value
* @param cosAmplitude The amplitude to apply to the cosine table (default 1.0) if you need values between say -+ 125 then give 125 as the value
* @param frequency The frequency of the sine and cosine table data
* @return Returns the sine table
* @see getSinTable
* @see getCosTable
* @method Phaser.Math#sinCosGenerator
* @param {number} length - The length of the wave
* @param {number} sinAmplitude - The amplitude to apply to the sine table (default 1.0) if you need values between say -+ 125 then give 125 as the value
* @param {number} cosAmplitude - The amplitude to apply to the cosine table (default 1.0) if you need values between say -+ 125 then give 125 as the value
* @param {number} frequency - The frequency of the sine and cosine table data
* @return {Array} Returns the sine table
*/
sinCosGenerator: function (length, sinAmplitude, cosAmplitude, frequency) {
@@ -768,9 +944,11 @@ Phaser.Math = {
/**
* Removes the top element from the stack and re-inserts it onto the bottom, then returns it.
* The original stack is modified in the process.
* This effectively moves the position of the data from the start to the end of the table.
* @return The value.
* The original stack is modified in the process. This effectively moves the position of the data from the start to the end of the table.
*
* @method Phaser.Math#shift
* @param {array} stack - The array to shift.
* @return {any} The shifted value.
*/
shift: function (stack) {
@@ -783,8 +961,9 @@ Phaser.Math = {
/**
* Shuffles the data in the given array into a new order
* @param array The array to shuffle
* @return The array
* @method Phaser.Math#shuffleArray
* @param {array} array - The array to shuffle
* @return {array} The array
*/
shuffleArray: function (array) {
@@ -802,12 +981,13 @@ Phaser.Math = {
/**
* Returns the distance between the two given set of coordinates.
* @method distance
* @param {Number} x1
* @param {Number} y1
* @param {Number} x2
* @param {Number} y2
* @return {Number} The distance between this Point object and the destination Point object.
*
* @method Phaser.Math#distance
* @param {number} x1
* @param {number} y1
* @param {number} x2
* @param {number} y2
* @return {number} The distance between this Point object and the destination Point object.
**/
distance: function (x1, y1, x2, y2) {
@@ -818,6 +998,16 @@ Phaser.Math = {
},
/**
* Returns the rounded distance between the two given set of coordinates.
*
* @method Phaser.Math#distanceRounded
* @param {number} x1
* @param {number} y1
* @param {number} x2
* @param {number} y2
* @return {number} The distance between this Point object and the destination Point object.
**/
distanceRounded: function (x1, y1, x2, y2) {
return Math.round(Phaser.Math.distance(x1, y1, x2, y2));
@@ -825,34 +1015,77 @@ Phaser.Math = {
},
/**
* force a value within the boundaries of two values
*
* Force a value within the boundaries of two values.
* Clamp value to range <a, b>
*
* @method Phaser.Math#clamp
* @param {number} x
* @param {number} a
* @param {number} b
* @return {number}
*/
clamp: function ( x, a, b ) {
return ( x < a ) ? a : ( ( x > b ) ? b : x );
},
// Clamp value to range <a, inf)
/**
* Clamp value to range <a, inf).
*
* @method Phaser.Math#clampBottom
* @param {number} x
* @param {number} a
* @return {number}
*/
clampBottom: function ( x, a ) {
return x < a ? a : x;
},
// Linear mapping from range <a1, a2> to range <b1, b2>
/**
* Checks if two values are within the given tolerance of each other.
*
* @method Phaser.Math#within
* @param {number} a - The first number to check
* @param {number} b - The second number to check
* @param {number} tolerance - The tolerance. Anything equal to or less than this is considered within the range.
* @return {boolean} True if a is <= tolerance of b.
*/
within: function ( a, b, tolerance ) {
return (Math.abs(a - b) <= tolerance);
},
/**
* Linear mapping from range <a1, a2> to range <b1, b2>
*
* @method Phaser.Math#mapLinear
* @param {number} x
* @param {number} a1
* @param {number} a1
* @param {number} a2
* @param {number} b1
* @param {number} b2
* @return {number}
*/
mapLinear: function ( x, a1, a2, b1, b2 ) {
return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 );
},
// http://en.wikipedia.org/wiki/Smoothstep
/**
* Smoothstep function as detailed at http://en.wikipedia.org/wiki/Smoothstep
*
* @method Phaser.Math#smoothstep
* @param {number} x
* @param {number} min
* @param {number} max
* @return {number}
*/
smoothstep: function ( x, min, max ) {
if ( x <= min ) return 0;
@@ -864,6 +1097,15 @@ Phaser.Math = {
},
/**
* Smootherstep function as detailed at http://en.wikipedia.org/wiki/Smoothstep
*
* @method Phaser.Math#smootherstep
* @param {number} x
* @param {number} min
* @param {number} max
* @return {number}
*/
smootherstep: function ( x, min, max ) {
if ( x <= min ) return 0;
@@ -876,8 +1118,12 @@ Phaser.Math = {
},
/**
* a value representing the sign of the value.
* A value representing the sign of the value.
* -1 for negative, +1 for positive, 0 if value is 0
*
* @method Phaser.Math#sign
* @param {number} x
* @return {number}
*/
sign: function ( x ) {
@@ -885,6 +1131,12 @@ Phaser.Math = {
},
/**
* Convert degrees to radians.
*
* @method Phaser.Math#degToRad
* @return {function}
*/
degToRad: function() {
var degreeToRadiansFactor = Math.PI / 180;
@@ -897,6 +1149,12 @@ Phaser.Math = {
}(),
/**
* Convert degrees to radians.
*
* @method Phaser.Math#radToDeg
* @return {function}
*/
radToDeg: function() {
var radianToDegreesFactor = 180 / Math.PI;
+63 -38
View File
@@ -1,4 +1,10 @@
/*
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Javascript QuadTree
* @version 1.0
* @author Timo Hausmann
@@ -12,35 +18,45 @@
* Original version at https://github.com/timohausmann/quadtree-js/
*/
/*
Copyright © 2012 Timo Hausmann
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
/**
* @copyright © 2012 Timo Hausmann
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
* QuadTree Constructor
* @param Integer maxObjects (optional) max objects a node can hold before splitting into 4 subnodes (default: 10)
* @param Integer maxLevels (optional) total max levels inside root QuadTree (default: 4)
* @param Integer level (optional) deepth level, required for subnodes
*/
/**
* QuadTree Constructor
*
* @class Phaser.QuadTree
* @classdesc A QuadTree implementation. The original code was a conversion of the Java code posted to GameDevTuts. However I've tweaked
* it massively to add node indexing, removed lots of temp. var creation and significantly increased performance as a result. Original version at https://github.com/timohausmann/quadtree-js/
* @constructor
* @param {Description} physicsManager - Description.
* @param {Description} x - Description.
* @param {Description} y - Description.
* @param {number} width - The width of your game in game pixels.
* @param {number} height - The height of your game in game pixels.
* @param {number} maxObjects - Description.
* @param {number} maxLevels - Description.
* @param {number} level - Description.
*/
Phaser.QuadTree = function (physicsManager, x, y, width, height, maxObjects, maxLevels, level) {
this.physicsManager = physicsManager;
@@ -70,8 +86,10 @@ Phaser.QuadTree = function (physicsManager, x, y, width, height, maxObjects, max
Phaser.QuadTree.prototype = {
/*
* Split the node into 4 subnodes
*/
* Split the node into 4 subnodes
*
* @method Phaser.QuadTree#split
*/
split: function() {
this.level++;
@@ -94,7 +112,9 @@ Phaser.QuadTree.prototype = {
* Insert the object into the node. If the node
* exceeds the capacity, it will split and add all
* objects to their corresponding subnodes.
* @param Object pRect bounds of the object to be added, with x, y, width, height
*
* @method Phaser.QuadTree#insert
* @param {object} body - Description.
*/
insert: function (body) {
@@ -142,9 +162,11 @@ Phaser.QuadTree.prototype = {
},
/*
* Determine which node the object belongs to
* @param Object pRect bounds of the area to be checked, with x, y, width, height
* @return Integer index of the subnode (0-3), or -1 if pRect cannot completely fit within a subnode and is part of the parent node
* Determine which node the object belongs to.
*
* @method Phaser.QuadTree#getIndex
* @param {object} rect - Description.
* @return {number} index - Index of the subnode (0-3), or -1 if rect cannot completely fit within a subnode and is part of the parent node.
*/
getIndex: function (rect) {
@@ -184,9 +206,11 @@ Phaser.QuadTree.prototype = {
},
/*
* Return all objects that could collide with the given object
* @param Object pRect bounds of the object to be checked, with x, y, width, height
* @Return Array array with all detected objects
* Return all objects that could collide with the given object.
*
* @method Phaser.QuadTree#retrieve
* @param {object} rect - Description.
* @Return {array} - Array with all detected objects.
*/
retrieve: function (sprite) {
@@ -219,7 +243,8 @@ Phaser.QuadTree.prototype = {
},
/*
* Clear the quadtree
* Clear the quadtree.
* @method Phaser.QuadTree#clear
*/
clear: function () {
+72 -59
View File
@@ -1,9 +1,19 @@
/**
* Phaser.RandomDataGenerator
*
* An extremely useful repeatable random data generator. Access it via Phaser.Game.rnd
* Based on Nonsense by Josh Faul https://github.com/jocafa/Nonsense
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Phaser.RandomDataGenerator constructor.
*
* @class Phaser.RandomDataGenerator
* @classdesc An extremely useful repeatable random data generator. Access it via Phaser.Game.rnd
* Based on Nonsense by Josh Faul https://github.com/jocafa/Nonsense.
* Random number generator from http://baagoe.org/en/wiki/Better_random_numbers_for_javascript
*
* @constructor
* @param {array} seeds
*/
Phaser.RandomDataGenerator = function (seeds) {
@@ -16,37 +26,34 @@ Phaser.RandomDataGenerator = function (seeds) {
Phaser.RandomDataGenerator.prototype = {
/**
* @property c
* @type Number
* @property {number} c
* @private
*/
c: 1,
/**
* @property s0
* @type Number
* @property {number} s0
* @private
*/
s0: 0,
/**
* @property s1
* @type Number
* @property {number} s1
* @private
*/
s1: 0,
/**
* @property s2
* @type Number
* @property {number} s2
* @private
*/
s2: 0,
/**
* Private random helper
* @method rnd
* Private random helper.
* @method Phaser.RandomDataGenerator#rnd
* @private
* @return {number} Description.
*/
rnd: function () {
@@ -61,9 +68,10 @@ Phaser.RandomDataGenerator.prototype = {
},
/**
* Reset the seed of the random data generator
* @method sow
* @param {Array} seeds
* Reset the seed of the random data generator.
*
* @method Phaser.RandomDataGenerator#sow
* @param {array} seeds
*/
sow: function (seeds) {
@@ -72,10 +80,12 @@ Phaser.RandomDataGenerator.prototype = {
this.s0 = this.hash(' ');
this.s1 = this.hash(this.s0);
this.s2 = this.hash(this.s1);
this.c = 1;
var seed;
for (var i = 0; seed = seeds[i++]; ) {
for (var i = 0; seed = seeds[i++]; )
{
this.s0 -= this.hash(seed);
this.s0 += ~~(this.s0 < 0);
this.s1 -= this.hash(seed);
@@ -87,9 +97,11 @@ Phaser.RandomDataGenerator.prototype = {
},
/**
* @method hash
* Description.
* @method Phaser.RandomDataGenerator#hash
* @param {Any} data
* @private
* @return {number} Description.
*/
hash: function (data) {
@@ -113,72 +125,69 @@ Phaser.RandomDataGenerator.prototype = {
},
/**
* Returns a random integer between 0 and 2^32
* @method integer
* @return {Number}
* Returns a random integer between 0 and 2^32.
* @method Phaser.RandomDataGenerator#integer
* @return {number}
*/
integer: function() {
return this.rnd.apply(this) * 0x100000000;// 2^32
},
/**
* Returns a random real number between 0 and 1
* @method frac
* @return {Number}
* Returns a random real number between 0 and 1.
* @method Phaser.RandomDataGenerator#frac
* @return {number}
*/
frac: function() {
return this.rnd.apply(this) + (this.rnd.apply(this) * 0x200000 | 0) * 1.1102230246251565e-16;// 2^-53
},
/**
* Returns a random real number between 0 and 2^32
* @method real
* @return {Number}
* Returns a random real number between 0 and 2^32.
* @method Phaser.RandomDataGenerator#real
* @return {number}
*/
real: function() {
return this.integer() + this.frac();
},
/**
* Returns a random integer between min and max
* @method integerInRange
* @param {Number} min
* @param {Number} max
* @return {Number}
* Returns a random integer between min and max.
* @method Phaser.RandomDataGenerator#integerInRange
* @param {number} min
* @param {number} max
* @return {number}
*/
integerInRange: function (min, max) {
return Math.floor(this.realInRange(min, max));
},
/**
* Returns a random real number between min and max
* @method realInRange
* @param {Number} min
* @param {Number} max
* @return {Number}
* Returns a random real number between min and max.
* @method Phaser.RandomDataGenerator#realInRange
* @param {number} min
* @param {number} max
* @return {number}
*/
realInRange: function (min, max) {
min = min || 0;
max = max || 0;
return this.frac() * (max - min) + min;
},
/**
* Returns a random real number between -1 and 1
* @method normal
* @return {Number}
* Returns a random real number between -1 and 1.
* @method Phaser.RandomDataGenerator#normal
* @return {number}
*/
normal: function () {
return 1 - 2 * this.frac();
},
/**
* Returns a valid RFC4122 version4 ID hex string (from https://gist.github.com/1308368)
* @method uuid
* @return {String}
* Returns a valid RFC4122 version4 ID hex string from https://gist.github.com/1308368
* @method Phaser.RandomDataGenerator#uuid
* @return {string}
*/
uuid: function () {
@@ -195,36 +204,40 @@ Phaser.RandomDataGenerator.prototype = {
},
/**
* Returns a random member of `array`
* @method pick
* @param {Any} array
* Returns a random member of `array`.
* @method Phaser.RandomDataGenerator#pick
* @param {Any} ary
* @return {number}
*/
pick: function (ary) {
return ary[this.integerInRange(0, ary.length)];
},
/**
* Returns a random member of `array`, favoring the earlier entries
* @method weightedPick
* @param {Any} array
* Returns a random member of `array`, favoring the earlier entries.
* @method Phaser.RandomDataGenerator#weightedPick
* @param {Any} ary
* @return {number}
*/
weightedPick: function (ary) {
return ary[~~(Math.pow(this.frac(), 2) * ary.length)];
},
/**
* Returns a random timestamp between min and max, or between the beginning of 2000 and the end of 2020 if min and max aren't specified
* @method timestamp
* @param {Number} min
* @param {Number} max
* Returns a random timestamp between min and max, or between the beginning of 2000 and the end of 2020 if min and max aren't specified.
* @method Phaser.RandomDataGenerator#timestamp
* @param {number} min
* @param {number} max
* @return {number}
*/
timestamp: function (a, b) {
return this.realInRange(a || 946684800000, b || 1577862000000);
},
/**
* Returns a random angle between -180 and 180
* @method angle
* Returns a random angle between -180 and 180.
* @method Phaser.RandomDataGenerator#angle
* @return {number}
*/
angle: function() {
return this.integerInRange(-180, 180);
+39 -1
View File
@@ -1,3 +1,16 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Description of Phaser.Net
*
* @class Phaser.Net
* @constructor
* @param {Phaser.Game} game - A reference to the currently running game.
*/
Phaser.Net = function (game) {
this.game = game;
@@ -8,6 +21,9 @@ Phaser.Net.prototype = {
/**
* Returns the hostname given by the browser.
*
* @method Phaser.Net#getHostName
* @return {string}
*/
getHostName: function () {
@@ -24,6 +40,10 @@ Phaser.Net.prototype = {
* If the domain name is found it returns true.
* You can specify a part of a domain, for example 'google' would match 'google.com', 'google.co.uk', etc.
* Do not include 'http://' at the start.
*
* @method Phaser.Net#checkDomainName
* @param {string} domain
* @return {boolean}
*/
checkDomainName: function (domain) {
return window.location.hostname.indexOf(domain) !== -1;
@@ -34,6 +54,13 @@ Phaser.Net.prototype = {
* If the value doesn't already exist it is set.
* If the value exists it is replaced with the new value given. If you don't provide a new value it is removed from the query string.
* Optionally you can redirect to the new url, or just return it as a string.
*
* @method Phaser.Net#updateQueryString
* @param {string} key - The querystring key to update.
* @param {string} value - The new value to be set. If it already exists it will be replaced.
* @param {boolean} redirect - If true the browser will issue a redirect to the url with the new querystring.
* @param {string} url - The URL to modify. If none is given it uses window.location.href.
* @return {string} If redirect is false then the modified url and query string is returned.
*/
updateQueryString: function (key, value, redirect, url) {
@@ -93,6 +120,10 @@ Phaser.Net.prototype = {
/**
* Returns the Query String as an object.
* If you specify a parameter it will return just the value of that parameter, should it exist.
*
* @method Phaser.Net#getQueryString
* @param {string} [parameter=''] - If specified this will return just the value for that key.
* @return {string|object} An object containing the key value pairs found in the query string or just the value if a parameter was given.
*/
getQueryString: function (parameter) {
@@ -122,9 +153,16 @@ Phaser.Net.prototype = {
},
/**
* Returns the Query String as an object.
* If you specify a parameter it will return just the value of that parameter, should it exist.
*
* @method Phaser.Net#decodeURI
* @param {string} value - The URI component to be decoded.
* @return {string} The decoded value.
*/
decodeURI: function (value) {
return decodeURIComponent(value.replace(/\+/g, " "));
}
};
+38 -3
View File
@@ -1,15 +1,40 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Phaser.Particles is the Particle Manager for the game. It is called during the game update loop and in turn updates any Emitters attached to it.
*
* @class Phaser.Particles
* @classdesc Phaser Particles
* @constructor
* @param {Phaser.Game} game - A reference to the currently running game.
*/
Phaser.Particles = function (game) {
/**
* @property {Description} emitters - Description.
*/
this.emitters = {};
/**
* @property {number} ID - Description.
* @default
*/
this.ID = 0;
};
Phaser.Particles.prototype = {
emitters: null,
/**
* Adds a new Particle Emitter to the Particle Manager.
* @method Phaser.Particles#add
* @param {Phaser.Emitter} emitter - Description.
* @return {Phaser.Emitter} The emitter that was added.
*/
add: function (emitter) {
this.emitters[emitter.name] = emitter;
@@ -18,12 +43,22 @@ Phaser.Particles.prototype = {
},
/**
* Removes an existing Particle Emitter from the Particle Manager.
* @method Phaser.Particles#remove
* @param {Phaser.Emitter} emitter - The emitter to remove.
*/
remove: function (emitter) {
delete this.emitters[emitter.name];
},
/**
* Called by the core game loop. Updates all Emitters who have their exists value set to true.
* @method Phaser.Particles#update
* @protected
*/
update: function () {
for (var key in this.emitters)
@@ -34,6 +69,6 @@ Phaser.Particles.prototype = {
}
}
},
}
};
+174 -75
View File
@@ -1,145 +1,205 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Phaser - ArcadeEmitter
*
* Emitter is a lightweight particle emitter. It can be used for one-time explosions or for
* @class Phaser.Particles.Arcade.Emitter
* @classdesc Emitter is a lightweight particle emitter. It can be used for one-time explosions or for
* continuous effects like rain and fire. All it really does is launch Particle objects out
* at set intervals, and fixes their positions and velocities accorindgly.
* @constructor
* @extends Phaser.Group
* @param {Phaser.Game} game - Current game instance.
* @param {number} [x=0] - The x coordinate within the Emitter that the particles are emitted from.
* @param {number} [y=0] - The y coordinate within the Emitter that the particles are emitted from.
* @param {number} [maxParticles=50] - The total number of particles in this emitter..
*/
Phaser.Particles.Arcade.Emitter = function (game, x, y, maxParticles) {
maxParticles = maxParticles || 50;
/**
* The total number of particles in this emitter.
* @property {number} maxParticles - The total number of particles in this emitter..
* @default
*/
this.maxParticles = maxParticles || 50;
Phaser.Group.call(this, game);
/**
* @property {string} name - Description.
*/
this.name = 'emitter' + this.game.particles.ID++;
/**
* @property {Description} type - Description.
*/
this.type = Phaser.EMITTER;
/**
* The X position of the top left corner of the emitter in world space.
* @property {number} x - The X position of the top left corner of the emitter in world space.
* @default
*/
this.x = 0;
/**
* The Y position of the top left corner of emitter in world space.
* @property {number} y - The Y position of the top left corner of emitter in world space.
* @default
*/
this.y = 0;
/**
* The width of the emitter. Particles can be randomly generated from anywhere within this box.
* @property {number} width - The width of the emitter. Particles can be randomly generated from anywhere within this box.
* @default
*/
this.width = 1;
/**
* The height of the emitter. Particles can be randomly generated from anywhere within this box.
*/
* @property {number} height - The height of the emitter. Particles can be randomly generated from anywhere within this box.
* @default
*/
this.height = 1;
/**
* The minimum possible velocity of a particle.
* The default value is (-100,-100).
*/
* The minimum possible velocity of a particle.
* The default value is (-100,-100).
* @property {Phaser.Point} minParticleSpeed
*/
this.minParticleSpeed = new Phaser.Point(-100, -100);
/**
* The maximum possible velocity of a particle.
* The default value is (100,100).
*/
* The maximum possible velocity of a particle.
* The default value is (100,100).
* @property {Phaser.Point} maxParticleSpeed
*/
this.maxParticleSpeed = new Phaser.Point(100, 100);
/**
* The minimum possible scale of a particle.
* The default value is 1.
*/
* The minimum possible scale of a particle.
* The default value is 1.
* @property {number} minParticleScale
* @default
*/
this.minParticleScale = 1;
/**
* The maximum possible scale of a particle.
* The default value is 1.
* @property {number} maxParticleScale
* @default
*/
this.maxParticleScale = 1;
/**
* The minimum possible angular velocity of a particle. The default value is -360.
* @property {number} minRotation
* @default
*/
this.minRotation = -360;
/**
* The maximum possible angular velocity of a particle. The default value is 360.
* @property {number} maxRotation
* @default
*/
this.maxRotation = 360;
/**
* Sets the <code>gravity.y</code> of each particle to this value on launch.
* @property {number} gravity
* @default
*/
this.gravity = 2;
/**
* Set your own particle class type here.
* Default is <code>Particle</code>.
* @property {Description} particleClass
* @default
*/
this.particleClass = null;
/**
* The X and Y drag component of particles launched from the emitter.
* @property {Phaser.Point} particleDrag
*/
this.particleDrag = new Phaser.Point();
/**
* The angular drag component of particles launched from the emitter if they are rotating.
* @property {number} angularDrag
* @default
*/
this.angularDrag = 0;
/**
* How often a particle is emitted in ms (if emitter is started with Explode == false).
* @property {boolean} frequency
* @default
*/
this.frequency = 100;
/**
* The total number of particles in this emitter.
*/
this.maxParticles = maxParticles;
/**
* How long each particle lives once it is emitted in ms. Default is 2 seconds.
* Set lifespan to 'zero' for particles to live forever.
* @property {number} lifespan
* @default
*/
this.lifespan = 2000;
/**
* How much each particle should bounce on each axis. 1 = full bounce, 0 = no bounce.
* @property {Phaser.Point} bounce
*/
this.bounce = new Phaser.Point();
/**
* Internal helper for deciding how many particles to launch.
* @property {number} _quantity
* @private
* @default
*/
this._quantity = 0;
/**
* Internal helper for deciding when to launch particles or kill them.
* @property {number} _timer
* @private
* @default
*/
this._timer = 0;
/**
* Internal counter for figuring out how many particles to launch.
* @property {number} _counter
* @private
* @default
*/
this._counter = 0;
/**
* Internal helper for the style of particle emission (all at once, or one at a time).
* @property {boolean} _explode
* @private
* @default
*/
this._explode = true;
/**
* Determines whether the emitter is currently emitting particles.
* It is totally safe to directly toggle this.
* @property {boolean} on
* @default
*/
this.on = false;
/**
* Determines whether the emitter is being updated by the core game loop.
* @property {boolean} exists
* @default
*/
this.exists = true;
@@ -147,8 +207,16 @@ Phaser.Particles.Arcade.Emitter = function (game, x, y, maxParticles) {
* The point the particles are emitted from.
* Emitter.x and Emitter.y control the containers location, which updates all current particles
* Emitter.emitX and Emitter.emitY control the emission location relative to the x/y position.
* @property {boolean} emitX
*/
this.emitX = x;
/**
* The point the particles are emitted from.
* Emitter.x and Emitter.y control the containers location, which updates all current particles
* Emitter.emitX and Emitter.emitY control the emission location relative to the x/y position.
* @property {boolean} emitY
*/
this.emitY = y;
};
@@ -157,8 +225,9 @@ Phaser.Particles.Arcade.Emitter.prototype = Object.create(Phaser.Group.prototype
Phaser.Particles.Arcade.Emitter.prototype.constructor = Phaser.Particles.Arcade.Emitter;
/**
* Called automatically by the game loop, decides when to launch particles and when to "die".
*/
* Called automatically by the game loop, decides when to launch particles and when to "die".
* @method Phaser.Particles.Arcade.Emitter#update
*/
Phaser.Particles.Arcade.Emitter.prototype.update = function () {
if (this.on)
@@ -200,15 +269,16 @@ Phaser.Particles.Arcade.Emitter.prototype.update = function () {
}
/**
* This function generates a new array of particle sprites to attach to the emitter.
*
* @param graphics If you opted to not pre-configure an array of Sprite objects, you can simply pass in a particle image or sprite sheet.
* @param quantity {number} The number of particles to generate when using the "create from image" option.
* @param multiple {boolean} Whether the image in the Graphics param is a single particle or a bunch of particles (if it's a bunch, they need to be square!).
* @param collide {number} Whether the particles should be flagged as not 'dead' (non-colliding particles are higher performance). 0 means no collisions, 0-1 controls scale of particle's bounding box.
*
* @return This Emitter instance (nice for chaining stuff together, if you're into that).
*/
* This function generates a new array of particle sprites to attach to the emitter.
*
* @method Phaser.Particles.Arcade.Emitter#makeParticles
* @param {Description} keys - Description.
* @param {number} frames - Description.
* @param {number} quantity - The number of particles to generate when using the "create from image" option.
* @param {number} collide - Description.
* @param {boolean} collideWorldBounds - Description.
* @return This Emitter instance (nice for chaining stuff together, if you're into that).
*/
Phaser.Particles.Arcade.Emitter.prototype.makeParticles = function (keys, frames, quantity, collide, collideWorldBounds) {
if (typeof frames == 'undefined')
@@ -278,6 +348,7 @@ Phaser.Particles.Arcade.Emitter.prototype.makeParticles = function (keys, frames
/**
* Call this function to turn off all the particles and the emitter.
* @method Phaser.Particles.Arcade.Emitter#kill
*/
Phaser.Particles.Arcade.Emitter.prototype.kill = function () {
@@ -290,6 +361,7 @@ Phaser.Particles.Arcade.Emitter.prototype.kill = function () {
/**
* Handy for bringing game objects "back to life". Just sets alive and exists back to true.
* In practice, this is most often called by <code>Object.reset()</code>.
* @method Phaser.Particles.Arcade.Emitter#revive
*/
Phaser.Particles.Arcade.Emitter.prototype.revive = function () {
@@ -300,11 +372,11 @@ Phaser.Particles.Arcade.Emitter.prototype.revive = function () {
/**
* Call this function to start emitting particles.
*
* @param explode {boolean} Whether the particles should all burst out at once.
* @param lifespan {number} How long each particle lives once emitted. 0 = forever.
* @param frequency {number} Ignored if Explode is set to true. Frequency is how often to emit a particle in ms.
* @param quantity {number} How many particles to launch. 0 = "all of the particles".
* @method Phaser.Particles.Arcade.Emitter#start
* @param {boolean} explode - Whether the particles should all burst out at once.
* @param {number} lifespan - How long each particle lives once emitted. 0 = forever.
* @param {number} frequency - Ignored if Explode is set to true. Frequency is how often to emit a particle in ms.
* @param {number} quantity - How many particles to launch. 0 = "all of the particles".
*/
Phaser.Particles.Arcade.Emitter.prototype.start = function (explode, lifespan, frequency, quantity) {
@@ -346,6 +418,7 @@ Phaser.Particles.Arcade.Emitter.prototype.start = function (explode, lifespan, f
/**
* This function can be used both internally and externally to emit the next particle.
* @method Phaser.Particles.Arcade.Emitter#emitParticle
*/
Phaser.Particles.Arcade.Emitter.prototype.emitParticle = function () {
@@ -358,7 +431,7 @@ Phaser.Particles.Arcade.Emitter.prototype.emitParticle = function () {
if (this.width > 1 || this.height > 1)
{
particle.reset(this.emiteX - this.game.rnd.integerInRange(this.left, this.right), this.emiteY - this.game.rnd.integerInRange(this.top, this.bottom));
particle.reset(this.game.rnd.integerInRange(this.left, this.right), this.game.rnd.integerInRange(this.top, this.bottom));
}
else
{
@@ -411,11 +484,11 @@ Phaser.Particles.Arcade.Emitter.prototype.emitParticle = function () {
}
/**
* A more compact way of setting the width and height of the emitter.
*
* @param width {number} The desired width of the emitter (particles are spawned randomly within these dimensions).
* @param height {number} The desired height of the emitter.
*/
* A more compact way of setting the width and height of the emitter.
* @method Phaser.Particles.Arcade.Emitter#setSize
* @param {number} width - The desired width of the emitter (particles are spawned randomly within these dimensions).
* @param {number} height - The desired height of the emitter.
*/
Phaser.Particles.Arcade.Emitter.prototype.setSize = function (width, height) {
this.width = width;
@@ -424,11 +497,11 @@ Phaser.Particles.Arcade.Emitter.prototype.setSize = function (width, height) {
}
/**
* A more compact way of setting the X velocity range of the emitter.
*
* @param Min {number} The minimum value for this range.
* @param Max {number} The maximum value for this range.
*/
* A more compact way of setting the X velocity range of the emitter.
* @method Phaser.Particles.Arcade.Emitter#setXSpeed
* @param {number} min - The minimum value for this range.
* @param {number} max - The maximum value for this range.
*/
Phaser.Particles.Arcade.Emitter.prototype.setXSpeed = function (min, max) {
min = min || 0;
@@ -440,11 +513,11 @@ Phaser.Particles.Arcade.Emitter.prototype.setXSpeed = function (min, max) {
}
/**
* A more compact way of setting the Y velocity range of the emitter.
*
* @param Min {number} The minimum value for this range.
* @param Max {number} The maximum value for this range.
*/
* A more compact way of setting the Y velocity range of the emitter.
* @method Phaser.Particles.Arcade.Emitter#setYSpeed
* @param {number} min - The minimum value for this range.
* @param {number} max - The maximum value for this range.
*/
Phaser.Particles.Arcade.Emitter.prototype.setYSpeed = function (min, max) {
min = min || 0;
@@ -456,11 +529,11 @@ Phaser.Particles.Arcade.Emitter.prototype.setYSpeed = function (min, max) {
}
/**
* A more compact way of setting the angular velocity constraints of the emitter.
*
* @param Min {number} The minimum value for this range.
* @param Max {number} The maximum value for this range.
*/
* A more compact way of setting the angular velocity constraints of the emitter.
* @method Phaser.Particles.Arcade.Emitter#setRotation
* @param {number} min - The minimum value for this range.
* @param {number} max - The maximum value for this range.
*/
Phaser.Particles.Arcade.Emitter.prototype.setRotation = function (min, max) {
min = min || 0;
@@ -472,10 +545,10 @@ Phaser.Particles.Arcade.Emitter.prototype.setRotation = function (min, max) {
}
/**
* Change the emitter's midpoint to match the midpoint of a <code>Object</code>.
*
* @param Object {object} The <code>Object</code> that you want to sync up with.
*/
* Change the emitter's midpoint to match the midpoint of a <code>Object</code>.
* @method Phaser.Particles.Arcade.Emitter#at
* @param {object} object - The <code>Object</code> that you want to sync up with.
*/
Phaser.Particles.Arcade.Emitter.prototype.at = function (object) {
this.emitX = object.center.x;
@@ -483,42 +556,44 @@ Phaser.Particles.Arcade.Emitter.prototype.at = function (object) {
}
/**
* The emitters alpha value.
* @name Phaser.Particles.Arcade.Emitter#alpha
* @property {number} alpha - Gets or sets the alpha value of the Emitter.
*/
Object.defineProperty(Phaser.Particles.Arcade.Emitter.prototype, "alpha", {
/**
* Get the emitter alpha.
*/
get: function () {
return this._container.alpha;
},
/**
* Set the emiter alpha value.
*/
set: function (value) {
this._container.alpha = value;
}
});
/**
* The emitter visible state.
* @name Phaser.Particles.Arcade.Emitter#visible
* @property {boolean} visible - Gets or sets the Emitter visible state.
*/
Object.defineProperty(Phaser.Particles.Arcade.Emitter.prototype, "visible", {
/**
* Get the emitter visible state.
*/
get: function () {
return this._container.visible;
},
/**
* Set the emitter visible state.
*/
set: function (value) {
this._container.visible = value;
}
});
/**
* @name Phaser.Particles.Arcade.Emitter#x
* @property {number} x - Gets or sets the x position of the Emitter.
*/
Object.defineProperty(Phaser.Particles.Arcade.Emitter.prototype, "x", {
get: function () {
@@ -531,6 +606,10 @@ Object.defineProperty(Phaser.Particles.Arcade.Emitter.prototype, "x", {
});
/**
* @name Phaser.Particles.Arcade.Emitter#y
* @property {number} y - Gets or sets the y position of the Emitter.
*/
Object.defineProperty(Phaser.Particles.Arcade.Emitter.prototype, "y", {
get: function () {
@@ -543,6 +622,11 @@ Object.defineProperty(Phaser.Particles.Arcade.Emitter.prototype, "y", {
});
/**
* @name Phaser.Particles.Arcade.Emitter#left
* @property {number} left - Gets the left position of the Emitter.
* @readonly
*/
Object.defineProperty(Phaser.Particles.Arcade.Emitter.prototype, "left", {
get: function () {
@@ -551,6 +635,11 @@ Object.defineProperty(Phaser.Particles.Arcade.Emitter.prototype, "left", {
});
/**
* @name Phaser.Particles.Arcade.Emitter#right
* @property {number} right - Gets the right position of the Emitter.
* @readonly
*/
Object.defineProperty(Phaser.Particles.Arcade.Emitter.prototype, "right", {
get: function () {
@@ -559,6 +648,11 @@ Object.defineProperty(Phaser.Particles.Arcade.Emitter.prototype, "right", {
});
/**
* @name Phaser.Particles.Arcade.Emitter#top
* @property {number} top - Gets the top position of the Emitter.
* @readonly
*/
Object.defineProperty(Phaser.Particles.Arcade.Emitter.prototype, "top", {
get: function () {
@@ -567,6 +661,11 @@ Object.defineProperty(Phaser.Particles.Arcade.Emitter.prototype, "top", {
});
/**
* @name Phaser.Particles.Arcade.Emitter#bottom
* @property {number} bottom - Gets the bottom position of the Emitter.
* @readonly
*/
Object.defineProperty(Phaser.Particles.Arcade.Emitter.prototype, "bottom", {
get: function () {
-402
View File
@@ -1,402 +0,0 @@
/*
* Copyright (c) 2012 Ju Hyung Lee
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
Body = function(type, pos, angle) {
if (Body.id_counter == undefined) {
Body.id_counter = 0;
}
this.id = Body.id_counter++;
// Identifier
this.name = "body" + this.id;
// STATIC or DYNAMIC
this.type = type;
// Default values
pos = pos || new vec2(0, 0);
angle = angle || 0;
// Local to world transform
this.xf = new Transform(pos, angle);
// Local center of mass
this.centroid = new vec2(0, 0);
// World position of centroid
this.p = new vec2(pos.x, pos.y);
// Velocity
this.v = new vec2(0, 0);
// Force
this.f = new vec2(0, 0);
// Orientation (angle)
this.a = angle;
// Angular velocity
this.w = 0;
// Torque
this.t = 0;
// Linear damping
this.linearDamping = 0;
// Angular damping
this.angularDamping = 0;
// Sleep time
this.sleepTime = 0;
// Awaked flag
this.awaked = false;
// Shape list for this body
this.shapeArr = [];
// Joint hash for this body
this.jointArr = [];
this.jointHash = {};
// Bounds of all shapes
this.bounds = new Bounds;
this.fixedRotation = false;
this.categoryBits = 0x0001;
this.maskBits = 0xFFFF;
this.stepCount = 0;
}
Body.STATIC = 0;
Body.KINETIC = 1;
Body.DYNAMIC = 2;
Body.prototype.duplicate = function() {
var body = new Body(this.type, this.xf.t, this.a);
for (var i = 0; i < this.shapeArr.length; i++) {
body.addShape(this.shapeArr[i].duplicate());
}
body.resetMassData();
return body;
}
Body.prototype.serialize = function() {
var shapes = [];
for (var i = 0; i < this.shapeArr.length; i++) {
var obj = this.shapeArr[i].serialize();
shapes.push(obj);
}
return {
"type": ["static", "kinetic", "dynamic"][this.type],
"name": this.name,
"position": this.xf.t,
"angle": this.xf.a,
"shapes": shapes
};
}
Body.prototype.toString = function() {
return "[{Body (name=" + this.name + " velocity=" + this.v.toString() + " angularVelocity: " + this.w + ")}]";
}
Body.prototype.isStatic = function() {
return this.type == Body.STATIC ? true : false;
}
Body.prototype.isDynamic = function() {
return this.type == Body.DYNAMIC ? true : false;
}
Body.prototype.isKinetic = function() {
return this.type == Body.KINETIC ? true : false;
}
Body.prototype.setType = function(type) {
if (type == this.type) {
return;
}
this.f.set(0, 0);
this.v.set(0, 0);
this.t = 0;
this.w = 0;
this.type = type;
this.awake(true);
}
Body.prototype.addShape = function(shape) {
shape.body = this;
this.shapeArr.push(shape);
}
Body.prototype.removeShape = function(shape) {
var index = this.shapeArr.indexOf(shape);
if (index != -1) {
this.shapeArr.splice(index, 1);
shape.body = undefined;
}
}
// Internal function
Body.prototype.setMass = function(mass) {
this.m = mass;
this.m_inv = mass > 0 ? 1 / mass : 0;
}
// Internal function
Body.prototype.setInertia = function(inertia) {
this.i = inertia;
this.i_inv = inertia > 0 ? 1 / inertia : 0;
}
Body.prototype.setTransform = function(pos, angle) {
this.xf.set(pos, angle);
this.p = this.xf.transform(this.centroid);
this.a = angle;
}
Body.prototype.syncTransform = function() {
this.xf.setRotation(this.a);
this.xf.setPosition(vec2.sub(this.p, this.xf.rotate(this.centroid)));
}
Body.prototype.getWorldPoint = function(p) {
return this.xf.transform(p);
}
Body.prototype.getWorldVector = function(v) {
return this.xf.rotate(v);
}
Body.prototype.getLocalPoint = function(p) {
return this.xf.untransform(p);
}
Body.prototype.getLocalVector = function(v) {
return this.xf.unrotate(v);
}
Body.prototype.setFixedRotation = function(flag) {
this.fixedRotation = flag;
this.resetMassData();
}
Body.prototype.resetMassData = function() {
this.centroid.set(0, 0);
this.m = 0;
this.m_inv = 0;
this.i = 0;
this.i_inv = 0;
if (!this.isDynamic()) {
this.p = this.xf.transform(this.centroid);
return;
}
var totalMassCentroid = new vec2(0, 0);
var totalMass = 0;
var totalInertia = 0;
for (var i = 0; i < this.shapeArr.length; i++) {
var shape = this.shapeArr[i];
var centroid = shape.centroid();
var mass = shape.area() * shape.density;
var inertia = shape.inertia(mass);
totalMassCentroid.mad(centroid, mass);
totalMass += mass;
totalInertia += inertia;
}
this.centroid.copy(vec2.scale(totalMassCentroid, 1 / totalMass));
this.setMass(totalMass);
if (!this.fixedRotation) {
this.setInertia(totalInertia - totalMass * vec2.dot(this.centroid, this.centroid));
}
// Move center of mass
var old_p = this.p;
this.p = this.xf.transform(this.centroid);
// Update center of mass velocity ??
this.v.mad(vec2.perp(vec2.sub(this.p, old_p)), this.w);
}
Body.prototype.resetJointAnchors = function() {
for (var i = 0; i < this.jointArr.length; i++) {
var joint = this.jointArr[i];
if (!joint) {
continue;
}
var anchor1 = joint.getWorldAnchor1();
var anchor2 = joint.getWorldAnchor2();
joint.setWorldAnchor1(anchor1);
joint.setWorldAnchor2(anchor2);
}
}
Body.prototype.cacheData = function() {
this.bounds.clear();
for (var i = 0; i < this.shapeArr.length; i++) {
var shape = this.shapeArr[i];
shape.cacheData(this.xf);
this.bounds.addBounds(shape.bounds);
}
}
Body.prototype.updateVelocity = function(gravity, dt, damping) {
this.v = vec2.mad(this.v, vec2.mad(gravity, this.f, this.m_inv), dt);
this.w = this.w + this.t * this.i_inv * dt;
// Apply damping.
// ODE: dv/dt + c * v = 0
// Solution: v(t) = v0 * exp(-c * t)
// Time step: v(t + dt) = v0 * exp(-c * (t + dt)) = v0 * exp(-c * t) * exp(-c * dt) = v * exp(-c * dt)
// v2 = exp(-c * dt) * v1
// Taylor expansion:
// v2 = (1.0f - c * dt) * v1
this.v.scale(Math.clamp(1 - dt * (damping + this.linearDamping), 0, 1));
this.w *= Math.clamp(1 - dt * (damping + this.angularDamping), 0, 1);
this.f.set(0, 0);
this.t = 0;
}
Body.prototype.updatePosition = function(dt) {
this.p.addself(vec2.scale(this.v, dt));
this.a += this.w * dt;
}
Body.prototype.resetForce = function() {
this.f.set(0, 0);
this.t = 0;
}
Body.prototype.applyForce = function(force, p) {
if (!this.isDynamic())
return;
if (!this.isAwake())
this.awake(true);
this.f.addself(force);
this.t += vec2.cross(vec2.sub(p, this.p), force);
}
Body.prototype.applyForceToCenter = function(force) {
if (!this.isDynamic())
return;
if (!this.isAwake())
this.awake(true);
this.f.addself(force);
}
Body.prototype.applyTorque = function(torque) {
if (!this.isDynamic())
return;
if (!this.isAwake())
this.awake(true);
this.t += torque;
}
Body.prototype.applyLinearImpulse = function(impulse, p) {
if (!this.isDynamic())
return;
if (!this.isAwake())
this.awake(true);
this.v.mad(impulse, this.m_inv);
this.w += vec2.cross(vec2.sub(p, this.p), impulse) * this.i_inv;
}
Body.prototype.applyAngularImpulse = function(impulse) {
if (!this.isDynamic())
return;
if (!this.isAwake())
this.awake(true);
this.w += impulse * this.i_inv;
}
Body.prototype.kineticEnergy = function() {
var vsq = this.v.dot(this.v);
var wsq = this.w * this.w;
return 0.5 * (this.m * vsq + this.i * wsq);
}
Body.prototype.isAwake = function() {
return this.awaked;
}
Body.prototype.awake = function(flag) {
this.awaked = flag;
if (flag) {
this.sleepTime = 0;
}
else {
this.v.set(0, 0);
this.w = 0;
this.f.set(0, 0);
this.t = 0;
}
}
Body.prototype.isCollidable = function(other) {
if (this == other)
return false;
if (!this.isDynamic() && !other.isDynamic())
return false;
if (!(this.maskBits & other.categoryBits) || !(other.maskBits & this.categoryBits))
return false;
for (var i = 0; i < this.jointArr.length; i++) {
var joint = this.jointArr[i];
if (!joint) {
continue;
}
if (!joint.collideConnected && other.jointHash[joint.id] != undefined) {
return false;
}
}
return true;
}
-382
View File
@@ -1,382 +0,0 @@
/*
* Copyright (c) 2012 Ju Hyung Lee
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var collision = {};
(function() {
var colFuncs = [];
function addCollideFunc(a, b, func) {
colFuncs[a * Shape.NUM_TYPES + b] = func;
}
function _circle2Circle(c1, r1, c2, r2, contactArr) {
var rmax = r1 + r2;
var t = vec2.sub(c2, c1);
var distsq = t.lengthsq();
if (distsq > rmax * rmax) {
return 0;
}
var dist = Math.sqrt(distsq);
var p = vec2.mad(c1, t, 0.5 + (r1 - r2) * 0.5 / dist);
var n = (dist != 0) ? vec2.scale(t, 1 / dist) : vec2.zero;
var d = dist - rmax;
contactArr.push(new Contact(p, n, d, 0));
return 1;
}
function circle2Circle(circ1, circ2, contactArr) {
return _circle2Circle(circ1.tc, circ1.r, circ2.tc, circ2.r, contactArr);
}
function circle2Segment(circ, seg, contactArr) {
var rsum = circ.r + seg.r;
// Normal distance from segment
var dn = vec2.dot(circ.tc, seg.tn) - vec2.dot(seg.ta, seg.tn);
var dist = (dn < 0 ? dn * -1 : dn) - rsum;
if (dist > 0) {
return 0;
}
// Tangential distance along segment
var dt = vec2.cross(circ.tc, seg.tn);
var dtMin = vec2.cross(seg.ta, seg.tn);
var dtMax = vec2.cross(seg.tb, seg.tn);
if (dt < dtMin) {
if (dt < dtMin - rsum) {
return 0;
}
return _circle2Circle(circ.tc, circ.r, seg.ta, seg.r, contactArr);
}
else if (dt > dtMax) {
if (dt > dtMax + rsum) {
return 0;
}
return _circle2Circle(circ.tc, circ.r, seg.tb, seg.r, contactArr);
}
var n = (dn > 0) ? seg.tn : vec2.neg(seg.tn);
contactArr.push(new Contact(vec2.mad(circ.tc, n, -(circ.r + dist * 0.5)), vec2.neg(n), dist, 0));
return 1;
}
function circle2Poly(circ, poly, contactArr) {
var minDist = -999999;
var minIdx = -1;
for (var i = 0; i < poly.verts.length; i++) {
var plane = poly.tplanes[i];
var dist = vec2.dot(circ.tc, plane.n) - plane.d - circ.r;
if (dist > 0) {
return 0;
}
else if (dist > minDist) {
minDist = dist;
minIdx = i;
}
}
var n = poly.tplanes[minIdx].n;
var a = poly.tverts[minIdx];
var b = poly.tverts[(minIdx + 1) % poly.verts.length];
var dta = vec2.cross(a, n);
var dtb = vec2.cross(b, n);
var dt = vec2.cross(circ.tc, n);
if (dt > dta) {
return _circle2Circle(circ.tc, circ.r, a, 0, contactArr);
}
else if (dt < dtb) {
return _circle2Circle(circ.tc, circ.r, b, 0, contactArr);
}
contactArr.push(new Contact(vec2.mad(circ.tc, n, -(circ.r + minDist * 0.5)), vec2.neg(n), minDist, 0));
return 1;
}
function segmentPointDistanceSq(seg, p) {
var w = vec2.sub(p, seg.ta);
var d = vec2.sub(seg.tb, seg.ta);
var proj = w.dot(d);
if (proj <= 0) {
return w.dot(w);
}
var vsq = d.dot(d)
if (proj >= vsq) {
return w.dot(w) - 2 * proj + vsq;
}
return w.dot(w) - proj * proj / vsq;
}
// FIXME !!
function segment2Segment(seg1, seg2, contactArr) {
var d = [];
d[0] = segmentPointDistanceSq(seg1, seg2.ta);
d[1] = segmentPointDistanceSq(seg1, seg2.tb);
d[2] = segmentPointDistanceSq(seg2, seg1.ta);
d[3] = segmentPointDistanceSq(seg2, seg1.tb);
var idx1 = d[0] < d[1] ? 0 : 1;
var idx2 = d[2] < d[3] ? 2 : 3;
var idxm = d[idx1] < d[idx2] ? idx1 : idx2;
var s, t;
var u = vec2.sub(seg1.tb, seg1.ta);
var v = vec2.sub(seg2.tb, seg2.ta);
switch (idxm) {
case 0:
s = vec2.dot(vec2.sub(seg2.ta, seg1.ta), u) / vec2.dot(u, u);
s = s < 0 ? 0 : (s > 1 ? 1 : s);
t = 0;
break;
case 1:
s = vec2.dot(vec2.sub(seg2.tb, seg1.ta), u) / vec2.dot(u, u);
s = s < 0 ? 0 : (s > 1 ? 1 : s);
t = 1;
break;
case 2:
s = 0;
t = vec2.dot(vec2.sub(seg1.ta, seg2.ta), v) / vec2.dot(v, v);
t = t < 0 ? 0 : (t > 1 ? 1 : t);
break;
case 3:
s = 1;
t = vec2.dot(vec2.sub(seg1.tb, seg2.ta), v) / vec2.dot(v, v);
t = t < 0 ? 0 : (t > 1 ? 1 : t);
break;
}
var minp1 = vec2.mad(seg1.ta, u, s);
var minp2 = vec2.mad(seg2.ta, v, t);
return _circle2Circle(minp1, seg1.r, minp2, seg2.r, contactArr);
}
// Identify vertexes that have penetrated the segment.
function findPointsBehindSeg(contactArr, seg, poly, dist, coef) {
var dta = vec2.cross(seg.tn, seg.ta);
var dtb = vec2.cross(seg.tn, seg.tb);
var n = vec2.scale(seg.tn, coef);
for (var i = 0; i < poly.verts.length; i++) {
var v = poly.tverts[i];
if (vec2.dot(v, n) < vec2.dot(seg.tn, seg.ta) * coef + seg.r) {
var dt = vec2.cross(seg.tn, v);
if (dta >= dt && dt >= dtb) {
contactArr.push(new Contact(v, n, dist, (poly.id << 16) | i));
}
}
}
}
function segment2Poly(seg, poly, contactArr) {
var seg_td = vec2.dot(seg.tn, seg.ta);
var seg_d1 = poly.distanceOnPlane(seg.tn, seg_td) - seg.r;
if (seg_d1 > 0) {
return 0;
}
var seg_d2 = poly.distanceOnPlane(vec2.neg(seg.tn), -seg_td) - seg.r;
if (seg_d2 > 0) {
return 0;
}
var poly_d = -999999;
var poly_i = -1;
for (var i = 0; i < poly.verts.length; i++) {
var plane = poly.tplanes[i];
var dist = seg.distanceOnPlane(plane.n, plane.d);
if (dist > 0) {
return 0;
}
if (dist > poly_d) {
poly_d = dist;
poly_i = i;
}
}
var poly_n = vec2.neg(poly.tplanes[poly_i].n);
var va = vec2.mad(seg.ta, poly_n, seg.r);
var vb = vec2.mad(seg.tb, poly_n, seg.r);
if (poly.containPoint(va)) {
contactArr.push(new Contact(va, poly_n, poly_d, (seg.id << 16) | 0));
}
if (poly.containPoint(vb)) {
contactArr.push(new Contact(vb, poly_n, poly_d, (seg.id << 16) | 1));
}
// Floating point precision problems here.
// This will have to do for now.
poly_d -= 0.1
if (seg_d1 >= poly_d || seg_d2 >= poly_d) {
if (seg_d1 > seg_d2) {
findPointsBehindSeg(contactArr, seg, poly, seg_d1, 1);
}
else {
findPointsBehindSeg(contactArr, seg, poly, seg_d2, -1);
}
}
// If no other collision points are found, try colliding endpoints.
if (contactArr.length == 0) {
var poly_a = poly.tverts[poly_i];
var poly_b = poly.tverts[(poly_i + 1) % poly.verts.length];
if (_circle2Circle(seg.ta, seg.r, poly_a, 0, contactArr))
return 1;
if (_circle2Circle(seg.tb, seg.r, poly_a, 0, contactArr))
return 1;
if (_circle2Circle(seg.ta, seg.r, poly_b, 0, contactArr))
return 1;
if (_circle2Circle(seg.tb, seg.r, poly_b, 0, contactArr))
return 1;
}
return contactArr.length;
}
// Find the minimum separating axis for the given poly and plane list.
function findMSA(poly, planes, num) {
var min_dist = -999999;
var min_index = -1;
for (var i = 0; i < num; i++) {
var dist = poly.distanceOnPlane(planes[i].n, planes[i].d);
if (dist > 0) { // no collision
return { dist: 0, index: -1 };
}
else if (dist > min_dist) {
min_dist = dist;
min_index = i;
}
}
return { dist: min_dist, index: min_index };
}
function findVertsFallback(contactArr, poly1, poly2, n, dist) {
var num = 0;
for (var i = 0; i < poly1.verts.length; i++) {
var v = poly1.tverts[i];
if (poly2.containPointPartial(v, n)) {
contactArr.push(new Contact(v, n, dist, (poly1.id << 16) | i));
num++;
}
}
for (var i = 0; i < poly2.verts.length; i++) {
var v = poly2.tverts[i];
if (poly1.containPointPartial(v, n)) {
contactArr.push(new Contact(v, n, dist, (poly2.id << 16) | i));
num++;
}
}
return num;
}
// Find the overlapped vertices.
function findVerts(contactArr, poly1, poly2, n, dist) {
var num = 0;
for (var i = 0; i < poly1.verts.length; i++) {
var v = poly1.tverts[i];
if (poly2.containPoint(v)) {
contactArr.push(new Contact(v, n, dist, (poly1.id << 16) | i));
num++;
}
}
for (var i = 0; i < poly2.verts.length; i++) {
var v = poly2.tverts[i];
if (poly1.containPoint(v)) {
contactArr.push(new Contact(v, n, dist, (poly2.id << 16) | i));
num++;
}
}
return num > 0 ? num : findVertsFallback(contactArr, poly1, poly2, n, dist);
}
function poly2Poly(poly1, poly2, contactArr) {
var msa1 = findMSA(poly2, poly1.tplanes, poly1.verts.length);
if (msa1.index == -1) {
return 0;
}
var msa2 = findMSA(poly1, poly2.tplanes, poly2.verts.length);
if (msa2.index == -1) {
return 0;
}
// Penetration normal direction shoud be from poly1 to poly2
if (msa1.dist > msa2.dist) {
return findVerts(contactArr, poly1, poly2, poly1.tplanes[msa1.index].n, msa1.dist);
}
return findVerts(contactArr, poly1, poly2, vec2.neg(poly2.tplanes[msa2.index].n), msa2.dist);
}
collision.init = function() {
addCollideFunc(Shape.TYPE_CIRCLE, Shape.TYPE_CIRCLE, circle2Circle);
addCollideFunc(Shape.TYPE_CIRCLE, Shape.TYPE_SEGMENT, circle2Segment);
addCollideFunc(Shape.TYPE_CIRCLE, Shape.TYPE_POLY, circle2Poly);
addCollideFunc(Shape.TYPE_SEGMENT, Shape.TYPE_SEGMENT, segment2Segment);
addCollideFunc(Shape.TYPE_SEGMENT, Shape.TYPE_POLY, segment2Poly);
addCollideFunc(Shape.TYPE_POLY, Shape.TYPE_POLY, poly2Poly);
};
collision.collide = function(a, b, contactArr) {
if (a.type > b.type) {
var c = a;
a = b;
b = c;
}
return colFuncs[a.type * Shape.NUM_TYPES + b.type](a, b, contactArr);
};
})();
-37
View File
@@ -1,37 +0,0 @@
/*
* Copyright (c) 2012 Ju Hyung Lee
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
function Contact(p, n, d, hash) {
this.hash = hash;
// Contact point
this.p = p;
// Contact normal (toward shape2)
this.n = n;
// Penetration depth (d < 0)
this.d = d;
// Accumulated normal constraint impulse
this.lambda_n_acc = 0;
// Accumulated tangential constraint impulse
this.lambda_t_acc = 0;
}
-267
View File
@@ -1,267 +0,0 @@
/*
* Copyright (c) 2012 Ju Hyung Lee
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
//-------------------------------------------------------------------------------------------------
// Contact Constraint
//
// Non-penetration constraint:
// C = dot(p2 - p1, n)
// Cdot = dot(v2 - v1, n)
// J = [ -n, -cross(r1, n), n, cross(r2, n) ]
//
// impulse = JT * lambda = [ -n * lambda, -cross(r1, n) * lambda, n * lambda, cross(r1, n) * lambda ]
//
// Friction constraint:
// C = dot(p2 - p1, t)
// Cdot = dot(v2 - v1, t)
// J = [ -t, -cross(r1, t), t, cross(r2, t) ]
//
// impulse = JT * lambda = [ -t * lambda, -cross(r1, t) * lambda, t * lambda, cross(r1, t) * lambda ]
//
// NOTE: lambda is an impulse in constraint space.
//-------------------------------------------------------------------------------------------------
function ContactSolver(shape1, shape2) {
// Contact shapes
this.shape1 = shape1;
this.shape2 = shape2;
// Contact list
this.contactArr = [];
// Coefficient of restitution (elasticity)
this.e = 1;
// Frictional coefficient
this.u = 1;
}
ContactSolver.COLLISION_SLOP = 0.0008;
ContactSolver.BAUMGARTE = 0.28;
ContactSolver.MAX_LINEAR_CORRECTION = 1;//Infinity;
ContactSolver.prototype.update = function(newContactArr) {
for (var i = 0; i < newContactArr.length; i++) {
var newContact = newContactArr[i];
var k = -1;
for (var j = 0; j < this.contactArr.length; j++) {
if (newContact.hash == this.contactArr[j].hash) {
k = j;
break;
}
}
if (k > -1) {
newContact.lambda_n_acc = this.contactArr[k].lambda_n_acc;
newContact.lambda_t_acc = this.contactArr[k].lambda_t_acc;
}
}
this.contactArr = newContactArr;
}
ContactSolver.prototype.initSolver = function(dt_inv) {
var body1 = this.shape1.body;
var body2 = this.shape2.body;
var sum_m_inv = body1.m_inv + body2.m_inv;
for (var i = 0; i < this.contactArr.length; i++) {
var con = this.contactArr[i];
// Transformed r1, r2
con.r1 = vec2.sub(con.p, body1.p);
con.r2 = vec2.sub(con.p, body2.p);
// Local r1, r2
con.r1_local = body1.xf.unrotate(con.r1);
con.r2_local = body2.xf.unrotate(con.r2);
var n = con.n;
var t = vec2.perp(con.n);
// invEMn = J * invM * JT
// J = [ -n, -cross(r1, n), n, cross(r2, n) ]
var sn1 = vec2.cross(con.r1, n);
var sn2 = vec2.cross(con.r2, n);
var emn_inv = sum_m_inv + body1.i_inv * sn1 * sn1 + body2.i_inv * sn2 * sn2;
con.emn = emn_inv == 0 ? 0 : 1 / emn_inv;
// invEMt = J * invM * JT
// J = [ -t, -cross(r1, t), t, cross(r2, t) ]
var st1 = vec2.cross(con.r1, t);
var st2 = vec2.cross(con.r2, t);
var emt_inv = sum_m_inv + body1.i_inv * st1 * st1 + body2.i_inv * st2 * st2;
con.emt = emt_inv == 0 ? 0 : 1 / emt_inv;
// Linear velocities at contact point
// in 2D: cross(w, r) = perp(r) * w
var v1 = vec2.mad(body1.v, vec2.perp(con.r1), body1.w);
var v2 = vec2.mad(body2.v, vec2.perp(con.r2), body2.w);
// relative velocity at contact point
var rv = vec2.sub(v2, v1);
// bounce velocity dot n
con.bounce = vec2.dot(rv, con.n) * this.e;
}
}
ContactSolver.prototype.warmStart = function() {
var body1 = this.shape1.body;
var body2 = this.shape2.body;
for (var i = 0; i < this.contactArr.length; i++) {
var con = this.contactArr[i];
var n = con.n;
var lambda_n = con.lambda_n_acc;
var lambda_t = con.lambda_t_acc;
// Apply accumulated impulses
//var impulse = vec2.rotate_vec(new vec2(lambda_n, lambda_t), n);
var impulse = new vec2(lambda_n * n.x - lambda_t * n.y, lambda_t * n.x + lambda_n * n.y);
body1.v.mad(impulse, -body1.m_inv);
body1.w -= vec2.cross(con.r1, impulse) * body1.i_inv;
body2.v.mad(impulse, body2.m_inv);
body2.w += vec2.cross(con.r2, impulse) * body2.i_inv;
}
}
ContactSolver.prototype.solveVelocityConstraints = function() {
var body1 = this.shape1.body;
var body2 = this.shape2.body;
var m1_inv = body1.m_inv;
var i1_inv = body1.i_inv;
var m2_inv = body2.m_inv;
var i2_inv = body2.i_inv;
for (var i = 0; i < this.contactArr.length; i++) {
var con = this.contactArr[i];
var n = con.n;
var t = vec2.perp(n);
var r1 = con.r1;
var r2 = con.r2;
// Linear velocities at contact point
// in 2D: cross(w, r) = perp(r) * w
var v1 = vec2.mad(body1.v, vec2.perp(r1), body1.w);
var v2 = vec2.mad(body2.v, vec2.perp(r2), body2.w);
// Relative velocity at contact point
var rv = vec2.sub(v2, v1);
// Compute normal constraint impulse + adding bounce as a velocity bias
// lambda_n = -EMn * J * V
var lambda_n = -con.emn * (vec2.dot(n, rv) + con.bounce);
// Accumulate and clamp
var lambda_n_old = con.lambda_n_acc;
con.lambda_n_acc = Math.max(lambda_n_old + lambda_n, 0);
lambda_n = con.lambda_n_acc - lambda_n_old;
// Compute frictional constraint impulse
// lambda_t = -EMt * J * V
var lambda_t = -con.emt * vec2.dot(t, rv);
// Max friction constraint impulse (Coulomb's Law)
var lambda_t_max = con.lambda_n_acc * this.u;
// Accumulate and clamp
var lambda_t_old = con.lambda_t_acc;
con.lambda_t_acc = Math.clamp(lambda_t_old + lambda_t, -lambda_t_max, lambda_t_max);
lambda_t = con.lambda_t_acc - lambda_t_old;
// Apply the final impulses
//var impulse = vec2.rotate_vec(new vec2(lambda_n, lambda_t), n);
var impulse = new vec2(lambda_n * n.x - lambda_t * n.y, lambda_t * n.x + lambda_n * n.y);
body1.v.mad(impulse, -m1_inv);
body1.w -= vec2.cross(r1, impulse) * i1_inv;
body2.v.mad(impulse, m2_inv);
body2.w += vec2.cross(r2, impulse) * i2_inv;
}
}
ContactSolver.prototype.solvePositionConstraints = function() {
var body1 = this.shape1.body;
var body2 = this.shape2.body;
var m1_inv = body1.m_inv;
var i1_inv = body1.i_inv;
var m2_inv = body2.m_inv;
var i2_inv = body2.i_inv;
var sum_m_inv = m1_inv + m2_inv;
var max_penetration = 0;
for (var i = 0; i < this.contactArr.length; i++) {
var con = this.contactArr[i];
var n = con.n;
// Transformed r1, r2
var r1 = vec2.rotate(con.r1_local, body1.a);
var r2 = vec2.rotate(con.r2_local, body2.a);
// Contact points (corrected)
var p1 = vec2.add(body1.p, r1);
var p2 = vec2.add(body2.p, r2);
// Corrected delta vector
var dp = vec2.sub(p2, p1);
// Position constraint
var c = vec2.dot(dp, n) + con.d;
var correction = Math.clamp(ContactSolver.BAUMGARTE * (c + ContactSolver.COLLISION_SLOP), -ContactSolver.MAX_LINEAR_CORRECTION, 0);
if (correction == 0) {
continue;
}
// We don't need max_penetration less than or equal slop
max_penetration = Math.max(max_penetration, -c);
// Compute lambda for position constraint
// Solve (J * invM * JT) * lambda = -C / dt
var sn1 = vec2.cross(r1, n);
var sn2 = vec2.cross(r2, n);
var em_inv = sum_m_inv + body1.i_inv * sn1 * sn1 + body2.i_inv * sn2 * sn2;
var lambda_dt = em_inv == 0 ? 0 : -correction / em_inv;
// Apply correction impulses
var impulse_dt = vec2.scale(n, lambda_dt);
body1.p.mad(impulse_dt, -m1_inv);
body1.a -= sn1 * lambda_dt * i1_inv;
body2.p.mad(impulse_dt, m2_inv);
body2.a += sn2 * lambda_dt * i2_inv;
}
return max_penetration <= ContactSolver.COLLISION_SLOP * 3;
}
-76
View File
@@ -1,76 +0,0 @@
/*
* Copyright (c) 2012 Ju Hyung Lee
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
Joint = function(type, body1, body2, collideConnected) {
if (arguments.length == 0)
return;
if (Joint.id_counter == undefined)
Joint.id_counter = 0;
this.id = Joint.id_counter++;
this.type = type;
this.body1 = body1;
this.body2 = body2;
// Allow collision between to cennected body
this.collideConnected = collideConnected;
// Constraint force limit
this.maxForce = 9999999999;
// Is breakable ?
this.breakable = false;
}
Joint.TYPE_ANGLE = 0;
Joint.TYPE_REVOLUTE = 1;
Joint.TYPE_WELD = 2;
Joint.TYPE_WHEEL = 3;
Joint.TYPE_PRISMATIC = 4;
Joint.TYPE_DISTANCE = 5;
Joint.TYPE_ROPE = 6;
Joint.TYPE_MOUSE = 7;
Joint.LINEAR_SLOP = 0.0008;
Joint.ANGULAR_SLOP = deg2rad(2);
Joint.MAX_LINEAR_CORRECTION = 0.5;
Joint.MAX_ANGULAR_CORRECTION = deg2rad(8);
Joint.LIMIT_STATE_INACTIVE = 0;
Joint.LIMIT_STATE_AT_LOWER = 1;
Joint.LIMIT_STATE_AT_UPPER = 2;
Joint.LIMIT_STATE_EQUAL_LIMITS = 3;
Joint.prototype.getWorldAnchor1 = function() {
return this.body1.getWorldPoint(this.anchor1);
}
Joint.prototype.getWorldAnchor2 = function() {
return this.body2.getWorldPoint(this.anchor2);
}
Joint.prototype.setWorldAnchor1 = function(anchor1) {
this.anchor1 = this.body1.getLocalPoint(anchor1);
}
Joint.prototype.setWorldAnchor2 = function(anchor2) {
this.anchor2 = this.body2.getLocalPoint(anchor2);
}
-817
View File
@@ -1,817 +0,0 @@
/*
* Copyright (c) 2012 Ju Hyung Lee
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
Math.clamp = function(v, min, max) { return v < min ? min : (v > max ? max : v); }
Math.log2 = function(a) { return Math.log(a) / Math.log(2); }
function deg2rad(deg) { return (deg / 180) * Math.PI; }
function rad2deg(rad) { return (rad / Math.PI) * 180; }
function pixel2meter(px) { return px * 0.02; }
function meter2pixel(mt) { return mt * 50; }
//-----------------------------------
// 2D Vector
//-----------------------------------
function vec2(x, y) {
this.x = x || 0;
this.y = y || 0;
}
vec2.zero = new vec2(0, 0);
vec2.prototype.toString = function() {
//return ["x:", this.x, "y:", this.y].join(" ");
return "x=" + this.x + " y=" + this.y;
}
vec2.prototype.set = function(x, y) {
this.x = x;
this.y = y;
return this;
}
vec2.prototype.copy = function(v) {
this.x = v.x;
this.y = v.y;
return this;
}
vec2.prototype.duplicate = function() {
return new vec2(this.x, this.y);
}
vec2.prototype.equal = function(v) {
return (this.x != v.x || this.y != v.y) ? false : true;
}
vec2.prototype.add = function(v1, v2) {
this.x = v1.x + v2.x;
this.y = v1.y + v2.y;
return this;
}
vec2.prototype.addself = function(v) {
this.x += v.x;
this.y += v.y;
return this;
}
vec2.prototype.sub = function(v1, v2) {
this.x = v1.x - v2.x;
this.y = v1.y - v2.y;
return this;
}
vec2.prototype.subself = function(v) {
this.x -= v.x;
this.y -= v.y;
return this;
}
vec2.prototype.scale = function(s) {
this.x *= s;
this.y *= s;
return this;
}
vec2.prototype.scale2 = function(s) {
this.x *= s.x;
this.y *= s.y;
return this;
}
vec2.prototype.mad = function(v, s) {
this.x += v.x * s;
this.y += v.y * s;
}
vec2.prototype.neg = function() {
this.x *= -1;
this.y *= -1;
return this;
}
vec2.prototype.rcp = function() {
this.x = 1 / this.x;
this.y = 1 / this.y;
return this;
}
vec2.prototype.lengthsq = function() {
return this.x * this.x + this.y * this.y;
}
vec2.prototype.length = function() {
return Math.sqrt(this.x * this.x + this.y * this.y);
}
vec2.prototype.normalize = function() {
var inv = (this.x != 0 || this.y != 0) ? 1 / Math.sqrt(this.x * this.x + this.y * this.y) : 0;
this.x *= inv;
this.y *= inv;
return this;
}
vec2.prototype.dot = function(v) {
return this.x * v.x + this.y * v.y;
}
// Z-component of 3d cross product (ax, ay, 0) x (bx, by, 0)
vec2.prototype.cross = function(v) {
return this.x * v.y - this.y * v.x;
}
vec2.prototype.toAngle = function() {
return Math.atan2(this.y, this.x);
}
vec2.prototype.rotation = function(angle) {
this.x = Math.cos(angle);
this.y = Math.sin(angle);
return this;
}
vec2.prototype.rotate = function(angle) {
var c = Math.cos(angle);
var s = Math.sin(angle);
return this.set(this.x * c - this.y * s, this.x * s + this.y * c);
}
vec2.prototype.lerp = function(v1, v2, t) {
return this.add(vec2.scale(v1, 1 - t), vec2.scale(v2, t));
}
vec2.add = function(v1, v2) {
return new vec2(v1.x + v2.x, v1.y + v2.y);
}
vec2.sub = function(v1, v2) {
return new vec2(v1.x - v2.x, v1.y - v2.y);
}
vec2.scale = function(v, s) {
return new vec2(v.x * s, v.y * s);
}
vec2.scale2 = function(v, s) {
return new vec2(v.x * s.x, v.y * s.y);
}
vec2.mad = function(v1, v2, s) {
return new vec2(v1.x + v2.x * s, v1.y + v2.y * s);
}
vec2.neg = function(v) {
return new vec2(-v.x, -v.y);
}
vec2.rcp = function(v) {
return new vec2(1 / v.x, 1 / v.y);
}
vec2.normalize = function(v) {
var inv = (v.x != 0 || v.y != 0) ? 1 / Math.sqrt(v.x * v.x + v.y * v.y) : 0;
return new vec2(v.x * inv, v.y * inv);
}
vec2.dot = function(v1, v2) {
return v1.x * v2.x + v1.y * v2.y;
}
vec2.cross = function(v1, v2) {
return v1.x * v2.y - v1.y * v2.x;
}
vec2.toAngle = function(v) {
return Math.atan2(v.y, v.x);
}
vec2.rotation = function(angle) {
return new vec2(Math.cos(angle), Math.sin(angle));
}
vec2.rotate = function(v, angle) {
var c = Math.cos(angle);
var s = Math.sin(angle);
return new vec2(v.x * c - v.y * s, v.x * s + v.y * c);
}
// Return perpendicular vector (90 degree rotation)
vec2.perp = function(v) {
return new vec2(-v.y, v.x);
}
// Return perpendicular vector (-90 degree rotation)
vec2.rperp = function(v) {
return new vec2(v.y, -v.x);
}
vec2.dist = function(v1, v2) {
var dx = v2.x - v1.x;
var dy = v2.y - v1.y;
return Math.sqrt(dx * dx + dy * dy);
}
vec2.distsq = function(v1, v2) {
var dx = v2.x - v1.x;
var dy = v2.y - v1.y;
return dx * dx + dy * dy;
}
vec2.lerp = function(v1, v2, t) {
return vec2.add(vec2.scale(v1, 1 - t), vec2.scale(v2, t));
}
vec2.truncate = function(v, length) {
var ret = v.duplicate();
var length_sq = v.x * v.x + v.y * v.y;
if (length_sq > length * length) {
ret.scale(length / Math.sqrt(length_sq));
}
return ret;
}
//-----------------------------------
// 3D Vector
//-----------------------------------
function vec3(x, y, z) {
this.x = x || 0;
this.y = y || 0;
this.z = z || 0;
}
vec3.zero = new vec3(0, 0, 0);
vec3.prototype.toString = function() {
return ["x:", this.x, "y:", this.y, "z:", this.z].join(" ");
}
vec3.prototype.set = function(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
return this;
}
vec3.prototype.copy = function(v) {
this.x = v.x;
this.y = v.y;
this.z = v.z;
return this;
}
vec3.prototype.duplicate = function() {
return new vec3(this.x, this.y, this.z);
}
vec3.prototype.equal = function(v) {
return this.x != v.x || this.y != v.y || this.z != v.z ? false : true;
}
vec3.prototype.add = function(v1, v2) {
this.x = v1.x + v2.x;
this.y = v1.y + v2.y;
this.z = v1.z + v2.z;
return this;
}
vec3.prototype.addself = function(v) {
this.x += v.x;
this.y += v.y;
this.z += v.z;
return this;
}
vec3.prototype.sub = function(v1, v2) {
this.x = v1.x - v2.x;
this.y = v1.y - v2.y;
this.z = v1.z - v2.z;
return this;
}
vec3.prototype.subself = function(v) {
this.x -= v.x;
this.y -= v.y;
this.z -= v.z;
return this;
}
vec3.prototype.scale = function(s) {
this.x *= s;
this.y *= s;
this.z *= s;
return this;
}
vec3.prototype.mad = function(v, s) {
this.x += v.x * s;
this.y += v.y * s;
this.z += v.z * s;
}
vec3.prototype.neg = function() {
this.x *= -1;
this.y *= -1;
this.z *= -1;
return this;
}
vec3.prototype.rcp = function() {
this.x = 1 / this.x;
this.y = 1 / this.y;
this.z = 1 / this.z;
return this;
}
vec3.prototype.lengthsq = function() {
return this.x * this.x + this.y * this.y + this.z * this.z;
}
vec3.prototype.length = function() {
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
}
vec3.prototype.normalize = function() {
var inv = (this.x != 0 || this.y != 0 || this.z != 0) ? 1 / Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z) : 0;
this.x *= inv;
this.y *= inv;
this.z *= inv;
return this;
}
vec3.prototype.dot = function(v) {
return this.x * v.x + this.y * v.y + this.z * v.z;
}
vec3.prototype.toVec2 = function() {
return new vec2(this.x, this.y);
}
vec3.fromVec2 = function(v, z) {
return new vec3(v.x, v.y, z);
}
vec3.truncate = function(v, length) {
var ret = v.duplicate();
var length_sq = v.x * v.x + v.y * v.y + v.z * v.z;
if (length_sq > length * length) {
ret.scale(length / Math.sqrt(length_sq));
}
return ret;
}
//-----------------------------------
// 2x2 Matrix (row major)
//-----------------------------------
function mat2(_11, _12, _21, _22) {
this._11 = _11 || 0;
this._12 = _12 || 0;
this._21 = _21 || 0;
this._22 = _22 || 0;
}
mat2.zero = new mat2(0, 0, 0, 0);
mat2.prototype.toString = function() {
return ["[", this._11, this._12, this_21, this._22, "]"].join(" ");
}
mat2.prototype.set = function(_11, _12, _21, _22) {
this._11 = _11;
this._12 = _12;
this._21 = _21;
this._22 = _22;
return this;
}
mat2.prototype.copy = function(m) {
this._11 = m._11;
this._12 = m._12;
this._21 = m._21;
this._22 = m._22;
return this;
}
mat2.prototype.duplicate = function() {
return new mat2(this._11, this._12, this._21, this._22);
}
mat2.prototype.scale = function(s) {
this._11 *= s;
this._12 *= s;
this._21 *= s;
this._22 *= s;
return this;
}
mat2.prototype.mul = function(m) {
return this.set(
this._11 * m2._11 + this._12 * m2._21,
this._11 * m2._12 + this._12 * m2._22,
this._21 * m2._11 + this._22 * m2._21,
this._21 * m2._12 + this._22 * m2._22);
}
mat2.prototype.mulvec = function(v) {
return new vec2(
this._11 * v.x + this._12 * v.y,
this._21 * v.x + this._22 * v.y);
}
mat2.prototype.invert = function() {
var det = this._11 * this._22 - this._12 * this._21;
if (det != 0)
det = 1 / det;
return this.set(
this._22 * det, -this._12 * det,
-this._21 * det, this._11 * det);
}
// Solve A * x = b
mat2.prototype.solve = function(b) {
var det = this._11 * this._22 - this._12 * this._21;
if (det != 0)
det = 1 / det;
return new vec2(
det * (this._22 * b.x - this._12 * b.y),
det * (this._11 * b.y - this._21 * b.x));
}
mat2.mul = function(m1, m2) {
return new mat2(
m1._11 * m2._11 + m1._12 * m2._21,
m1._11 * m2._12 + m1._12 * m2._22,
m1._21 * m2._11 + m1._22 * m2._21,
m1._21 * m2._12 + m1._22 * m2._22);
}
//-----------------------------------
// 3x3 Matrix (row major)
//-----------------------------------
function mat3(_11, _12, _13, _21, _22, _23, _31, _32, _33) {
this._11 = _11 || 0;
this._12 = _12 || 0;
this._13 = _13 || 0;
this._21 = _21 || 0;
this._22 = _22 || 0;
this._23 = _23 || 0;
this._31 = _31 || 0;
this._32 = _32 || 0;
this._33 = _33 || 0;
}
mat3.zero = new mat3(0, 0, 0, 0, 0, 0, 0, 0, 0);
mat3.prototype.toString = function() {
return ["[", this._11, this._12, this._13, this_21, this._22, this._23, this._31, this._32, this._33, "]"].join(" ");
}
mat3.prototype.set = function(_11, _12, _13, _21, _22, _23, _31, _32, _33) {
this._11 = _11;
this._12 = _12;
this._13 = _13;
this._21 = _21;
this._22 = _22;
this._23 = _23;
this._31 = _31;
this._32 = _32;
this._33 = _33;
return this;
}
mat3.prototype.copy = function(m) {
this._11 = m._11;
this._12 = m._12;
this._13 = m._13;
this._21 = m._21;
this._22 = m._22;
this._23 = m._23;
this._31 = m._31;
this._32 = m._32;
this._33 = m._33;
return this;
}
mat3.prototype.duplicate = function() {
return new mat3(this._11, this._12, this._13, this._21, this._22, this._23, this._31, this._32, this._33);
}
mat3.prototype.scale = function(s) {
this._11 *= s;
this._12 *= s;
this._13 *= s;
this._21 *= s;
this._22 *= s;
this._23 *= s;
this._31 *= s;
this._32 *= s;
this._33 *= s;
return this;
}
mat3.prototype.mul = function(m) {
return this.set(
this._11 * m2._11 + this._12 * m2._21 + this._13 * m2._31,
this._11 * m2._12 + this._12 * m2._22 + this._13 * m2._32,
this._11 * m2._13 + this._12 * m2._23 + this._13 * m2._33,
this._21 * m2._11 + this._22 * m2._21 + this._23 * m2._31,
this._21 * m2._12 + this._22 * m2._22 + this._23 * m2._32,
this._21 * m2._13 + this._22 * m2._23 + this._23 * m2._33,
this._31 * m2._11 + this._32 * m2._21 + this._33 * m2._31,
this._31 * m2._12 + this._32 * m2._22 + this._33 * m2._32,
this._31 * m2._13 + this._32 * m2._23 + this._33 * m2._33);
}
mat3.prototype.mulvec = function(v) {
return new vec2(
this._11 * v.x + this._12 * v.y + this._13 * v.z,
this._21 * v.x + this._22 * v.y + this._23 * v.z,
this._31 * v.x + this._32 * v.y + this._33 * v.z);
}
mat3.prototype.invert = function() {
var det2_11 = this._22 * this._33 - this._23 * this._32;
var det2_12 = this._23 * this._31 - this._21 * this._33;
var det2_13 = this._21 * this._32 - this._22 * this._31;
var det = this._11 * det2_11 + this._12 * det2_12 + this._13 * det2_13;
if (det != 0)
det = 1 / det;
var det2_21 = this._13 * this._32 - this._12 * this._33;
var det2_22 = this._11 * this._33 - this._13 * this._31;
var det2_23 = this._12 * this._31 - this._11 * this._32;
var det2_31 = this._12 * this._23 - this._13 * this._22;
var det2_32 = this._13 * this._21 - this._11 * this._23;
var det2_33 = this._11 * this._22 - this._12 * this._21;
return this.set(
det2_11 * det, det2_12 * det, det2_13 * det,
det2_21 * det, det2_22 * det, det2_23 * det,
det2_31 * det, det2_32 * det, det2_33 * det);
}
// Solve A(2x2) * x = b
mat3.prototype.solve2x2 = function(b) {
var det = this._11 * this._22 - this._12 * this._21;
if (det != 0)
det = 1 / det;
return new vec2(
det * (this._22 * b.x - this._12 * b.y),
det * (this._11 * b.y - this._21 * b.x));
}
// Solve A(3x3) * x = b
mat3.prototype.solve = function(b) {
var det2_11 = this._22 * this._33 - this._23 * this._32;
var det2_12 = this._23 * this._31 - this._21 * this._33;
var det2_13 = this._21 * this._32 - this._22 * this._31;
var det = this._11 * det2_11 + this._12 * det2_12 + this._13 * det2_13;
if (det != 0)
det = 1 / det;
var det2_21 = this._13 * this._32 - this._12 * this._33;
var det2_22 = this._11 * this._33 - this._13 * this._31;
var det2_23 = this._12 * this._31 - this._11 * this._32;
var det2_31 = this._12 * this._23 - this._13 * this._22;
var det2_32 = this._13 * this._21 - this._11 * this._23;
var det2_33 = this._11 * this._22 - this._12 * this._21;
return new vec3(
det * (det2_11 * b.x + det2_12 * b.y + det2_13 * b.z),
det * (det2_21 * b.x + det2_22 * b.y + det2_23 * b.z),
det * (det2_31 * b.x + det2_32 * b.y + det2_33 * b.z));
}
mat3.mul = function(m1, m2) {
return new mat3(
m1._11 * m2._11 + m1._12 * m2._21 + m1._13 * m2._31,
m1._11 * m2._12 + m1._12 * m2._22 + m1._13 * m2._32,
m1._11 * m2._13 + m1._12 * m2._23 + m1._13 * m2._33,
m1._21 * m2._11 + m1._22 * m2._21 + m1._23 * m2._31,
m1._21 * m2._12 + m1._22 * m2._22 + m1._23 * m2._32,
m1._21 * m2._13 + m1._22 * m2._23 + m1._23 * m2._33,
m1._31 * m2._11 + m1._32 * m2._21 + m1._33 * m2._31,
m1._31 * m2._12 + m1._32 * m2._22 + m1._33 * m2._32,
m1._31 * m2._13 + m1._32 * m2._23 + m1._33 * m2._33);
}
//-----------------------------------
// 2D Transform
//-----------------------------------
Transform = function(pos, angle) {
this.t = pos.duplicate();
this.c = Math.cos(angle);
this.s = Math.sin(angle);
this.a = angle;
}
Transform.prototype.toString = function() {
return 't=' + this.t.toString() + ' c=' + this.c + ' s=' + this.s + ' a=' + this.a;
}
Transform.prototype.set = function(pos, angle) {
this.t.copy(pos);
this.c = Math.cos(angle);
this.s = Math.sin(angle);
this.a = angle;
return this;
}
Transform.prototype.setRotation = function(angle) {
this.c = Math.cos(angle);
this.s = Math.sin(angle);
this.a = angle;
return this;
}
Transform.prototype.setPosition = function(p) {
this.t.copy(p);
return this;
}
Transform.prototype.identity = function() {
this.t.set(0, 0);
this.c = 1;
this.s = 0;
this.a = 0;
return this;
}
Transform.prototype.rotate = function(v) {
return new vec2(v.x * this.c - v.y * this.s, v.x * this.s + v.y * this.c);
}
Transform.prototype.unrotate = function(v) {
return new vec2(v.x * this.c + v.y * this.s, -v.x * this.s + v.y * this.c);
}
Transform.prototype.transform = function(v) {
return new vec2(v.x * this.c - v.y * this.s + this.t.x, v.x * this.s + v.y * this.c + this.t.y);
}
Transform.prototype.untransform = function(v) {
var px = v.x - this.t.x;
var py = v.y - this.t.y;
return new vec2(px * this.c + py * this.s, -px * this.s + py * this.c);
}
//-----------------------------------
// 2D AABB
//-----------------------------------
Bounds = function(mins, maxs) {
this.mins = mins ? new vec2(mins.x, mins.y) : new vec2(999999, 999999);
this.maxs = maxs ? new vec2(maxs.x, maxs.y) : new vec2(-999999, -999999);
}
Bounds.prototype.toString = function() {
return ["mins:", this.mins.toString(), "maxs:", this.maxs.toString()].join(" ");
}
Bounds.prototype.set = function(mins, maxs) {
this.mins.set(mins.x, mins.y);
this.maxs.set(maxs.x, maxs.y);
}
Bounds.prototype.copy = function(b) {
this.mins.copy(b.mins);
this.maxs.copy(b.maxs);
return this;
}
Bounds.prototype.clear = function() {
this.mins.set(999999, 999999);
this.maxs.set(-999999, -999999);
return this;
}
Bounds.prototype.isEmpty = function() {
if (this.mins.x > this.maxs.x || this.mins.y > this.maxs.y)
return true;
}
Bounds.prototype.getCenter = function() {
return vec2.scale(vec2.add(this.mins, this.maxs), 0.5);
}
Bounds.prototype.getExtent = function() {
return vec2.scale(vec2.sub(this.maxs, this.mins), 0.5);
}
Bounds.prototype.getPerimeter = function() {
return (maxs.x - mins.x + maxs.y - mins.y) * 2;
}
Bounds.prototype.addPoint = function(p) {
if (this.mins.x > p.x) this.mins.x = p.x;
if (this.maxs.x < p.x) this.maxs.x = p.x;
if (this.mins.y > p.y) this.mins.y = p.y;
if (this.maxs.y < p.y) this.maxs.y = p.y;
return this;
}
Bounds.prototype.addBounds = function(b) {
if (this.mins.x > b.mins.x) this.mins.x = b.mins.x;
if (this.maxs.x < b.maxs.x) this.maxs.x = b.maxs.x;
if (this.mins.y > b.mins.y) this.mins.y = b.mins.y;
if (this.maxs.y < b.maxs.y) this.maxs.y = b.maxs.y;
return this;
}
Bounds.prototype.addBounds2 = function(mins, maxs) {
if (this.mins.x > mins.x) this.mins.x = mins.x;
if (this.maxs.x < maxs.x) this.maxs.x = maxs.x;
if (this.mins.y > mins.y) this.mins.y = mins.y;
if (this.maxs.y < maxs.y) this.maxs.y = maxs.y;
return this;
}
Bounds.prototype.addExtents = function(center, extent_x, extent_y) {
if (this.mins.x > center.x - extent_x) this.mins.x = center.x - extent_x;
if (this.maxs.x < center.x + extent_x) this.maxs.x = center.x + extent_x;
if (this.mins.y > center.y - extent_y) this.mins.y = center.y - extent_y;
if (this.maxs.y < center.y + extent_y) this.maxs.y = center.y + extent_y;
return this;
}
Bounds.prototype.expand = function(ax, ay) {
this.mins.x -= ax;
this.mins.y -= ay;
this.maxs.x += ax;
this.maxs.y += ay;
return this;
}
Bounds.prototype.containPoint = function(p) {
if (p.x < this.mins.x || p.x > this.maxs.x || p.y < this.mins.y || p.y > this.maxs.y)
return false;
return true;
}
Bounds.prototype.intersectsBounds = function(b) {
if (this.mins.x > b.maxs.x || this.maxs.x < b.mins.x || this.mins.y > b.maxs.y || this.maxs.y < b.mins.y)
return false;
return true;
}
Bounds.expand = function(b, ax, ay) {
var b = new Bounds(b.mins, b.maxs);
b.expand(ax, ay);
return b;
}
-47
View File
@@ -1,47 +0,0 @@
/*
* Copyright (c) 2012 Ju Hyung Lee
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
Shape = function(type) {
if (arguments.length == 0)
return;
if (Shape.id_counter == undefined)
Shape.id_counter = 0;
this.id = Shape.id_counter++;
this.type = type;
// Coefficient of restitution (elasticity)
this.e = 0.0;
// Frictional coefficient
this.u = 1.0;
// Mass density
this.density = 1;
// Axis-aligned bounding box
this.bounds = new Bounds;
}
Shape.TYPE_CIRCLE = 0;
Shape.TYPE_SEGMENT = 1;
Shape.TYPE_POLY = 2;
Shape.NUM_TYPES = 3;
-797
View File
@@ -1,797 +0,0 @@
/*
* Copyright (c) 2012 Ju Hyung Lee
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
function Space() {
this.bodyArr = [];
this.bodyHash = {};
this.jointArr = [];
this.jointHash = {};
this.numContacts = 0;
this.contactSolverArr = [];
this.postSolve = function(arb) {};
this.gravity = new vec2(0, 0);
this.damping = 0;
this.log = [];
}
Space.TIME_TO_SLEEP = 0.5;
Space.SLEEP_LINEAR_TOLERANCE = 0.5;
Space.SLEEP_ANGULAR_TOLERANCE = deg2rad(2);
Space.prototype.clear = function() {
Shape.id_counter = 0;
Body.id_counter = 0;
Joint.id_counter = 0;
for (var i = 0; i < this.bodyArr.length; i++) {
if (this.bodyArr[i]) {
this.removeBody(this.bodyArr[i]);
}
}
this.bodyArr = [];
this.bodyHash = {};
this.jointArr = [];
this.jointHash = {};
this.contactSolverArr = [];
this.stepCount = 0;
}
Space.prototype.toJSON = function(key) {
var o_bodies = [];
for (var i = 0; i < this.bodyArr.length; i++) {
if (this.bodyArr[i]) {
o_bodies.push(this.bodyArr[i].serialize());
}
}
var o_joints = [];
for (var i = 0; i < this.jointArr.length; i++) {
if (this.jointArr[i]) {
o_joints.push(this.jointHash[i].serialize());
}
}
return {
bodies: o_bodies,
joints: o_joints
};
}
Space.prototype.create = function(text) {
var config = JSON.parse(text);
this.clear();
for (var i = 0; i < config.bodies.length; i++) {
var config_body = config.bodies[i];
var type = {"static": Body.Static, "kinetic": Body.KINETIC, "dynamic": Body.DYNAMIC}[config_body.type];
var body = new Body(type, config_body.position.x, config_body.position.y, config_body.angle);
for (var j = 0; j < config_body.shapes.length; j++) {
var config_shape = config_body.shapes[j];
var shape;
switch (config_shape.type) {
case "ShapeCircle":
shape = new ShapeCircle(config_shape.center.x, config_shape.center.y, config_shape.radius);
break;
case "ShapeSegment":
shape = new ShapeSegment(config_shape.a, config_shape.b, config_shape.radius);
break;
case "ShapePoly":
shape = new ShapePoly(config_shape.verts);
break;
}
shape.e = config_shape.e;
shape.u = config_shape.u;
shape.density = config_shape.density;
body.addShape(shape);
}
body.resetMassData();
this.addBody(body);
}
for (var i = 0; i < config.joints.length; i++) {
var config_joint = config.joints[i];
var body1 = this.bodyArr[this.bodyHash[config_joint.body1]];
var body2 = this.bodyArr[this.bodyHash[config_joint.body2]];
var joint;
switch (config_joint.type) {
case "AngleJoint":
joint = new AngleJoint(body1, body2);
break;
case "RevoluteJoint":
joint = new RevoluteJoint(body1, body2, config_joint.anchor);
joint.enableLimit(config_joint.limitEnabled);
joint.setLimits(config_joint.limitLowerAngle, config_joint.limitUpperAngle);
joint.enableMotor(config_joint.motorEnabled);
joint.setMotorSpeed(config_joint.motorSpeed);
joint.setMaxMotorTorque(config_joint.maxMotorTorque);
break;
case "WeldJoint":
joint = new WeldJoint(body1, body2, config_joint.anchor);
joint.setSpringFrequencyHz(config_joint.frequencyHz);
joint.setSpringDampingRatio(config_joint.dampingRatio);
break;
case "WheelJoint":
joint = new WheelJoint(body1, body2, config_joint.anchor1, config_joint.anchor2);
joint.enableMotor(config_joint.motorEnabled);
joint.setMotorSpeed(config_joint.motorSpeed);
joint.setMaxMotorTorque(config_joint.maxMotorTorque);
break;
case "PrismaticJoint":
joint = new PrismaticJoint(body1, body2, config_joint.anchor1, config_joint.anchor2);
break;
case "DistanceJoint":
joint = new DistanceJoint(body1, body2, config_joint.anchor1, config_joint.anchor2);
joint.setSpringFrequencyHz(config_joint.frequencyHz);
joint.setSpringDampingRatio(config_joint.dampingRatio);
break;
case "RopeJoint":
joint = new RopeJoint(body1, body2, config_joint.anchor1, config_joint.anchor2);
break;
}
joint.collideConnected = config_joint.collideConnected;
joint.maxForce = config_joint.maxForce;
joint.breakable = config_joint.breakable;
this.addJoint(joint);
}
}
Space.prototype.addBody = function(body) {
if (this.bodyHash[body.id] != undefined) {
return;
}
var index = this.bodyArr.push(body) - 1;
this.bodyHash[body.id] = index;
body.awake(true);
body.space = this;
body.cacheData();
}
Space.prototype.removeBody = function(body) {
if (this.bodyHash[body.id] == undefined) {
return;
}
// Remove linked joint
for (var i = 0; i < body.jointArr.length; i++) {
if (body.jointArr[i]) {
this.removeJoint(body.jointArr[i]);
}
}
body.space = null;
var index = this.bodyHash[body.id];
delete this.bodyHash[body.id];
delete this.bodyArr[index];
}
Space.prototype.addJoint = function(joint) {
if (this.jointHash[joint.id] != undefined) {
return;
}
joint.body1.awake(true);
joint.body2.awake(true);
var index = this.jointArr.push(joint) - 1;
this.jointHash[joint.id] = index;
var index = joint.body1.jointArr.push(joint) - 1;
joint.body1.jointHash[joint.id] = index;
var index = joint.body2.jointArr.push(joint) - 1;
joint.body2.jointHash[joint.id] = index;
}
Space.prototype.removeJoint = function(joint) {
if (this.jointHash[joint.id] == undefined) {
return;
}
joint.body1.awake(true);
joint.body2.awake(true);
var index = joint.body1.jointHash[joint.id];
delete joint.body1.jointHash[joint.id];
delete joint.body1.jointArr[index];
var index = joint.body2.jointHash[joint.id];
delete joint.body2.jointHash[joint.id];
delete joint.body2.jointArr[index];
var index = this.jointHash[joint.id];
delete this.jointHash[joint.id];
delete this.jointArr[index];
}
Space.prototype.findShapeByPoint = function(p, refShape) {
var firstShape;
for (var i = 0; i < this.bodyArr.length; i++) {
var body = this.bodyArr[i];
if (!body) {
continue;
}
for (var j = 0; j < body.shapeArr.length; j++) {
var shape = body.shapeArr[j];
if (shape.pointQuery(p)) {
if (!refShape) {
return shape;
}
if (!firstShape) {
firstShape = shape;
}
if (shape == refShape) {
refShape = null;
}
}
}
}
return firstShape;
}
Space.prototype.findBodyByPoint = function(p, refBody) {
var firstBody;
for (var i = 0; i < this.bodyArr.length; i++) {
var body = this.bodyArr[i];
if (!body) {
continue;
}
for (var j = 0; j < body.shapeArr.length; j++) {
var shape = body.shapeArr[j];
if (shape.pointQuery(p)) {
if (!refBody) {
return shape.body;
}
if (!firstBody) {
firstBody = shape.body;
}
if (shape.body == refBody) {
refBody = null;
}
break;
}
}
}
return firstBody;
}
// TODO: Replace this function to shape hashing
Space.prototype.shapeById = function(id) {
var shape;
for (var i = 0; i < this.bodyArr.length; i++) {
var body = this.bodyArr[i];
if (!body) {
continue;
}
for (var j = 0; j < body.shapeArr.length; j++) {
if (body.shapeArr[j].id == id) {
return body.shapeArr[j];
}
}
}
return null;
}
Space.prototype.jointById = function(id) {
var index = this.jointHash[id];
if (index != undefined) {
return this.jointArr[index];
}
return null;
}
Space.prototype.findVertexByPoint = function(p, minDist, refVertexId) {
var firstVertexId = -1;
refVertexId = refVertexId || -1;
for (var i = 0; i < this.bodyArr.length; i++) {
var body = this.bodyArr[i];
if (!body) {
continue;
}
for (var j = 0; j < body.shapeArr.length; j++) {
var shape = body.shapeArr[j];
var index = shape.findVertexByPoint(p, minDist);
if (index != -1) {
var vertex = (shape.id << 16) | index;
if (refVertexId == -1) {
return vertex;
}
if (firstVertexId == -1) {
firstVertexId = vertex;
}
if (vertex == refVertexId) {
refVertexId = -1;
}
}
}
}
return firstVertexId;
}
Space.prototype.findEdgeByPoint = function(p, minDist, refEdgeId) {
var firstEdgeId = -1;
refEdgeId = refEdgeId || -1;
for (var i = 0; i < this.bodyArr.length; i++) {
var body = this.bodyArr[i];
if (!body) {
continue;
}
for (var j = 0; j < body.shapeArr.length; j++) {
var shape = body.shapeArr[j];
if (shape.type != Shape.TYPE_POLY) {
continue;
}
var index = shape.findEdgeByPoint(p, minDist);
if (index != -1) {
var edge = (shape.id << 16) | index;
if (refEdgeId == -1) {
return edge;
}
if (firstEdgeId == -1) {
firstEdgeId = edge;
}
if (edge == refEdgeId) {
refEdgeId = -1;
}
}
}
}
return firstEdgeId;
}
Space.prototype.findJointByPoint = function(p, minDist, refJointId) {
var firstJointId = -1;
var dsq = minDist * minDist;
refJointId = refJointId || -1;
for (var i = 0; i < this.jointArr.length; i++) {
var joint = this.jointArr[i];
if (!joint) {
continue;
}
var jointId = -1;
if (vec2.distsq(p, joint.getWorldAnchor1()) < dsq) {
jointId = (joint.id << 16 | 0);
}
else if (vec2.distsq(p, joint.getWorldAnchor2()) < dsq) {
jointId = (joint.id << 16 | 1);
}
if (jointId != -1) {
if (refJointId == -1) {
return jointId;
}
if (firstJointId == -1) {
firstJointId = jointId;
}
if (jointId == refJointId) {
refJointId = -1;
}
}
}
return firstJointId;
}
Space.prototype.findContactSolver = function(shape1, shape2) {
for (var i = 0; i < this.contactSolverArr.length; i++) {
var contactSolver = this.contactSolverArr[i];
if (shape1 == contactSolver.shape1 && shape2 == contactSolver.shape2) {
return contactSolver;
}
}
return null;
}
Space.dump = function (phase, body) {
var s = "\n\nPhase: " + phase + "\n";
s += "Position: " + body.p.toString() + "\n";
s += "Velocity: " + body.v.toString() + "\n";
s += "Angle: " + body.a + "\n";
s += "Force: " + body.f.toString() + "\n";
s += "Torque: " + body.t + "\n";
s += "Bounds: " + body.bounds.toString() + "\n";
s += "Shape ***\n";
s += "Vert 0: " + body.shapeArr[0].verts[0].toString() + "\n";
s += "Vert 1: " + body.shapeArr[0].verts[1].toString() + "\n";
s += "Vert 2: " + body.shapeArr[0].verts[2].toString() + "\n";
s += "Vert 3: " + body.shapeArr[0].verts[3].toString() + "\n";
s += "TVert 0: " + body.shapeArr[0].tverts[0].toString() + "\n";
s += "TVert 1: " + body.shapeArr[0].tverts[1].toString() + "\n";
s += "TVert 2: " + body.shapeArr[0].tverts[2].toString() + "\n";
s += "TVert 3: " + body.shapeArr[0].tverts[3].toString() + "\n";
s += "Plane 0: " + body.shapeArr[0].planes[0].n.toString() + "\n";
s += "Plane 1: " + body.shapeArr[0].planes[1].n.toString() + "\n";
s += "Plane 2: " + body.shapeArr[0].planes[2].n.toString() + "\n";
s += "Plane 3: " + body.shapeArr[0].planes[3].n.toString() + "\n";
s += "TPlane 0: " + body.shapeArr[0].tplanes[0].n.toString() + "\n";
s += "TPlane 1: " + body.shapeArr[0].tplanes[1].n.toString() + "\n";
s += "TPlane 2: " + body.shapeArr[0].tplanes[2].n.toString() + "\n";
s += "TPlane 3: " + body.shapeArr[0].tplanes[3].n.toString() + "\n";
this.log.push(s);
}
Space.prototype.genTemporalContactSolvers = function() {
var t0 = Date.now();
var newContactSolverArr = [];
this.numContacts = 0;
for (var body1_index = 0; body1_index < this.bodyArr.length; body1_index++) {
var body1 = this.bodyArr[body1_index];
if (!body1) {
continue;
}
body1.stepCount = this.stepCount;
for (var body2_index = 0; body2_index < this.bodyArr.length; body2_index++) {
var body2 = this.bodyArr[body2_index];
if (!body2) {
continue;
}
if (body1.stepCount == body2.stepCount) {
continue;
}
var active1 = body1.isAwake() && !body1.isStatic();
var active2 = body2.isAwake() && !body2.isStatic();
if (!active1 && !active2) {
continue;
}
if (!body1.isCollidable(body2)) {
continue;
}
if (!body1.bounds.intersectsBounds(body2.bounds)) {
continue;
}
for (var i = 0; i < body1.shapeArr.length; i++) {
for (var j = 0; j < body2.shapeArr.length; j++) {
var shape1 = body1.shapeArr[i];
var shape2 = body2.shapeArr[j];
var contactArr = [];
if (!collision.collide(shape1, shape2, contactArr)) {
continue;
}
if (shape1.type > shape2.type) {
var temp = shape1;
shape1 = shape2;
shape2 = temp;
}
this.numContacts += contactArr.length;
var contactSolver = this.findContactSolver(shape1, shape2);
if (contactSolver) {
contactSolver.update(contactArr);
newContactSolverArr.push(contactSolver);
}
else {
body1.awake(true);
body2.awake(true);
var newContactSolver = new ContactSolver(shape1, shape2);
newContactSolver.contactArr = contactArr;
newContactSolver.e = Math.max(shape1.e, shape2.e);
newContactSolver.u = Math.sqrt(shape1.u * shape2.u);
newContactSolverArr.push(newContactSolver);
}
}
}
}
}
stats.timeCollision = Date.now() - t0;
return newContactSolverArr;
}
Space.prototype.initSolver = function(dt, dt_inv, warmStarting) {
var t0 = Date.now();
// Initialize contact solvers
for (var i = 0; i < this.contactSolverArr.length; i++) {
this.contactSolverArr[i].initSolver(dt_inv);
}
// Initialize joint solver
for (var i = 0; i < this.jointArr.length; i++) {
if (this.jointArr[i]) {
this.jointArr[i].initSolver(dt, warmStarting);
}
}
// Warm starting (apply cached impulse)
if (warmStarting) {
for (var i = 0; i < this.contactSolverArr.length; i++) {
this.contactSolverArr[i].warmStart();
}
}
stats.timeInitSolver = Date.now() - t0;
}
Space.prototype.velocitySolver = function(iteration) {
var t0 = Date.now();
for (var i = 0; i < iteration; i++) {
for (var j = 0; j < this.jointArr.length; j++) {
if (this.jointArr[j]) {
this.jointArr[j].solveVelocityConstraints();
}
}
for (var j = 0; j < this.contactSolverArr.length; j++) {
this.contactSolverArr[j].solveVelocityConstraints();
}
}
stats.timeVelocitySolver = Date.now() - t0;
}
Space.prototype.positionSolver = function(iteration) {
var t0 = Date.now();
var positionSolved = false;
stats.positionIterations = 0;
for (var i = 0; i < iteration; i++) {
var contactsOk = true;
var jointsOk = true;
for (var j = 0; j < this.contactSolverArr.length; j++) {
var contactOk = this.contactSolverArr[j].solvePositionConstraints();
contactsOk = contactOk && contactsOk;
}
for (var j = 0; j < this.jointArr.length; j++) {
if (this.jointArr[j]) {
var jointOk = this.jointArr[j].solvePositionConstraints();
jointsOk = jointOk && jointsOk;
}
}
if (contactsOk && jointsOk) {
// exit early if the position errors are small
positionSolved = true;
break;
}
stats.positionIterations++;
}
stats.timePositionSolver = Date.now() - t0;
return positionSolved;
}
Space.prototype.step = function(dt, vel_iteration, pos_iteration, warmStarting, allowSleep) {
var dt_inv = 1 / dt;
this.stepCount++;
// Generate contact & contactSolver
this.contactSolverArr = this.genTemporalContactSolvers();
// Initialize contacts & joints solver
this.initSolver(dt, dt_inv, warmStarting);
// Intergrate velocity
for (var i = 0; i < this.bodyArr.length; i++) {
var body = this.bodyArr[i];
if (!body) {
continue;
}
if (body.isDynamic() && body.isAwake()) {
body.updateVelocity(this.gravity, dt, this.damping);
}
}
for (var i = 0; i < this.jointArr.length; i++) {
var joint = this.jointArr[i];
if (!joint) {
continue;
}
var body1 = joint.body1;
var body2 = joint.body2;
var awake1 = body1.isAwake() && !body1.isStatic();
var awake2 = body2.isAwake() && !body2.isStatic();
if (awake1 ^ awake2) {
if (!awake1)
body1.awake(true);
if (!awake2)
body2.awake(true);
}
}
// Iterative velocity constraints solver
this.velocitySolver(vel_iteration);
// Intergrate position
for (var i = 0; i < this.bodyArr.length; i++) {
var body = this.bodyArr[i];
if (!body) {
continue
}
if (body.isDynamic() && body.isAwake()) {
body.updatePosition(dt);
}
}
// Process breakable joint
for (var i = 0; i < this.jointArr.length; i++) {
var joint = this.jointArr[i];
if (!joint) {
continue;
}
if (joint.breakable) {
if (joint.getReactionForce(dt_inv).lengthsq() >= joint.maxForce * joint.maxForce)
this.removeJoint(joint);
}
}
// Iterative position constraints solver
var positionSolved = this.positionSolver(pos_iteration);
for (var i = 0; i < this.bodyArr.length; i++) {
var body = this.bodyArr[i];
if (!body) {
continue;
}
body.syncTransform();
}
// Post solve collision callback
for (var i = 0; i < this.contactSolverArr.length; i++) {
var arb = this.contactSolverArr[i];
//this.postSolve(arb);
}
for (var i = 0; i < this.bodyArr.length; i++) {
var body = this.bodyArr[i];
if (!body) {
continue;
}
if (body.isDynamic() && body.isAwake()) {
body.cacheData();
}
}
// Process sleeping
if (allowSleep) {
var minSleepTime = 999999;
var linTolSqr = Space.SLEEP_LINEAR_TOLERANCE * Space.SLEEP_LINEAR_TOLERANCE;
var angTolSqr = Space.SLEEP_ANGULAR_TOLERANCE * Space.SLEEP_ANGULAR_TOLERANCE;
for (var i = 0; i < this.bodyArr.length; i++) {
var body = this.bodyArr[i];
if (!body) {
continue;
}
if (!body.isDynamic()) {
continue;
}
if (body.w * body.w > angTolSqr || body.v.dot(body.v) > linTolSqr) {
body.sleepTime = 0;
minSleepTime = 0;
}
else {
body.sleepTime += dt;
minSleepTime = Math.min(minSleepTime, body.sleepTime);
}
}
if (positionSolved && minSleepTime >= Space.TIME_TO_SLEEP) {
for (var i = 0; i < this.bodyArr.length; i++) {
var body = this.bodyArr[i];
if (!body) {
continue;
}
body.awake(false);
}
}
}
}
-147
View File
@@ -1,147 +0,0 @@
/*
* Copyright (c) 2012 Ju Hyung Lee
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
function areaForCircle(radius_outer, radius_inner) {
return Math.PI * (radius_outer * radius_outer - radius_inner * radius_inner);
}
function inertiaForCircle(mass, center, radius_outer, radius_inner) {
return mass * ((radius_outer * radius_outer + radius_inner * radius_inner) * 0.5 + center.lengthsq());
}
function areaForSegment(a, b, radius) {
return radius * (Math.PI * radius + 2 * vec2.dist(a, b));
}
function centroidForSegment(a, b) {
return vec2.scale(vec2.add(a, b), 0.5);
}
function inertiaForSegment(mass, a, b) {
var distsq = vec2.distsq(b, a);
var offset = vec2.scale(vec2.add(a, b), 0.5);
return mass * (distsq / 12 + offset.lengthsq());
}
function areaForPoly(verts) {
var area = 0;
for (var i = 0; i < verts.length; i++) {
area += vec2.cross(verts[i], verts[(i + 1) % verts.length]);
}
return area / 2;
}
function centroidForPoly(verts) {
var area = 0;
var vsum = new vec2(0, 0);
for (var i = 0; i < verts.length; i++) {
var v1 = verts[i];
var v2 = verts[(i + 1) % verts.length];
var cross = vec2.cross(v1, v2);
area += cross;
vsum.addself(vec2.scale(vec2.add(v1, v2), cross));
}
return vec2.scale(vsum, 1 / (3 * area));
}
function inertiaForPoly(mass, verts, offset) {
var sum1 = 0;
var sum2 = 0;
for (var i = 0; i < verts.length; i++) {
var v1 = vec2.add(verts[i], offset);
var v2 = vec2.add(verts[(i+1) % verts.length], offset);
var a = vec2.cross(v2, v1);
var b = vec2.dot(v1, v1) + vec2.dot(v1, v2) + vec2.dot(v2, v2);
sum1 += a * b;
sum2 += a;
}
return (mass * sum1) / (6 * sum2);
}
function inertiaForBox(mass, w, h) {
return mass * (w * w + h * h) / 12;
}
// Create the convex hull using the Gift wrapping algorithm
// http://en.wikipedia.org/wiki/Gift_wrapping_algorithm
function createConvexHull(points) {
// Find the right most point on the hull
var i0 = 0;
var x0 = points[0].x;
for (var i = 1; i < points.length; i++) {
var x = points[i].x;
if (x > x0 || (x == x0 && points[i].y < points[i0].y)) {
i0 = i;
x0 = x;
}
}
var n = points.length;
var hull = [];
var m = 0;
var ih = i0;
while (1) {
hull[m] = ih;
var ie = 0;
for (var j = 1; j < n; j++) {
if (ie == ih) {
ie = j;
continue;
}
var r = vec2.sub(points[ie], points[hull[m]]);
var v = vec2.sub(points[j], points[hull[m]]);
var c = vec2.cross(r, v);
if (c < 0) {
ie = j;
}
// Collinearity check
if (c == 0 && v.lengthsq() > r.lengthsq()) {
ie = j;
}
}
m++;
ih = ie;
if (ie == i0) {
break;
}
}
// Copy vertices
var newPoints = [];
for (var i = 0; i < m; ++i) {
newPoints.push(points[hull[i]]);
}
return newPoints;
}
@@ -0,0 +1,43 @@
var vec2 = require('../math/vec2')
, Nearphase = require('./Nearphase')
, Shape = require('./../shapes/Shape')
module.exports = Broadphase;
/**
* Base class for broadphase implementations.
* @class Broadphase
* @constructor
*/
function Broadphase(){
this.result = [];
};
/**
* Get all potential intersecting body pairs.
* @method getCollisionPairs
* @param {World} world The world to search in.
* @return {Array} An array of the bodies, ordered in pairs. Example: A result of [a,b,c,d] means that the potential pairs are: (a,b), (c,d).
*/
Broadphase.prototype.getCollisionPairs = function(world){
throw new Error("getCollisionPairs must be implemented in a subclass!");
};
// Temp things
var dist = vec2.create(),
worldNormal = vec2.create(),
yAxis = vec2.fromValues(0,1);
/**
* Check whether the bounding radius of two bodies overlap.
* @method boundingRadiusCheck
* @param {Body} bodyA
* @param {Body} bodyB
* @return {Boolean}
*/
Broadphase.boundingRadiusCheck = function(bodyA, bodyB){
vec2.sub(dist, bodyA.position, bodyB.position);
var d2 = vec2.squaredLength(dist),
r = bodyA.boundingRadius + bodyB.boundingRadius;
return d2 <= r*r;
};
@@ -0,0 +1,159 @@
var Circle = require('../shapes/Circle')
, Plane = require('../shapes/Plane')
, Particle = require('../shapes/Particle')
, Broadphase = require('../collision/Broadphase')
, vec2 = require('../math/vec2')
module.exports = GridBroadphase;
/**
* Broadphase that uses axis-aligned bins.
* @class GridBroadphase
* @constructor
* @extends Broadphase
* @param {number} xmin Lower x bound of the grid
* @param {number} xmax Upper x bound
* @param {number} ymin Lower y bound
* @param {number} ymax Upper y bound
* @param {number} nx Number of bins along x axis
* @param {number} ny Number of bins along y axis
* @todo test
*/
function GridBroadphase(xmin,xmax,ymin,ymax,nx,ny){
Broadphase.apply(this);
nx = nx || 10;
ny = ny || 10;
this.binsizeX = (xmax-xmin) / nx;
this.binsizeY = (ymax-ymin) / ny;
this.nx = nx;
this.ny = ny;
this.xmin = xmin;
this.ymin = ymin;
this.xmax = xmax;
this.ymax = ymax;
};
GridBroadphase.prototype = new Broadphase();
/**
* Get a bin index given a world coordinate
* @method getBinIndex
* @param {Number} x
* @param {Number} y
* @return {Number} Integer index
*/
GridBroadphase.prototype.getBinIndex = function(x,y){
var nx = this.nx,
ny = this.ny,
xmin = this.xmin,
ymin = this.ymin,
xmax = this.xmax,
ymax = this.ymax;
var xi = Math.floor(nx * (x - xmin) / (xmax-xmin));
var yi = Math.floor(ny * (y - ymin) / (ymax-ymin));
return xi*ny + yi;
}
/**
* Get collision pairs.
* @method getCollisionPairs
* @param {World} world
* @return {Array}
*/
GridBroadphase.prototype.getCollisionPairs = function(world){
var result = [],
collidingBodies = world.bodies,
Ncolliding = Ncolliding=collidingBodies.length,
binsizeX = this.binsizeX,
binsizeY = this.binsizeY;
var bins=[], Nbins=nx*ny;
for(var i=0; i<Nbins; i++)
bins.push([]);
var xmult = nx / (xmax-xmin);
var ymult = ny / (ymax-ymin);
// Put all bodies into bins
for(var i=0; i!==Ncolliding; i++){
var bi = collidingBodies[i];
var si = bi.shape;
if (si === undefined) {
continue;
} else if(si instanceof Circle){
// Put in bin
// check if overlap with other bins
var x = bi.position[0];
var y = bi.position[1];
var r = si.radius;
var xi1 = Math.floor(xmult * (x-r - xmin));
var yi1 = Math.floor(ymult * (y-r - ymin));
var xi2 = Math.floor(xmult * (x+r - xmin));
var yi2 = Math.floor(ymult * (y+r - ymin));
for(var j=xi1; j<=xi2; j++){
for(var k=yi1; k<=yi2; k++){
var xi = j;
var yi = k;
if(xi*(ny-1) + yi >= 0 && xi*(ny-1) + yi < Nbins)
bins[ xi*(ny-1) + yi ].push(bi);
}
}
} else if(si instanceof Plane){
// Put in all bins for now
if(bi.angle == 0){
var y = bi.position[1];
for(var j=0; j!==Nbins && ymin+binsizeY*(j-1)<y; j++){
for(var k=0; k<nx; k++){
var xi = k;
var yi = Math.floor(ymult * (binsizeY*j - ymin));
bins[ xi*(ny-1) + yi ].push(bi);
}
}
} else if(bi.angle == Math.PI*0.5){
var x = bi.position[0];
for(var j=0; j!==Nbins && xmin+binsizeX*(j-1)<x; j++){
for(var k=0; k<ny; k++){
var yi = k;
var xi = Math.floor(xmult * (binsizeX*j - xmin));
bins[ xi*(ny-1) + yi ].push(bi);
}
}
} else {
for(var j=0; j!==Nbins; j++)
bins[j].push(bi);
}
} else {
throw new Error("Shape not supported in GridBroadphase!");
}
}
// Check each bin
for(var i=0; i!==Nbins; i++){
var bin = bins[i];
for(var j=0, NbodiesInBin=bin.length; j!==NbodiesInBin; j++){
var bi = bin[j];
var si = bi.shape;
for(var k=0; k!==j; k++){
var bj = bin[k];
var sj = bj.shape;
if(si instanceof Circle){
if(sj instanceof Circle) c=Broadphase.circleCircle (bi,bj);
else if(sj instanceof Particle) c=Broadphase.circleParticle(bi,bj);
else if(sj instanceof Plane) c=Broadphase.circlePlane (bi,bj);
} else if(si instanceof Particle){
if(sj instanceof Circle) c=Broadphase.circleParticle(bj,bi);
} else if(si instanceof Plane){
if(sj instanceof Circle) c=Broadphase.circlePlane (bj,bi);
}
}
}
}
return result;
};
@@ -0,0 +1,47 @@
var Circle = require('../shapes/Circle')
, Plane = require('../shapes/Plane')
, Shape = require('../shapes/Shape')
, Particle = require('../shapes/Particle')
, Broadphase = require('../collision/Broadphase')
, vec2 = require('../math/vec2')
module.exports = NaiveBroadphase;
/**
* Naive broadphase implementation. Does N^2 tests.
*
* @class NaiveBroadphase
* @constructor
* @extends Broadphase
*/
function NaiveBroadphase(){
Broadphase.apply(this);
};
NaiveBroadphase.prototype = new Broadphase();
/**
* Get the colliding pairs
* @method getCollisionPairs
* @param {World} world
* @return {Array}
*/
NaiveBroadphase.prototype.getCollisionPairs = function(world){
var bodies = world.bodies,
result = this.result,
i, j, bi, bj;
result.length = 0;
for(i=0, Ncolliding=bodies.length; i!==Ncolliding; i++){
bi = bodies[i];
for(j=0; j<i; j++){
bj = bodies[j];
if(Broadphase.boundingRadiusCheck(bi,bj))
result.push(bi,bj);
}
}
return result;
};
File diff suppressed because it is too large Load Diff
+376
View File
@@ -0,0 +1,376 @@
var Plane = require("../shapes/Plane");
var Broadphase = require("../collision/Broadphase");
module.exports = {
QuadTree : QuadTree,
Node : Node,
BoundsNode : BoundsNode,
};
/**
* QuadTree data structure. See https://github.com/mikechambers/ExamplesByMesh/tree/master/JavaScript/QuadTree
* @class QuadTree
* @constructor
* @param {Object} An object representing the bounds of the top level of the QuadTree. The object
* should contain the following properties : x, y, width, height
* @param {Boolean} pointQuad Whether the QuadTree will contain points (true), or items with bounds
* (width / height)(false). Default value is false.
* @param {Number} maxDepth The maximum number of levels that the quadtree will create. Default is 4.
* @param {Number} maxChildren The maximum number of children that a node can contain before it is split into sub-nodes.
*/
function QuadTree(bounds, pointQuad, maxDepth, maxChildren){
var node;
if(pointQuad){
node = new Node(bounds, 0, maxDepth, maxChildren);
} else {
node = new BoundsNode(bounds, 0, maxDepth, maxChildren);
}
/**
* The root node of the QuadTree which covers the entire area being segmented.
* @property root
* @type Node
*/
this.root = node;
}
/**
* Inserts an item into the QuadTree.
* @method insert
* @param {Object|Array} item The item or Array of items to be inserted into the QuadTree. The item should expose x, y
* properties that represents its position in 2D space.
*/
QuadTree.prototype.insert = function(item){
if(item instanceof Array){
var len = item.length;
for(var i = 0; i < len; i++){
this.root.insert(item[i]);
}
} else {
this.root.insert(item);
}
}
/**
* Clears all nodes and children from the QuadTree
* @method clear
*/
QuadTree.prototype.clear = function(){
this.root.clear();
}
/**
* Retrieves all items / points in the same node as the specified item / point. If the specified item
* overlaps the bounds of a node, then all children in both nodes will be returned.
* @method retrieve
* @param {Object} item An object representing a 2D coordinate point (with x, y properties), or a shape
* with dimensions (x, y, width, height) properties.
*/
QuadTree.prototype.retrieve = function(item){
//get a copy of the array of items
var out = this.root.retrieve(item).slice(0);
return out;
}
QuadTree.prototype.getCollisionPairs = function(world){
var result = [];
// Add all bodies
this.insert(world.bodies);
/*
console.log("bodies",world.bodies.length);
console.log("maxDepth",this.root.maxDepth,"maxChildren",this.root.maxChildren);
*/
for(var i=0; i!==world.bodies.length; i++){
var b = world.bodies[i],
items = this.retrieve(b);
//console.log("items",items.length);
// Check results
for(var j=0, len=items.length; j!==len; j++){
var item = items[j];
if(b === item) continue; // Do not add self
// Check if they were already added
var found = false;
for(var k=0, numAdded=result.length; k<numAdded; k+=2){
var r1 = result[k],
r2 = result[k+1];
if( (r1==item && r2==b) || (r2==item && r1==b) ){
found = true;
break;
}
}
if(!found && Broadphase.boundingRadiusCheck(b,item)){
result.push(b,item);
}
}
}
//console.log("results",result.length);
// Clear until next
this.clear();
return result;
};
function Node(bounds, depth, maxDepth, maxChildren){
this.bounds = bounds;
this.children = [];
this.nodes = [];
if(maxChildren){
this.maxChildren = maxChildren;
}
if(maxDepth){
this.maxDepth = maxDepth;
}
if(depth){
this.depth = depth;
}
}
//subnodes
Node.prototype.classConstructor = Node;
//children contained directly in the node
Node.prototype.children = null;
//read only
Node.prototype.depth = 0;
Node.prototype.maxChildren = 4;
Node.prototype.maxDepth = 4;
Node.TOP_LEFT = 0;
Node.TOP_RIGHT = 1;
Node.BOTTOM_LEFT = 2;
Node.BOTTOM_RIGHT = 3;
Node.prototype.insert = function(item){
if(this.nodes.length){
var index = this.findIndex(item);
this.nodes[index].insert(item);
return;
}
this.children.push(item);
var len = this.children.length;
if(!(this.depth >= this.maxDepth) && len > this.maxChildren) {
this.subdivide();
for(var i = 0; i < len; i++){
this.insert(this.children[i]);
}
this.children.length = 0;
}
}
Node.prototype.retrieve = function(item){
if(this.nodes.length){
var index = this.findIndex(item);
return this.nodes[index].retrieve(item);
}
return this.children;
}
Node.prototype.findIndex = function(item){
var b = this.bounds;
var left = (item.position[0]-item.boundingRadius > b.x + b.width / 2) ? false : true;
var top = (item.position[1]-item.boundingRadius > b.y + b.height / 2) ? false : true;
if(item instanceof Plane){
left = top = false; // Will overlap the left/top boundary since it is infinite
}
//top left
var index = Node.TOP_LEFT;
if(left){
if(!top){
index = Node.BOTTOM_LEFT;
}
} else {
if(top){
index = Node.TOP_RIGHT;
} else {
index = Node.BOTTOM_RIGHT;
}
}
return index;
}
Node.prototype.subdivide = function(){
var depth = this.depth + 1;
var bx = this.bounds.x;
var by = this.bounds.y;
//floor the values
var b_w_h = (this.bounds.width / 2);
var b_h_h = (this.bounds.height / 2);
var bx_b_w_h = bx + b_w_h;
var by_b_h_h = by + b_h_h;
//top left
this.nodes[Node.TOP_LEFT] = new this.classConstructor({
x:bx,
y:by,
width:b_w_h,
height:b_h_h
},
depth);
//top right
this.nodes[Node.TOP_RIGHT] = new this.classConstructor({
x:bx_b_w_h,
y:by,
width:b_w_h,
height:b_h_h
},
depth);
//bottom left
this.nodes[Node.BOTTOM_LEFT] = new this.classConstructor({
x:bx,
y:by_b_h_h,
width:b_w_h,
height:b_h_h
},
depth);
//bottom right
this.nodes[Node.BOTTOM_RIGHT] = new this.classConstructor({
x:bx_b_w_h,
y:by_b_h_h,
width:b_w_h,
height:b_h_h
},
depth);
}
Node.prototype.clear = function(){
this.children.length = 0;
var len = this.nodes.length;
for(var i = 0; i < len; i++){
this.nodes[i].clear();
}
this.nodes.length = 0;
}
// BoundsQuadTree
function BoundsNode(bounds, depth, maxChildren, maxDepth){
Node.call(this, bounds, depth, maxChildren, maxDepth);
this.stuckChildren = [];
}
BoundsNode.prototype = new Node();
BoundsNode.prototype.classConstructor = BoundsNode;
BoundsNode.prototype.stuckChildren = null;
//we use this to collect and conctenate items being retrieved. This way
//we dont have to continuously create new Array instances.
//Note, when returned from QuadTree.retrieve, we then copy the array
BoundsNode.prototype.out = [];
BoundsNode.prototype.insert = function(item){
if(this.nodes.length){
var index = this.findIndex(item);
var node = this.nodes[index];
/*
console.log("radius:",item.boundingRadius);
console.log("item x:",item.position[0] - item.boundingRadius,"x range:",node.bounds.x,node.bounds.x+node.bounds.width);
console.log("item y:",item.position[1] - item.boundingRadius,"y range:",node.bounds.y,node.bounds.y+node.bounds.height);
*/
//todo: make _bounds bounds
if( !(item instanceof Plane) && // Plane is infinite.. Make it a "stuck" child
item.position[0] - item.boundingRadius >= node.bounds.x &&
item.position[0] + item.boundingRadius <= node.bounds.x + node.bounds.width &&
item.position[1] - item.boundingRadius >= node.bounds.y &&
item.position[1] + item.boundingRadius <= node.bounds.y + node.bounds.height){
this.nodes[index].insert(item);
} else {
this.stuckChildren.push(item);
}
return;
}
this.children.push(item);
var len = this.children.length;
if(this.depth < this.maxDepth && len > this.maxChildren){
this.subdivide();
for(var i=0; i<len; i++){
this.insert(this.children[i]);
}
this.children.length = 0;
}
}
BoundsNode.prototype.getChildren = function(){
return this.children.concat(this.stuckChildren);
}
BoundsNode.prototype.retrieve = function(item){
var out = this.out;
out.length = 0;
if(this.nodes.length){
var index = this.findIndex(item);
out.push.apply(out, this.nodes[index].retrieve(item));
}
out.push.apply(out, this.stuckChildren);
out.push.apply(out, this.children);
return out;
}
BoundsNode.prototype.clear = function(){
this.stuckChildren.length = 0;
//array
this.children.length = 0;
var len = this.nodes.length;
if(!len){
return;
}
for(var i = 0; i < len; i++){
this.nodes[i].clear();
}
//array
this.nodes.length = 0;
//we could call the super clear function but for now, im just going to inline it
//call the hidden super.clear, and make sure its called with this = this instance
//Object.getPrototypeOf(BoundsNode.prototype).clear.call(this);
}
@@ -0,0 +1,120 @@
var Circle = require('../shapes/Circle')
, Plane = require('../shapes/Plane')
, Shape = require('../shapes/Shape')
, Particle = require('../shapes/Particle')
, Broadphase = require('../collision/Broadphase')
, vec2 = require('../math/vec2')
module.exports = SAP1DBroadphase;
/**
* Sweep and prune broadphase along one axis.
*
* @class SAP1DBroadphase
* @constructor
* @extends Broadphase
* @param {World} world
*/
function SAP1DBroadphase(world){
Broadphase.apply(this);
/**
* List of bodies currently in the broadphase.
* @property axisList
* @type {Array}
*/
this.axisList = world.bodies.slice(0);
/**
* The world to search in.
* @property world
* @type {World}
*/
this.world = world;
/**
* Axis to sort the bodies along. Set to 0 for x axis, and 1 for y axis. For best performance, choose an axis that the bodies are spread out more on.
* @property axisIndex
* @type {Number}
*/
this.axisIndex = 0;
// Add listeners to update the list of bodies.
var axisList = this.axisList;
world.on("addBody",function(e){
axisList.push(e.body);
}).on("removeBody",function(e){
var idx = axisList.indexOf(e.body);
if(idx !== -1)
axisList.splice(idx,1);
});
};
SAP1DBroadphase.prototype = new Broadphase();
/**
* Function for sorting bodies along the X axis. To be passed to array.sort()
* @method sortAxisListX
* @param {Body} bodyA
* @param {Body} bodyB
* @return {Number}
*/
SAP1DBroadphase.sortAxisListX = function(bodyA,bodyB){
return (bodyA.position[0]-bodyA.boundingRadius) - (bodyB.position[0]-bodyB.boundingRadius);
};
/**
* Function for sorting bodies along the Y axis. To be passed to array.sort()
* @method sortAxisListY
* @param {Body} bodyA
* @param {Body} bodyB
* @return {Number}
*/
SAP1DBroadphase.sortAxisListY = function(bodyA,bodyB){
return (bodyA.position[1]-bodyA.boundingRadius) - (bodyB.position[1]-bodyB.boundingRadius);
};
/**
* Get the colliding pairs
* @method getCollisionPairs
* @param {World} world
* @return {Array}
*/
SAP1DBroadphase.prototype.getCollisionPairs = function(world){
var bodies = this.axisList,
result = this.result,
axisIndex = this.axisIndex,
i,j;
result.length = 0;
// Sort the list
bodies.sort(axisIndex === 0 ? SAP1DBroadphase.sortAxisListX : SAP1DBroadphase.sortAxisListY );
// Look through the list
for(i=0, N=bodies.length; i!==N; i++){
var bi = bodies[i],
biPos = bi.position[axisIndex],
ri = bi.boundingRadius;
for(j=i+1; j<N; j++){
var bj = bodies[j],
bjPos = bj.position[axisIndex],
rj = bj.boundingRadius,
boundA1 = biPos-ri,
boundA2 = biPos+ri,
boundB1 = bjPos-rj,
boundB2 = bjPos+rj;
// Abort if we got gap til the next body
if( boundB1 > boundA2 ){
break;
}
// If we got overlap, add pair
if(Broadphase.boundingRadiusCheck(bi,bj))
result.push(bi,bj);
}
}
return result;
};
@@ -0,0 +1,42 @@
module.exports = Constraint;
/**
* Base constraint class.
*
* @class Constraint
* @constructor
* @author schteppe
* @param {Body} bodyA
* @param {Body} bodyB
*/
function Constraint(bodyA,bodyB){
/**
* Equations to be solved in this constraint
* @property equations
* @type {Array}
*/
this.equations = [];
/**
* First body participating in the constraint.
* @property bodyA
* @type {Body}
*/
this.bodyA = bodyA;
/**
* Second body participating in the constraint.
* @property bodyB
* @type {Body}
*/
this.bodyB = bodyB;
};
/**
* To be implemented by subclasses. Should update the internal constraint parameters.
* @method update
*/
/*Constraint.prototype.update = function(){
throw new Error("method update() not implmemented in this Constraint subclass!");
};*/
@@ -0,0 +1,136 @@
var Equation = require("./Equation"),
vec2 = require('../math/vec2'),
mat2 = require('../math/mat2');
module.exports = ContactEquation;
/**
* Non-penetration constraint equation.
*
* @class ContactEquation
* @constructor
* @extends Equation
* @param {Body} bi
* @param {Body} bj
*/
function ContactEquation(bi,bj){
Equation.call(this,bi,bj,0,1e6);
this.ri = vec2.create();
this.penetrationVec = vec2.create();
this.rj = vec2.create();
this.ni = vec2.create();
this.rixn = 0;
this.rjxn = 0;
};
ContactEquation.prototype = new Equation();
ContactEquation.prototype.constructor = ContactEquation;
ContactEquation.prototype.computeB = function(a,b,h){
var bi = this.bi,
bj = this.bj,
ri = this.ri,
rj = this.rj,
xi = bi.position,
xj = bj.position;
var vi = bi.velocity,
wi = bi.angularVelocity,
fi = bi.force,
taui = bi.angularForce;
var vj = bj.velocity,
wj = bj.angularVelocity,
fj = bj.force,
tauj = bj.angularForce;
var penetrationVec = this.penetrationVec,
invMassi = bi.invMass,
invMassj = bj.invMass,
invIi = bi.invInertia,
invIj = bj.invInertia,
n = this.ni;
// Caluclate cross products
this.rixn = vec2.crossLength(ri,n);
this.rjxn = vec2.crossLength(rj,n);
// Calculate q = xj+rj -(xi+ri) i.e. the penetration vector
vec2.add(penetrationVec,xj,rj);
vec2.sub(penetrationVec,penetrationVec,xi);
vec2.sub(penetrationVec,penetrationVec,ri);
var Gq = vec2.dot(n,penetrationVec);
// Compute iteration
var GW = vec2.dot(vj,n) - vec2.dot(vi,n) + wj * this.rjxn - wi * this.rixn;
var GiMf = vec2.dot(fj,n)*invMassj - vec2.dot(fi,n)*invMassi + invIj*tauj*this.rjxn - invIi*taui*this.rixn;
var B = - Gq * a - GW * b - h*GiMf;
return B;
};
// Compute C = GMG+eps in the SPOOK equation
var computeC_tmp1 = vec2.create(),
tmpMat1 = mat2.create(),
tmpMat2 = mat2.create();
ContactEquation.prototype.computeC = function(eps){
var bi = this.bi,
bj = this.bj,
n = this.ni,
rixn = this.rixn,
rjxn = this.rjxn,
tmp = computeC_tmp1,
imMat1 = tmpMat1,
imMat2 = tmpMat2;
mat2.identity(imMat1);
mat2.identity(imMat2);
imMat1[0] = imMat1[3] = bi.invMass;
imMat2[0] = imMat2[3] = bj.invMass;
var C = vec2.dot(n,vec2.transformMat2(tmp,n,imMat1)) + vec2.dot(n,vec2.transformMat2(tmp,n,imMat2)) + eps;
//var C = bi.invMass + bj.invMass + eps;
C += bi.invInertia * this.rixn * this.rixn;
C += bj.invInertia * this.rjxn * this.rjxn;
return C;
};
ContactEquation.prototype.computeGWlambda = function(){
var bi = this.bi,
bj = this.bj,
n = this.ni,
dot = vec2.dot;
return dot(n, bj.vlambda) + bj.wlambda * this.rjxn - dot(n, bi.vlambda) - bi.wlambda * this.rixn;
};
var addToWlambda_temp = vec2.create();
ContactEquation.prototype.addToWlambda = function(deltalambda){
var bi = this.bi,
bj = this.bj,
n = this.ni,
temp = addToWlambda_temp,
imMat1 = tmpMat1,
imMat2 = tmpMat2;
mat2.identity(imMat1);
mat2.identity(imMat2);
imMat1[0] = imMat1[3] = bi.invMass;
imMat2[0] = imMat2[3] = bj.invMass;
// Add to linear velocity
//vec2.scale(temp,n,-bi.invMass*deltalambda);
vec2.scale(temp,vec2.transformMat2(temp,n,imMat1),-deltalambda);
vec2.add( bi.vlambda,bi.vlambda, temp );
//vec2.scale(temp,n,bj.invMass*deltalambda);
vec2.scale(temp,vec2.transformMat2(temp,n,imMat2),deltalambda);
vec2.add( bj.vlambda,bj.vlambda, temp);
// Add to angular velocity
bi.wlambda -= bi.invInertia * this.rixn * deltalambda;
bj.wlambda += bj.invInertia * this.rjxn * deltalambda;
};
@@ -0,0 +1,62 @@
var Constraint = require('./Constraint')
, ContactEquation = require('./ContactEquation')
, vec2 = require('../math/vec2')
module.exports = DistanceConstraint;
/**
* Constraint that tries to keep the distance between two bodies constant.
*
* @class DistanceConstraint
* @constructor
* @author schteppe
* @param {Body} bodyA
* @param {Body} bodyB
* @param {number} dist The distance to keep between the bodies.
* @param {number} maxForce
* @extends {Constraint}
*/
function DistanceConstraint(bodyA,bodyB,distance,maxForce){
Constraint.call(this,bodyA,bodyB);
this.distance = distance;
if(typeof(maxForce)==="undefined" ) {
maxForce = 1e6;
}
var normal = new ContactEquation(bodyA,bodyB); // Just in the normal direction
this.equations = [ normal ];
// Make the contact constraint bilateral
this.setMaxForce(maxForce);
}
DistanceConstraint.prototype = new Constraint();
/**
* Update the constraint equations. Should be done if any of the bodies changed position, before solving.
* @method update
*/
DistanceConstraint.prototype.update = function(){
var normal = this.equations[0],
bodyA = this.bodyA,
bodyB = this.bodyB,
distance = this.distance;
vec2.sub(normal.ni, bodyB.position, bodyA.position);
vec2.normalize(normal.ni,normal.ni);
vec2.scale(normal.ri, normal.ni, distance*0.5);
vec2.scale(normal.rj, normal.ni, -distance*0.5);
};
DistanceConstraint.prototype.setMaxForce = function(f){
var normal = this.equations[0];
normal.minForce = -f;
normal.maxForce = f;
};
DistanceConstraint.prototype.getMaxForce = function(f){
var normal = this.equations[0];
return normal.maxForce;
};
@@ -0,0 +1,77 @@
module.exports = Equation;
/**
* Base class for constraint equations.
* @class Equation
* @constructor
* @param {Body} bi First body participating in the equation
* @param {Body} bj Second body participating in the equation
* @param {number} minForce Minimum force to apply. Default: -1e6
* @param {number} maxForce Maximum force to apply. Default: 1e6
*/
function Equation(bi,bj,minForce,maxForce){
/**
* Minimum force to apply when solving
* @property minForce
* @type {Number}
*/
this.minForce = typeof(minForce)=="undefined" ? -1e6 : minForce;
/**
* Max force to apply when solving
* @property maxForce
* @type {Number}
*/
this.maxForce = typeof(maxForce)=="undefined" ? 1e6 : maxForce;
/**
* First body participating in the constraint
* @property bi
* @type {Body}
*/
this.bi = bi;
/**
* Second body participating in the constraint
* @property bj
* @type {Body}
*/
this.bj = bj;
/**
* The stiffness of this equation. Typically chosen to a large number (~1e7), but can be chosen somewhat freely to get a stable simulation.
* @property stiffness
* @type {Number}
*/
this.stiffness = 1e6;
/**
* The number of time steps needed to stabilize the constraint equation. Typically between 3 and 5 time steps.
* @property relaxation
* @type {Number}
*/
this.relaxation = 4;
this.a = 0;
this.b = 0;
this.eps = 0;
this.h = 0;
this.updateSpookParams(1/60);
};
Equation.prototype.constructor = Equation;
/**
* Update SPOOK parameters .a, .b and .eps according to the given time step. See equations 9, 10 and 11 in the <a href="http://www8.cs.umu.se/kurser/5DV058/VT09/lectures/spooknotes.pdf">SPOOK notes</a>.
* @method updateSpookParams
* @param {number} timeStep
*/
Equation.prototype.updateSpookParams = function(timeStep){
var k = this.stiffness,
d = this.relaxation,
h = timeStep;
this.a = 4.0 / (h * (1 + 4 * d));
this.b = (4.0 * d) / (1 + 4 * d);
this.eps = 4.0 / (h * h * k * (1 + 4 * d));
this.h = timeStep;
};
@@ -0,0 +1,180 @@
var mat2 = require('../math/mat2')
, vec2 = require('../math/vec2')
, Equation = require('./Equation')
module.exports = FrictionEquation;
// 3D cross product from glmatrix, until we get this to work...
function cross(out, a, b) {
var ax = a[0], ay = a[1], az = a[2],
bx = b[0], by = b[1], bz = b[2];
out[0] = ay * bz - az * by;
out[1] = az * bx - ax * bz;
out[2] = ax * by - ay * bx;
return out;
};
var dot = vec2.dot;
/**
* Constrains the slipping in a contact along a tangent
*
* @class FrictionEquation
* @constructor
* @param {Body} bi
* @param {Body} bj
* @param {Number} slipForce
* @extends {Equation}
*/
function FrictionEquation(bi,bj,slipForce){
Equation.call(this,bi,bj,-slipForce,slipForce);
/**
* Relative vector from center of body i to the contact point, in world coords.
* @property ri
* @type {Float32Array}
*/
this.ri = vec2.create();
/**
* Relative vector from center of body j to the contact point, in world coords.
* @property rj
* @type {Float32Array}
*/
this.rj = vec2.create();
/**
* Tangent vector that the friction force will act along, in world coords.
* @property t
* @type {Float32Array}
*/
this.t = vec2.create();
this.rixt = 0;
this.rjxt = 0;
};
FrictionEquation.prototype = new Equation();
FrictionEquation.prototype.constructor = FrictionEquation;
/**
* Set the slipping condition for the constraint. The friction force cannot be
* larger than this value.
* @method setSlipForce
* @param {Number} slipForce
*/
FrictionEquation.prototype.setSlipForce = function(slipForce){
this.maxForce = slipForce;
this.minForce = -slipForce;
};
var rixtVec = [0,0,0];
var rjxtVec = [0,0,0];
var ri3 = [0,0,0];
var rj3 = [0,0,0];
var t3 = [0,0,0];
FrictionEquation.prototype.computeB = function(a,b,h){
var a = this.a,
b = this.b,
bi = this.bi,
bj = this.bj,
ri = this.ri,
rj = this.rj,
t = this.t;
// Caluclate cross products
ri3[0] = ri[0];
ri3[1] = ri[1];
rj3[0] = rj[0];
rj3[1] = rj[1];
t3[0] = t[0];
t3[1] = t[1];
cross(rixtVec, ri3, t3);//ri.cross(t,rixt);
cross(rjxtVec, rj3, t3);//rj.cross(t,rjxt);
this.rixt = rixtVec[2];
this.rjxt = rjxtVec[2];
var GW = -dot(bi.velocity,t) + dot(bj.velocity,t) - this.rixt*bi.angularVelocity + this.rjxt*bj.angularVelocity; // eq. 40
var GiMf = -dot(bi.force,t)*bi.invMass +dot(bj.force,t)*bj.invMass -this.rixt*bi.invInertia*bi.angularForce + this.rjxt*bj.invInertia*bj.angularForce;
var B = /* - Gq * a */ - GW * b - h*GiMf;
return B;
};
// Compute C = G * iM * G' + eps
//
// G*iM*G' =
//
// [ iM1 ] [-t ]
// [-t (-ri x t) t (rj x t)] * [ iI1 ] [-ri x t]
// [ iM2 ] [t ]
// [ iI2 ] [rj x t ]
//
// = (-t)*iM1*(-t) + (-ri x t)*iI1*(-ri x t) + t*iM2*t + (rj x t)*iI2*(rj x t)
//
// = t*iM1*t + (ri x t)*iI1*(ri x t) + t*iM2*t + (rj x t)*iI2*(rj x t)
//
var computeC_tmp1 = vec2.create(),
tmpMat1 = mat2.create(),
tmpMat2 = mat2.create();
FrictionEquation.prototype.computeC = function(eps){
var bi = this.bi,
bj = this.bj,
t = this.t,
C = 0.0,
tmp = computeC_tmp1,
imMat1 = tmpMat1,
imMat2 = tmpMat2,
dot = vec2.dot;
mat2.identity(imMat1);
mat2.identity(imMat2);
imMat1[0] = imMat1[3] = bi.invMass;
imMat2[0] = imMat2[3] = bj.invMass;
C = dot(t,vec2.transformMat2(tmp,t,imMat1)) + dot(t,vec2.transformMat2(tmp,t,imMat2)) + eps;
//C = bi.invMass + bj.invMass + eps;
C += bi.invInertia * this.rixt * this.rixt;
C += bj.invInertia * this.rjxt * this.rjxt;
return C;
};
FrictionEquation.prototype.computeGWlambda = function(){
var bi = this.bi,
bj = this.bj,
t = this.t,
dot = vec2.dot;
return dot(t, bj.vlambda) + bj.wlambda * this.rjxt - bi.wlambda * this.rixt - dot(t, bi.vlambda);
};
var FrictionEquation_addToWlambda_tmp = vec2.create();
FrictionEquation.prototype.addToWlambda = function(deltalambda){
var bi = this.bi,
bj = this.bj,
t = this.t,
tmp = FrictionEquation_addToWlambda_tmp,
imMat1 = tmpMat1,
imMat2 = tmpMat2;
mat2.identity(imMat1);
mat2.identity(imMat2);
imMat1[0] = imMat1[3] = bi.invMass;
imMat2[0] = imMat2[3] = bj.invMass;
vec2.scale(tmp,vec2.transformMat2(tmp,t,imMat1),-deltalambda);
//vec2.scale(tmp, t, -bi.invMass * deltalambda); //t.mult(invMassi * deltalambda, tmp);
vec2.add(bi.vlambda, bi.vlambda, tmp); //bi.vlambda.vsub(tmp,bi.vlambda);
vec2.scale(tmp,vec2.transformMat2(tmp,t,imMat2),deltalambda);
//vec2.scale(tmp, t, bj.invMass * deltalambda); //t.mult(invMassj * deltalambda, tmp);
vec2.add(bj.vlambda, bj.vlambda, tmp); //bj.vlambda.vadd(tmp,bj.vlambda);
bi.wlambda -= bi.invInertia * this.rixt * deltalambda;
bj.wlambda += bj.invInertia * this.rjxt * deltalambda;
};
@@ -0,0 +1,94 @@
var Constraint = require('./Constraint')
, ContactEquation = require('./ContactEquation')
, RotationalVelocityEquation = require('./RotationalVelocityEquation')
, vec2 = require('../math/vec2')
module.exports = PointToPointConstraint;
/**
* Connects two bodies at given offset points
* @class PointToPointConstraint
* @constructor
* @author schteppe
* @param {Body} bodyA
* @param {Float32Array} pivotA The point relative to the center of mass of bodyA which bodyA is constrained to.
* @param {Body} bodyB Body that will be constrained in a similar way to the same point as bodyA. We will therefore get sort of a link between bodyA and bodyB. If not specified, bodyA will be constrained to a static point.
* @param {Float32Array} pivotB See pivotA.
* @param {Number} maxForce The maximum force that should be applied to constrain the bodies.
* @extends {Constraint}
* @todo Ability to specify world points
*/
function PointToPointConstraint(bodyA, pivotA, bodyB, pivotB, maxForce){
Constraint.call(this,bodyA,bodyB);
maxForce = typeof(maxForce)!="undefined" ? maxForce : 1e7;
this.pivotA = pivotA;
this.pivotB = pivotB;
// Equations to be fed to the solver
var eqs = this.equations = [
new ContactEquation(bodyA,bodyB), // Normal
new ContactEquation(bodyA,bodyB), // Tangent
];
var normal = eqs[0];
var tangent = eqs[1];
tangent.minForce = normal.minForce = -maxForce;
tangent.maxForce = normal.maxForce = maxForce;
this.motorEquation = null;
}
PointToPointConstraint.prototype = new Constraint();
PointToPointConstraint.prototype.update = function(){
var bodyA = this.bodyA,
bodyB = this.bodyB,
pivotA = this.pivotA,
pivotB = this.pivotB,
eqs = this.equations,
normal = eqs[0],
tangent= eqs[1];
vec2.subtract(normal.ni, bodyB.position, bodyA.position);
vec2.normalize(normal.ni,normal.ni);
vec2.rotate(normal.ri, pivotA, bodyA.angle);
vec2.rotate(normal.rj, pivotB, bodyB.angle);
vec2.rotate(tangent.ni, normal.ni, Math.PI / 2);
vec2.copy(tangent.ri, normal.ri);
vec2.copy(tangent.rj, normal.rj);
};
/**
* Enable the rotational motor
* @method enableMotor
*/
PointToPointConstraint.prototype.enableMotor = function(){
if(this.motorEquation) return;
this.motorEquation = new RotationalVelocityEquation(this.bodyA,this.bodyB);
this.equations.push(this.motorEquation);
};
/**
* Disable the rotational motor
* @method disableMotor
*/
PointToPointConstraint.prototype.disableMotor = function(){
if(!this.motorEquation) return;
var i = this.equations.indexOf(this.motorEquation);
this.motorEquation = null;
this.equations.splice(i,1);
};
/**
* Set the speed of the rotational constraint motor
* @method setMotorSpeed
* @param {Number} speed
*/
PointToPointConstraint.prototype.setMotorSpeed = function(speed){
if(!this.motorEquation) return;
var i = this.equations.indexOf(this.motorEquation);
this.equations[i].relativeVelocity = speed;
};
@@ -0,0 +1,83 @@
var Constraint = require('./Constraint')
, ContactEquation = require('./ContactEquation')
, vec2 = require('../math/vec2')
module.exports = PrismaticConstraint;
/**
* Constraint that only allows translation along a line between the bodies, no rotation
*
* @class PrismaticConstraint
* @constructor
* @author schteppe
* @param {Body} bodyA
* @param {Body} bodyB
* @param {Object} options
* @param {Number} options.maxForce
* @param {Array} options.worldAxis
* @param {Array} options.localAxisA
* @param {Array} options.localAxisB
* @extends {Constraint}
*/
function PrismaticConstraint(bodyA,bodyB,options){
options = options || {};
Constraint.call(this,bodyA,bodyB);
var maxForce = this.maxForce = typeof(options.maxForce)==="undefined" ? options.maxForce : 1e6;
// Equations to be fed to the solver
var eqs = this.equations = [
new ContactEquation(bodyA,bodyB), // Tangent for bodyA
new ContactEquation(bodyB,bodyA), // Tangent for bodyB
];
var tangentA = eqs[0],
tangentB = eqs[1];
tangentA.minForce = tangentB.minForce = -maxForce;
tangentA.maxForce = tangentB.maxForce = maxForce;
var worldAxis = vec2.create();
if(options.worldAxis){
vec2.copy(worldAxis, options.worldAxis);
} else {
vec2.sub(worldAxis, bodyB.position, bodyA.position);
}
vec2.normalize(worldAxis,worldAxis);
// Axis that is local in each body
this.localAxisA = vec2.create();
this.localAxisB = vec2.create();
if(options.localAxisA) vec2.copy(this.localAxisA, options.localAxisA);
else vec2.rotate(this.localAxisA, worldAxis, -bodyA.angle);
if(options.localAxisB) vec2.copy(this.localAxisB, options.localAxisB);
else vec2.rotate(this.localAxisB, worldAxis, -bodyB.angle);
}
PrismaticConstraint.prototype = new Constraint();
/**
* Update the constraint equations. Should be done if any of the bodies changed position, before solving.
* @method update
*/
PrismaticConstraint.prototype.update = function(){
var tangentA = this.equations[0],
tangentB = this.equations[1],
bodyA = this.bodyA,
bodyB = this.bodyB;
// Get tangent directions
vec2.rotate(tangentA.ni, this.localAxisA, bodyA.angle - Math.PI/2);
vec2.rotate(tangentB.ni, this.localAxisB, bodyB.angle + Math.PI/2);
// Get distance vector
var dist = vec2.create();
vec2.sub(dist, bodyB.position, bodyA.position);
vec2.scale(tangentA.ri, tangentA.ni, -vec2.dot(tangentA.ni, dist));
vec2.scale(tangentB.ri, tangentB.ni, vec2.dot(tangentB.ni, dist));
vec2.add(tangentA.rj, tangentA.ri, dist);
vec2.sub(tangentB.rj, tangentB.ri, dist);
vec2.set(tangentA.ri, 0, 0);
vec2.set(tangentB.ri, 0, 0);
};
@@ -0,0 +1,70 @@
var Equation = require("./Equation"),
vec2 = require('../math/vec2');
module.exports = RotationalVelocityEquation;
/**
* Syncs rotational velocity of two bodies, or sets a relative velocity (motor).
*
* @class RotationalVelocityEquation
* @constructor
* @extends Equation
* @param {Body} bi
* @param {Body} bj
*/
function RotationalVelocityEquation(bi,bj){
Equation.call(this,bi,bj,-1e6,1e6);
this.relativeVelocity = 1;
this.ratio = 1;
};
RotationalVelocityEquation.prototype = new Equation();
RotationalVelocityEquation.prototype.constructor = RotationalVelocityEquation;
RotationalVelocityEquation.prototype.computeB = function(a,b,h){
var bi = this.bi,
bj = this.bj,
vi = bi.velocity,
wi = bi.angularVelocity,
taui = bi.angularForce,
vj = bj.velocity,
wj = bj.angularVelocity,
tauj = bj.angularForce,
invIi = bi.invInertia,
invIj = bj.invInertia,
Gq = 0,
GW = this.ratio * wj - wi + this.relativeVelocity,
GiMf = invIj*tauj - invIi*taui;
var B = - Gq * a - GW * b - h*GiMf;
return B;
};
// Compute C = GMG+eps in the SPOOK equation
RotationalVelocityEquation.prototype.computeC = function(eps){
var bi = this.bi,
bj = this.bj;
var C = bi.invInertia + bj.invInertia + eps;
return C;
};
var computeGWlambda_ulambda = vec2.create();
RotationalVelocityEquation.prototype.computeGWlambda = function(){
var bi = this.bi,
bj = this.bj;
var GWlambda = bj.wlambda - bi.wlambda;
return GWlambda;
};
var addToWlambda_temp = vec2.create();
RotationalVelocityEquation.prototype.addToWlambda = function(deltalambda){
var bi = this.bi,
bj = this.bj;
// Add to angular velocity
bi.wlambda -= bi.invInertia * deltalambda;
bj.wlambda += bj.invInertia * deltalambda;
};
@@ -0,0 +1,84 @@
/**
* Base class for objects that dispatches events.
* @class EventEmitter
* @constructor
*/
var EventEmitter = function () {}
module.exports = EventEmitter;
EventEmitter.prototype = {
constructor: EventEmitter,
/**
* Add an event listener
* @method on
* @param {String} type
* @param {Function} listener
* @return {EventEmitter} The self object, for chainability.
*/
on: function ( type, listener ) {
if ( this._listeners === undefined ) this._listeners = {};
var listeners = this._listeners;
if ( listeners[ type ] === undefined ) {
listeners[ type ] = [];
}
if ( listeners[ type ].indexOf( listener ) === - 1 ) {
listeners[ type ].push( listener );
}
return this;
},
/**
* Check if an event listener is added
* @method has
* @param {String} type
* @param {Function} listener
* @return {Boolean}
*/
has: function ( type, listener ) {
if ( this._listeners === undefined ) return false;
var listeners = this._listeners;
if ( listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1 ) {
return true;
}
return false;
},
/**
* Remove an event listener
* @method off
* @param {String} type
* @param {Function} listener
* @return {EventEmitter} The self object, for chainability.
*/
off: function ( type, listener ) {
if ( this._listeners === undefined ) return;
var listeners = this._listeners;
var index = listeners[ type ].indexOf( listener );
if ( index !== - 1 ) {
listeners[ type ].splice( index, 1 );
}
return this;
},
/**
* Emit an event.
* @method emit
* @param {Object} event
* @param {String} event.type
* @return {EventEmitter} The self object, for chainability.
*/
emit: function ( event ) {
if ( this._listeners === undefined ) return;
var listeners = this._listeners;
var listenerArray = listeners[ event.type ];
if ( listenerArray !== undefined ) {
event.target = this;
for ( var i = 0, l = listenerArray.length; i < l; i ++ ) {
listenerArray[ i ].call( this, event );
}
}
return this;
}
};
-130
View File
@@ -1,130 +0,0 @@
/*
* Copyright (c) 2012 Ju Hyung Lee
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
//-------------------------------------------------------------------------------------------------
// Angle Joint
//
// C = a2 - a1 - refAngle
// Cdot = w2 - w1
// J = [0, -1, 0, 1]
//
// impulse = JT * lambda = [ 0, -lambda, 0, lambda ]
//-------------------------------------------------------------------------------------------------
AngleJoint = function(body1, body2) {
Joint.call(this, Joint.TYPE_ANGLE, body1, body2, true);
this.anchor1 = new vec2(0, 0);
this.anchor2 = new vec2(0, 0);
// Initial angle difference
this.refAngle = body2.a - body1.a;
// Accumulated lambda for angular velocity constraint
this.lambda_acc = 0;
}
AngleJoint.prototype = new Joint;
AngleJoint.prototype.constructor = AngleJoint;
AngleJoint.prototype.setWorldAnchor1 = function(anchor1) {
this.anchor1 = new vec2(0, 0);
}
AngleJoint.prototype.setWorldAnchor2 = function(anchor2) {
this.anchor2 = new vec2(0, 0);
}
AngleJoint.prototype.serialize = function() {
return {
"type": "AngleJoint",
"body1": this.body1.id,
"body2": this.body2.id,
"collideConnected": this.collideConnected
};
}
AngleJoint.prototype.initSolver = function(dt, warmStarting) {
var body1 = this.body1;
var body2 = this.body2;
// Max impulse
this.maxImpulse = this.maxForce * dt;
// invEM = J * invM * JT
var em_inv = body1.i_inv + body2.i_inv;
this.em = em_inv == 0 ? 0 : 1 / em_inv;
if (warmStarting) {
// Apply cached constraint impulses
// V += JT * lambda * invM
body1.w -= this.lambda_acc * body1.i_inv;
body2.w += this.lambda_acc * body2.i_inv;
}
else {
this.lambda_acc = 0;
}
}
AngleJoint.prototype.solveVelocityConstraints = function() {
var body1 = this.body1;
var body2 = this.body2;
// Compute lambda for velocity constraint
// Solve J * invM * JT * lambda = -J * V
var cdot = body2.w - body1.w;
var lambda = -this.em * cdot;
// Accumulate lambda
this.lambda_acc += lambda;
// Apply constraint impulses
// V += JT * lambda * invM
body1.w -= lambda * body1.i_inv;
body2.w += lambda * body2.i_inv;
}
AngleJoint.prototype.solvePositionConstraints = function() {
var body1 = this.body1;
var body2 = this.body2;
// Position (angle) constraint
var c = body2.a - body1.a - this.refAngle;
var correction = Math.clamp(c, -Joint.MAX_ANGULAR_CORRECTION, Joint.MAX_ANGULAR_CORRECTION);
// Compute lambda for position (angle) constraint
// Solve J * invM * JT * lambda = -C / dt
var lambda_dt = this.em * (-correction);
// Apply constraint impulses
// impulse = JT * lambda
// X += impulse * invM * dt
body1.a -= lambda_dt * body1.i_inv;
body2.a += lambda_dt * body2.i_inv;
return Math.abs(c) < Joint.ANGULAR_SLOP;
}
AngleJoint.prototype.getReactionForce = function(dt_inv) {
return vec2.zero;
}
AngleJoint.prototype.getReactionTorque = function(dt_inv) {
return this.lambda_acc * dt_inv;
}
-522
View File
@@ -1,522 +0,0 @@
/*
* Copyright (c) 2012 Ju Hyung Lee
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
//-------------------------------------------------------------------------------------------------
// Distance Joint
//
// d = p2 - p1
// u = d / norm(d)
// C = norm(d) - l
// C = sqrt(dot(d, d)) - l
// Cdot = dot(u, v2 + cross(w2, r2) - v1 - cross(w1, r1))
// = -dot(u, v1) - dot(w1, cross(r1, u)) + dot(u, v2) + dot(w2, cross(r2, u))
// J = [ -u, -cross(r1, u), u, cross(r2, u) ]
//
// impulse = JT * lambda = [ -u * lambda, -cross(r1, u) * lambda, u * lambda, cross(r1, u) * lambda ]
//-------------------------------------------------------------------------------------------------
DistanceJoint = function(body1, body2, anchor1, anchor2) {
Joint.call(this, Joint.TYPE_DISTANCE, body1, body2, true);
// Local anchor points
this.anchor1 = this.body1.getLocalPoint(anchor1);
this.anchor2 = this.body2.getLocalPoint(anchor2);
// Rest length
this.restLength = vec2.dist(anchor1, anchor2);
// Soft constraint coefficients
this.gamma = 0;
this.beta_c = 0;
// Spring coefficients
this.frequencyHz = 0;
this.dampingRatio = 0;
// Accumulated impulse
this.lambda_acc = 0;
}
DistanceJoint.prototype = new Joint;
DistanceJoint.prototype.constructor = DistanceJoint;
DistanceJoint.prototype.setWorldAnchor1 = function(anchor1) {
this.anchor1 = this.body1.getLocalPoint(anchor1);
this.restLength = vec2.dist(anchor1, this.getWorldAnchor2());
}
DistanceJoint.prototype.setWorldAnchor2 = function(anchor2) {
this.anchor2 = this.body2.getLocalPoint(anchor2);
this.restLength = vec2.dist(anchor2, this.getWorldAnchor1());
}
DistanceJoint.prototype.serialize = function() {
return {
"type": "DistanceJoint",
"body1": this.body1.id,
"body2": this.body2.id,
"anchor1": this.body1.getWorldPoint(this.anchor1),
"anchor2": this.body2.getWorldPoint(this.anchor2),
"collideConnected": this.collideConnected,
"maxForce": this.maxForce,
"breakable": this.breakable,
"frequencyHz": this.frequencyHz,
"dampingRatio": this.dampingRatio
};
}
DistanceJoint.prototype.setSpringFrequencyHz = function(frequencyHz) {
// NOTE: frequencyHz should be limited to under 4 times time steps
this.frequencyHz = frequencyHz;
}
DistanceJoint.prototype.setSpringDampingRatio = function(dampingRatio) {
this.dampingRatio = dampingRatio;
}
DistanceJoint.prototype.initSolver = function(dt, warmStarting) {
var body1 = this.body1;
var body2 = this.body2;
// Max impulse
this.maxImpulse = this.maxForce * dt;
// Transformed r1, r2
this.r1 = body1.xf.rotate(vec2.sub(this.anchor1, body1.centroid));
this.r2 = body2.xf.rotate(vec2.sub(this.anchor2, body2.centroid));
// Delta vector between two world anchors
var d = vec2.sub(vec2.add(body2.p, this.r2), vec2.add(body1.p, this.r1));
// Distance between two anchors
var dist = d.length();
// Unit delta vector
if (dist > Joint.LINEAR_SLOP) {
this.u = vec2.scale(d, 1 / dist);
}
else {
this.u = vec2.zero;
}
// s1, s2
this.s1 = vec2.cross(this.r1, this.u);
this.s2 = vec2.cross(this.r2, this.u);
// invEM = J * invM * JT
var em_inv = body1.m_inv + body2.m_inv + body1.i_inv * this.s1 * this.s1 + body2.i_inv * this.s2 * this.s2;
this.em = em_inv == 0 ? 0 : 1 / em_inv;
// Compute soft constraint parameters
if (this.frequencyHz > 0) {
// Frequency
var omega = 2 * Math.PI * this.frequencyHz;
// Spring stiffness
var k = this.em * omega * omega;
// Damping coefficient
var c = this.em * 2 * this.dampingRatio * omega;
// Soft constraint formulas
// gamma and beta are divided by dt to reduce computation
this.gamma = (c + k * dt) * dt;
this.gamma = this.gamma == 0 ? 0 : 1 / this.gamma;
var beta = dt * k * this.gamma;
// Position constraint
var pc = dist - this.restLength;
this.beta_c = beta * pc;
// invEM = invEM + gamma * I (to reduce calculation)
em_inv = em_inv + this.gamma;
this.em = em_inv == 0 ? 0 : 1 / em_inv;
}
else {
this.gamma = 0;
this.beta_c = 0;
}
if (warmStarting) {
// linearImpulse = JT * lambda
var impulse = vec2.scale(this.u, this.lambda_acc);
// Apply cached constraint impulses
// V += JT * lambda * invM
body1.v.mad(impulse, -body1.m_inv);
body1.w -= this.s1 * this.lambda_acc * body1.i_inv;
body2.v.mad(impulse, body2.m_inv);
body2.w += this.s2 * this.lambda_acc * body2.i_inv;
}
else {
this.lambda_acc = 0;
}
}
DistanceJoint.prototype.solveVelocityConstraints = function() {
var body1 = this.body1;
var body2 = this.body2;
// Compute lambda for velocity constraint
// Solve J * invM * JT * lambda = -(J * V + beta * C + gamma * (lambda_acc + lambda))
var cdot = this.u.dot(vec2.sub(body2.v, body1.v)) + this.s2 * body2.w - this.s1 * body1.w;
var soft = this.beta_c + this.gamma * this.lambda_acc;
var lambda = -this.em * (cdot + soft);
// Accumulate lambda
this.lambda_acc += lambda;
// linearImpulse = JT * lambda
var impulse = vec2.scale(this.u, lambda);
// Apply constraint impulses
// V += JT * lambda * invM
body1.v.mad(impulse, -body1.m_inv);
body1.w -= this.s1 * lambda * body1.i_inv;
body2.v.mad(impulse, body2.m_inv);
body2.w += this.s2 * lambda * body2.i_inv;
}
DistanceJoint.prototype.solvePositionConstraints = function() {
// There is no position correction for soft constraints
if (this.frequencyHz > 0) {
return true;
}
var body1 = this.body1;
var body2 = this.body2;
// Transformed r1, r2
var r1 = vec2.rotate(vec2.sub(this.anchor1, body1.centroid), body1.a);
var r2 = vec2.rotate(vec2.sub(this.anchor2, body2.centroid), body2.a);
// Delta vector between two anchors
var d = vec2.sub(vec2.add(body2.p, r2), vec2.add(body1.p, r1));
// Distance between two anchors
var dist = d.length();
// Unit delta vector
var u = vec2.scale(d, 1 / dist);
// Position constraint
var c = dist - this.restLength;
var correction = Math.clamp(c, -Joint.MAX_LINEAR_CORRECTION, Joint.MAX_LINEAR_CORRECTION);
// Compute lambda for correction
// Solve J * invM * JT * lambda = -C / dt
var s1 = vec2.cross(r1, u);
var s2 = vec2.cross(r2, u);
var em_inv = body1.m_inv + body2.m_inv + body1.i_inv * s1 * s1 + body2.i_inv * s2 * s2;
var lambda_dt = em_inv == 0 ? 0 : -correction / em_inv;
// Apply constraint impulses
// impulse = JT * lambda
// X += impulse * invM * dt
var impulse_dt = vec2.scale(u, lambda_dt);
body1.p.mad(impulse_dt, -body1.m_inv);
body1.a -= s1 * lambda_dt * body1.i_inv;
body2.p.mad(impulse_dt, body2.m_inv);
body2.a += s2 * lambda_dt * body2.i_inv;
return Math.abs(c) < Joint.LINEAR_SLOP;
}
DistanceJoint.prototype.getReactionForce = function(dt_inv) {
return vec2.scale(this.u, this.lambda_acc * dt_inv);
}
DistanceJoint.prototype.getReactionTorque = function(dt_inv) {
return 0;
}
/*
//------------------------------------------
// MaxDistance Joint
//------------------------------------------
MaxDistanceJoint = function(body1, body2, anchor1, anchor2, minDist, maxDist) {
Joint.call(this, body1, body2, true);
// Local anchor points
this.anchor1 = body1.getLocalPoint(anchor1);
this.anchor2 = body2.getLocalPoint(anchor2);
this.minDist = minDist || 0;
this.maxDist = maxDist;
if (maxDist == undefined) {
var p1 = vec2.add(vec2.rotate(vec2.sub(this.anchor1, body1.centroid), body1.a), body1.p);
var p2 = vec2.add(vec2.rotate(vec2.sub(this.anchor2, body2.centroid), body2.a), body2.p);
this.maxDist = vec2.dist(p1, p2);
}
// accumulated impulse
this.lambda_acc = 0;
}
MaxDistanceJoint.prototype = new Joint;
MaxDistanceJoint.prototype.constructor = MaxDistanceJoint;
MaxDistanceJoint.prototype.initSolver = function(dt, warmStarting) {
var body1 = this.body1;
var body2 = this.body2;
// max impulse
this.maxImpulse = this.maxForce * dt;
// transformed r1, r2
this.r1 = body1.xf.rotate(vec2.sub(this.anchor1, body1.centroid));
this.r2 = body2.xf.rotate(vec2.sub(this.anchor2, body2.centroid));
// delta vector between two anchors
var d = vec2.sub(vec2.add(body2.p, this.r2), vec2.add(body1.p, this.r1));
// distance between two anchors
var dist = d.length();
// unit delta vector
this.u = vec2.scale(d, 1 / dist);
// s1, s2
this.s1 = vec2.cross(this.r1, this.u);
this.s2 = vec2.cross(this.r2, this.u);
// invEM = J * invM * JT
var em_inv = body1.m_inv + body2.m_inv + body1.i_inv * this.s1 * this.s1 + body2.i_inv * this.s2 * this.s2;
this.em = em_inv == 0 ? 0 : 1 / em_inv;
// initial error
this.initial_err = 0;
if (dist < this.minDist) {
this.initial_err = dist - this.minDist;
}
else if (dist > this.maxDist) {
this.initial_err = dist - this.maxDist;
}
if (this.initial_err == 0) {
this.lambda_acc = 0;
}
if (warmStarting) {
// apply cached impulses
// V += JT * lambda
var impulse = vec2.scale(this.u, this.lambda_acc);
body1.v.mad(impulse, -body1.m_inv);
body1.w -= this.s1 * this.lambda_acc * body1.i_inv;
body2.v.mad(impulse, body2.m_inv);
body2.w += this.s2 * this.lambda_acc * body2.i_inv;
}
else {
this.lambda_acc = 0;
}
}
MaxDistanceJoint.prototype.solveVelocityConstraints = function() {
if (this.initial_err == 0)
return;
var body1 = this.body1;
var body2 = this.body2;
// compute lambda for velocity constraint
// solve J * invM * JT * lambda = -J * V
var cdot = this.u.dot(vec2.sub(body2.v, body1.v)) + this.s2 * body2.w - this.s1 * body1.w;
var lambda = -this.em * cdot;
// accumulate lambda for velocity constraint
this.lambda_acc += lambda;
// apply impulses
// V += JT * lambda
var impulse = vec2.scale(this.u, lambda);
body1.v.mad(impulse, -body1.m_inv);
body1.w -= this.s1 * lambda * body1.i_inv;
body2.v.mad(impulse, body2.m_inv);
body2.w += this.s2 * lambda * body2.i_inv;
}
MaxDistanceJoint.prototype.solvePositionConstraints = function() {
if (this.initial_err == 0)
return;
var body1 = this.body1;
var body2 = this.body2;
// transformed r1, r2
var r1 = vec2.rotate(vec2.sub(this.anchor1, body1.centroid), body1.a);
var r2 = vec2.rotate(vec2.sub(this.anchor2, body2.centroid), body2.a);
// World Center points
var pc1 = vec2.add(body1.p, body1.centroid);
var pc2 = vec2.add(body2.p, body2.centroid);
// delta vector between two anchors
var d = vec2.sub(vec2.add(pc2, r2), vec2.add(pc1, r1));
// distance between two anchors
var dist = d.length();
// unit delta vector
u = vec2.scale(d, 1 / dist);
// position constraint
var c = 0;
if (dist < this.minDist) {
c = dist - this.minDist;
}
else if (dist > this.maxDist) {
c = dist - this.maxDist;
}
var correction = Math.clamp(c, -Joint.MAX_LINEAR_CORRECTION, Joint.MAX_LINEAR_CORRECTION);
// compute lambda for position constraint
// solve J * invM * JT * lambda = -C / dt
var s1 = vec2.cross(r1, u);
var s2 = vec2.cross(r2, u);
var em_inv = body1.m_inv + body2.m_inv + body1.i_inv * s1 * s1 + body2.i_inv * s2 * s2;
var lambda_dt = em_inv == 0 ? 0 : -correction / em_inv;
// apply impulses
// X += JT * lambda * dt
var impulse_dt = vec2.scale(u, lambda_dt);
body1.p.mad(impulse_dt, -body1.m_inv);
body1.a -= s1 * lambda_dt * body1.i_inv;
body2.p.mad(impulse_dt, body2.m_inv);
body2.a += s2 * lambda_dt * body2.i_inv;
return Math.abs(c) < Joint.LINEAR_SLOP;
}
MaxDistanceJoint.prototype.getReactionForce = function(dt_inv) {
return vec2.scale(this.u, this.lambda_acc * dt_inv);
}
MaxDistanceJoint.prototype.getReactionTorque = function(dt_inv) {
return 0;
}
//------------------------------------------
// Damped Spring (Deprecated)
//------------------------------------------
SpringJoint = function(body1, body2, anchor1, anchor2, restLength, stiffness, damping) {
Joint.call(this, body1, body2, true);
// local anchor points
this.anchor1 = anchor1;
this.anchor2 = anchor2;
this.restLength = restLength;
this.stiffness = stiffness;
this.damping = damping;
}
SpringJoint.prototype = new Joint;
SpringJoint.prototype.constructor = SpringJoint;
SpringJoint.prototype.initSolver = function(dt, warmStarting) {
var body1 = this.body1;
var body2 = this.body2;
// transformed r1, r2
this.r1 = body1.xf.rotate(vec2.sub(this.anchor1, body2.centroid));
this.r2 = body2.xf.rotate(vec2.sub(this.anchor2, body2.centroid));
var d = vec2.sub(vec2.add(body2.p, this.r2), vec2.add(body1.p, this.r1));
var dist = d.length();
this.u = vec2.scale(d, 1 / dist);
// s1, s2
this.s1 = vec2.cross(this.r1, this.u);
this.s2 = vec2.cross(this.r2, this.u);
// invEM = J * invM * JT
var em_inv = body1.m_inv + body2.m_inv + body1.i_inv * this.s1 * this.s1 + body2.i_inv * this.s2 * this.s2;
this.em = em_inv == 0 ? 0 : 1 / em_inv;
//
this.target_rnv = 0;
this.v_coeff = 1.0 - Math.exp(-this.damping * dt * em_inv);
// apply spring force
var spring_f = (this.restLength - dist) * this.stiffness;
this.spring_impulse = spring_f * dt;
// apply impulses
// V += JT * lambda
var impulse = vec2.scale(this.u, this.spring_impulse);
body1.v.mad(impulse, -body1.m_inv);
body1.w -= this.s1 * this.spring_impulse * body1.i_inv;
body2.v.mad(impulse, body2.m_inv);
body2.w += this.s2 * this.spring_impulse * body2.i_inv;
}
SpringJoint.prototype.solveVelocityConstraints = function() {
var body1 = this.body1;
var body2 = this.body2;
// compute lambda for velocity constraint
// solve J * invM * JT * lambda = -J * V
var cdot = this.u.dot(vec2.sub(body2.v, body1.v)) + this.s2 * body2.w - this.s1 * body1.w;
var rnv = cdot + this.target_rnv;
// compute velocity loss from drag
var v_damp = rnv * this.v_coeff;
this.target_rnv = -rnv + v_damp;
var lambda = -this.em * v_damp;
// apply impulses
// V += JT * lambda
var impulse = vec2.scale(this.u, lambda);
body1.v.mad(impulse, -body1.m_inv);
body1.w -= this.s1 * lambda * body1.i_inv;
body2.v.mad(impulse, body2.m_inv);
body2.w += this.s2 * lambda * body2.i_inv;
}
SpringJoint.prototype.solvePositionConstraints = function() {
return true;
}
SpringJoint.prototype.getReactionForce = function(dt_inv) {
return vec2.scale(this.u, this.spring_impulse * dt_inv);
}
SpringJoint.prototype.getReactionTorque = function(dt_inv) {
return 0;
}*/
-150
View File
@@ -1,150 +0,0 @@
/*
* Copyright (c) 2012 Ju Hyung Lee
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
//-------------------------------------------------------------------------------------------------
// Mouse Joint
//
// p = attached point, m = mouse point (constant)
// C = p - m
// Cdot = v + cross(w, r)
// J = [ I, -skew(r) ]
//
// impulse = JT * lambda = [ lambda, cross(r2, lambda) ]
//-------------------------------------------------------------------------------------------------
MouseJoint = function(mouseBody, body, anchor) {
if (arguments.length == 0)
return;
Joint.call(this, Joint.TYPE_MOUSE, mouseBody, body, true);
// Local anchor points
this.anchor1 = this.body1.getLocalPoint(anchor);
this.anchor2 = this.body2.getLocalPoint(anchor);
// Soft constraint coefficients
this.gamma = 0;
this.beta_c = 0;
// Spring coefficients
this.frequencyHz = 5;
this.dampingRatio = 0.9;
// Accumulated impulse
this.lambda_acc = new vec2(0, 0);
}
MouseJoint.prototype = new Joint;
MouseJoint.prototype.constructor = MouseJoint;
MouseJoint.prototype.setSpringFrequencyHz = function(frequencyHz) {
this.frequencyHz = frequencyHz;
}
MouseJoint.prototype.setSpringDampingRatio = function(dampingRatio) {
this.dampingRatio = dampingRatio;
}
MouseJoint.prototype.initSolver = function(dt, warmStarting) {
var body1 = this.body1;
var body2 = this.body2;
// Max impulse
this.maxImpulse = this.maxForce * dt;
// Frequency
var omega = 2 * Math.PI * this.frequencyHz;
// Spring stiffness
var k = body2.m * (omega * omega);
// Damping coefficient
var d = body2.m * 2 * this.dampingRatio * omega;
// Soft constraint formulas
// gamma and beta are divided by dt to reduce computation
this.gamma = (d + k * dt) * dt;
this.gamma = this.gamma == 0 ? 0 : 1 / this.gamma;
var beta = dt * k * this.gamma;
// Transformed r
this.r2 = body2.xf.rotate(vec2.sub(this.anchor2, body2.centroid));
// invEM = J * invM * JT
var r2 = this.r2;
var r2y_i = r2.y * body2.i_inv;
var k11 = body2.m_inv + r2.y * r2y_i + this.gamma;
var k12 = -r2.x * r2y_i;
var k22 = body2.m_inv + r2.x * r2.x * body2.i_inv + this.gamma;
this.em_inv = new mat2(k11, k12, k12, k22);
// Position constraint
var c = vec2.sub(vec2.add(body2.p, this.r2), body1.p);
this.beta_c = vec2.scale(c, beta);
body2.w *= 0.98;
if (warmStarting) {
// Apply cached constraint impulse
// V += JT * lambda * invM
body2.v.mad(this.lambda_acc, body2.m_inv);
body2.w += vec2.cross(this.r2, this.lambda_acc) * body2.i_inv;
}
else {
this.lambda_acc.set(0, 0);
}
}
MouseJoint.prototype.solveVelocityConstraints = function() {
var body2 = this.body2;
// Compute lambda for velocity constraint
// Solve J * invM * JT * lambda = -(J * V + beta * C + gamma * (lambda_acc + lambda))
// in 2D: cross(w, r) = perp(r) * w
var cdot = vec2.mad(body2.v, vec2.perp(this.r2), body2.w);
var soft = vec2.mad(this.beta_c, this.lambda_acc, this.gamma);
var lambda = this.em_inv.solve(vec2.add(cdot, soft).neg());
// Accumulate lambda
var lambda_old = this.lambda_acc.duplicate();
this.lambda_acc.addself(lambda);
var lsq = this.lambda_acc.lengthsq();
if (lsq > this.maxImpulse * this.maxImpulse) {
this.lambda_acc.scale(this.maxImpulse / Math.sqrt(lsq));
}
lambda = vec2.sub(this.lambda_acc, lambda_old);
// Apply constraint impulse
// V += JT * lambda * invM
body2.v.mad(lambda, body2.m_inv);
body2.w += vec2.cross(this.r2, lambda) * body2.i_inv;
}
MouseJoint.prototype.solvePositionConstraints = function() {
return true;
}
MouseJoint.prototype.getReactionForce = function(dt_inv) {
return vec2.scale(this.lambda_acc, dt_inv);
}
MouseJoint.prototype.getReactionTorque = function(dt_inv) {
return 0;
}
-241
View File
@@ -1,241 +0,0 @@
/*
* Copyright (c) 2012 Ju Hyung Lee
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
//-------------------------------------------------------------------------------------------------
// Prismatic Joint
//
// Linear Constraint:
// d = p2 - p1
// n = normalize(perp(d))
// C1 = dot(n, d)
// C1dot = dot(d, dn/dt) + dot(n dd/dt)
// = dot(d, cross(w1, n)) + dot(n, v2 + cross(w2, r2) - v1 - cross(w1, r1))
// = dot(d, cross(w1, n)) + dot(n, v2) + dot(n, cross(w2, r2)) - dot(n, v1) - dot(n, cross(w1, r1))
// = -dot(n, v1) - dot(cross(d + r1, n), w1) + dot(n, v2) + dot(cross(r2, n), w2)
// J1 = [ -n, -s1, n, s2 ]
// s1 = cross(r1 + d, n)
// s2 = cross(r2, n)
//
// Angular Constraint:
// C2 = a2 - a1 - initial_da
// C2dot = w2 - w1
// J2 = [ 0, -1, 0, 1 ]
//
// Block Jacobian Matrix:
// J = [ -n, -s1, n, s2 ]
// [ 0, -1, 0, 1 ]
//
// impulse = JT * lambda = [ -n * lambda_x, -(s1 * lambda_x + lambda_y), n * lambda_x, s2 * lambda_x + lambda_y ]
//-------------------------------------------------------------------------------------------------
PrismaticJoint = function(body1, body2, anchor1, anchor2) {
Joint.call(this, Joint.TYPE_PRISMATIC, body1, body2, true);
// Local anchor points
this.anchor1 = this.body1.getLocalPoint(anchor1);
this.anchor2 = this.body2.getLocalPoint(anchor2);
var d = vec2.sub(anchor2, anchor1);
// Body1's local line normal
this.n_local = this.body1.getLocalVector(vec2.normalize(vec2.perp(d)));
this.da = body2.a - body1.a;
// Accumulated lambda
this.lambda_acc = new vec2(0, 0);
}
PrismaticJoint.prototype = new Joint;
PrismaticJoint.prototype.constructor = PrismaticJoint;
PrismaticJoint.prototype.setWorldAnchor1 = function(anchor1) {
// Local anchor points
this.anchor1 = this.body1.getLocalPoint(anchor1);
var d = vec2.sub(this.getWorldAnchor2(), anchor1);
// Body1's local line normal
this.n_local = this.body1.getLocalVector(vec2.normalize(vec2.perp(d)));
}
PrismaticJoint.prototype.setWorldAnchor2 = function(anchor2) {
// Local anchor points
this.anchor2 = this.body2.getLocalPoint(anchor2);
var d = vec2.sub(anchor2, this.getWorldAnchor1());
// Body1's local line normal
this.n_local = this.body1.getLocalVector(vec2.normalize(vec2.perp(d)));
}
PrismaticJoint.prototype.serialize = function() {
return {
"type": "PrismaticJoint",
"body1": this.body1.id,
"body2": this.body2.id,
"anchor1": this.body1.getWorldPoint(this.anchor1),
"anchor2": this.body2.getWorldPoint(this.anchor2),
"collideConnected": this.collideConnected,
"maxForce": this.maxForce,
"breakable": this.breakable
};
}
PrismaticJoint.prototype.initSolver = function(dt, warmStarting) {
var body1 = this.body1;
var body2 = this.body2;
// Max impulse
this.maxImpulse = this.maxForce * dt;
// Transformed r1, r2
this.r1 = body1.xf.rotate(vec2.sub(this.anchor1, body1.centroid));
this.r2 = body2.xf.rotate(vec2.sub(this.anchor2, body2.centroid));
// World anchor points
var p1 = vec2.add(body1.p, this.r1);
var p2 = vec2.add(body2.p, this.r2);
// Delta vector between world anchor points
var d = vec2.sub(p2, p1);
// r1 + d
this.r1_d = vec2.add(this.r1, d);
// World line normal
this.n = vec2.normalize(vec2.perp(d));
// s1, s2
this.s1 = vec2.cross(this.r1_d, this.n);
this.s2 = vec2.cross(this.r2, this.n);
// invEM = J * invM * JT
var s1 = this.s1;
var s2 = this.s2;
var s1_i = s1 * body1.i_inv;
var s2_i = s2 * body2.i_inv;
var k11 = body1.m_inv + body2.m_inv + s1 * s1_i + s2 * s2_i;
var k12 = s1_i + s2_i;
var k22 = body1.i_inv + body2.i_inv;
this.em_inv = new mat2(k11, k12, k12, k22);
if (warmStarting) {
// linearImpulse = JT * lambda
var impulse = vec2.scale(this.n, this.lambda_acc.x);
// Apply cached constraint impulses
// V += JT * lambda * invM
body1.v.mad(impulse, -body1.m_inv);
body1.w -= (this.s1 * this.lambda_acc.x + this.lambda_acc.y) * body1.i_inv;
body2.v.mad(impulse, body2.m_inv);
body2.w += (this.s2 * this.lambda_acc.x + this.lambda_acc.y) * body2.i_inv;
}
else {
this.lambda_acc.set(0, 0);
}
}
PrismaticJoint.prototype.solveVelocityConstraints = function() {
var body1 = this.body1;
var body2 = this.body2;
// Compute lambda for velocity constraint
// Solve J * invM * JT * lambda = -J * V
var cdot1 = this.n.dot(vec2.sub(body2.v, body1.v)) + this.s2 * body2.w - this.s1 * body1.w;
var cdot2 = body2.w - body1.w;
var lambda = this.em_inv.solve(new vec2(-cdot1, -cdot2));
// Accumulate lambda
this.lambda_acc.addself(lambda);
// linearImpulse = JT * lambda
var impulse = vec2.scale(this.n, lambda.x);
// Apply constraint impulses
// V += JT * lambda * invM
body1.v.mad(impulse, -body1.m_inv);
body1.w -= (this.s1 * lambda.x + lambda.y) * body1.i_inv;
body2.v.mad(impulse, body2.m_inv);
body2.w += (this.s2 * lambda.x + lambda.y) * body2.i_inv;
}
PrismaticJoint.prototype.solvePositionConstraints = function() {
var body1 = this.body1;
var body2 = this.body2;
// Transformed r1, r2
var r1 = vec2.rotate(vec2.sub(this.anchor1, body1.centroid), body1.a);
var r2 = vec2.rotate(vec2.sub(this.anchor2, body2.centroid), body2.a);
// World anchor points
var p1 = vec2.add(body1.p, r1);
var p2 = vec2.add(body2.p, r2);
// Delta vector between world anchor points
var d = vec2.sub(p2, p1);
// r1 + d
var r1_d = vec2.add(r1, d);
// World line normal
var n = vec2.rotate(this.n_local, body1.a);
// Position constraint
var c1 = vec2.dot(n, d);
var c2 = body2.a - body1.a - this.da;
var correction = new vec2;
correction.x = Math.clamp(c1, -Joint.MAX_LINEAR_CORRECTION, Joint.MAX_LINEAR_CORRECTION);
correction.y = Math.clamp(c2, -Joint.MAX_ANGULAR_CORRECTION, Joint.MAX_ANGULAR_CORRECTION);
// Compute impulse for position constraint
// Solve J * invM * JT * lambda = -C / dt
var s1 = vec2.cross(r1_d, n);
var s2 = vec2.cross(r2, n);
var s1_i = s1 * body1.i_inv;
var s2_i = s2 * body2.i_inv;
var k11 = body1.m_inv + body2.m_inv + s1 * s1_i + s2 * s2_i;
var k12 = s1_i + s2_i;
var k22 = body1.i_inv + body2.i_inv;
var em_inv = new mat2(k11, k12, k12, k22);
var lambda_dt = em_inv.solve(correction.neg());
// Apply constarint impulses
// impulse = JT * lambda
// X += impulse * invM * dt
var impulse_dt = vec2.scale(n, lambda_dt.x);
body1.p.mad(impulse_dt, -body1.m_inv);
body1.a -= (vec2.cross(r1_d, impulse_dt) + lambda_dt.y) * body1.i_inv;
body2.p.mad(impulse_dt, body2.m_inv);
body2.a += (vec2.cross(r2, impulse_dt) + lambda_dt.y) * body2.i_inv;
return Math.abs(c1) <= Joint.LINEAR_SLOP && Math.abs(c2) <= Joint.ANGULAR_SLOP;
}
PrismaticJoint.prototype.getReactionForce = function(dt_inv) {
return vec2.scale(this.n, this.lambda_acc.x * dt_inv);
}
PrismaticJoint.prototype.getReactionTorque = function(dt_inv) {
return this.lambda_acc.y * dt_inv;
}
-378
View File
@@ -1,378 +0,0 @@
/*
* Copyright (c) 2012 Ju Hyung Lee
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
//-------------------------------------------------------------------------------------------------
// Revolute Joint
//
// Point-to-Point Constraint:
// C1 = p2 - p1
// C1dot = v2 + cross(w2, r2) - v1 - cross(w1, r1)
// = -v1 + cross(r1, w1) + v2 - cross(r2, w1)
// J1 = [ -I, skew(r1), I, -skew(r2) ]
//
// Angular Constraint (for angle limit):
// C2 = a2 - a1 - refAngle
// C2dot = w2 - w1
// J2 = [ 0, -1, 0, 1 ]
//
// Block Jacobian Matrix:
// J = [ -I, skew(r1), I, -skew(r2) ]
// [ 0, -1, 0, 1 ]
//
// impulse = JT * lambda = [ -lambda_xy, -(cross(r1, lambda_xy) + lambda_z), lambda_xy, cross(r1, lambda_xy) + lambda_z ]
//-------------------------------------------------------------------------------------------------
RevoluteJoint = function(body1, body2, anchor) {
Joint.call(this, Joint.TYPE_REVOLUTE, body1, body2, false);
this.anchor1 = this.body1.getLocalPoint(anchor);
this.anchor2 = this.body2.getLocalPoint(anchor);
// Initial angle difference
this.refAngle = body2.a - body1.a;
// Accumulated lambda
this.lambda_acc = new vec3(0, 0, 0);
this.motorLambda_acc = 0;
// Angle limit
this.limitEnabled = false;
this.limitLowerAngle = 0;
this.limitUpperAngle = 0;
this.limitState = Joint.LIMIT_STATE_INACTIVE;
// Motor
this.motorEnabled = false;
this.motorSpeed = 0;
this.maxMotorTorque = 0;
}
RevoluteJoint.prototype = new Joint;
RevoluteJoint.prototype.constructor = RevoluteJoint;
RevoluteJoint.prototype.setWorldAnchor1 = function(anchor1) {
this.anchor1 = this.body1.getLocalPoint(anchor1);
this.anchor2 = this.body2.getLocalPoint(anchor1);
}
RevoluteJoint.prototype.setWorldAnchor2 = function(anchor2) {
this.anchor1 = this.body1.getLocalPoint(anchor2);
this.anchor2 = this.body2.getLocalPoint(anchor2);
}
RevoluteJoint.prototype.serialize = function() {
return {
"type": "RevoluteJoint",
"body1": this.body1.id,
"body2": this.body2.id,
"anchor": this.body1.getWorldPoint(this.anchor1),
"collideConnected": this.collideConnected,
"maxForce": this.maxForce,
"breakable": this.breakable,
"limitEnabled": this.limitEnabled,
"limitLowerAngle": this.limitLowerAngle,
"limitUpperAngle": this.limitUpperAngle,
"motorEnabled": this.motorEnabled,
"motorSpeed": this.motorSpeed,
"maxMotorTorque": this.maxMotorTorque
};
}
RevoluteJoint.prototype.enableMotor = function(flag) {
this.motorEnabled = flag;
}
RevoluteJoint.prototype.setMotorSpeed = function(speed) {
this.motorSpeed = speed;
}
RevoluteJoint.prototype.setMaxMotorTorque = function(torque) {
this.maxMotorTorque = torque;
}
RevoluteJoint.prototype.enableLimit = function(flag) {
this.limitEnabled = flag;
}
RevoluteJoint.prototype.setLimits = function(lower, upper) {
this.limitLowerAngle = lower;
this.limitUpperAngle = upper;
}
RevoluteJoint.prototype.initSolver = function(dt, warmStarting) {
var body1 = this.body1;
var body2 = this.body2;
// Max impulse
this.maxImpulse = this.maxForce * dt;
if (!this.motorEnabled) {
this.motorLambda_acc = 0;
}
else {
this.maxMotorImpulse = this.maxMotorTorque * dt;
}
if (this.limitEnabled) {
var da = body2.a - body1.a - this.refAngle;
if (Math.abs(this.limitUpperAngle - this.limitLowerAngle) < Joint.ANGULAR_SLOP) {
this.limitState = Joint.LIMIT_STATE_EQUAL_LIMITS;
}
else if (da <= this.limitLowerAngle) {
if (this.limitState != Joint.LIMIT_STATE_AT_LOWER) {
this.lambda_acc.z = 0;
}
this.limitState = Joint.LIMIT_STATE_AT_LOWER;
}
else if (da >= this.limitUpperAngle) {
if (this.limitState != Joint.LIMIT_STATE_AT_UPPER) {
this.lambda_acc.z = 0;
}
this.limitState = Joint.LIMIT_STATE_AT_UPPER;
}
else {
this.limitState = Joint.LIMIT_STATE_INACTIVE;
this.lambda_acc.z = 0;
}
}
else {
this.limitState = Joint.LIMIT_STATE_INACTIVE;
}
// Transformed r1, r2
this.r1 = body1.xf.rotate(vec2.sub(this.anchor1, body1.centroid));
this.r2 = body2.xf.rotate(vec2.sub(this.anchor2, body2.centroid));
// invEM = J * invM * JT
var sum_m_inv = body1.m_inv + body2.m_inv;
var r1 = this.r1;
var r2 = this.r2;
var r1x_i = r1.x * body1.i_inv;
var r1y_i = r1.y * body1.i_inv;
var r2x_i = r2.x * body2.i_inv;
var r2y_i = r2.y * body2.i_inv;
var k11 = sum_m_inv + r1.y * r1y_i + r2.y * r2y_i;
var k12 = -r1.x * r1y_i - r2.x * r2y_i;
var k13 = -r1y_i - r2y_i;
var k22 = sum_m_inv + r1.x * r1x_i + r2.x * r2x_i;
var k23 = r1x_i + r2x_i;
var k33 = body1.i_inv + body2.i_inv;
this.em_inv = new mat3(k11, k12, k13, k12, k22, k23, k13, k23, k33);
// K2 = J2 * invM * J2T
if (k33 != 0) {
this.em2 = 1 / k33;
}
if (warmStarting) {
// Apply cached constraint impulses
// V += JT * lambda
var lambda_xy = new vec2(this.lambda_acc.x, this.lambda_acc.y);
var lambda_z = this.lambda_acc.z + this.motorLambda_acc;
body1.v.mad(lambda_xy, -body1.m_inv);
body1.w -= (vec2.cross(this.r1, lambda_xy) + lambda_z) * body1.i_inv;
body2.v.mad(lambda_xy, body2.m_inv);
body2.w += (vec2.cross(this.r2, lambda_xy) + lambda_z) * body2.i_inv;
}
else {
this.lambda_acc.set(0, 0, 0);
this.motorLambda_acc = 0;
}
}
RevoluteJoint.prototype.solveVelocityConstraints = function() {
var body1 = this.body1;
var body2 = this.body2;
// Solve motor constraint
if (this.motorEnabled && this.limitState != Joint.LIMIT_STATE_EQUAL_LIMITS) {
// Compute motor impulse
var cdot = body2.w - body1.w - this.motorSpeed;
var lambda = -this.em2 * cdot;
var motorLambdaOld = this.motorLambda_acc;
this.motorLambda_acc = Math.clamp(this.motorLambda_acc + lambda, -this.maxMotorImpulse, this.maxMotorImpulse);
lambda = this.motorLambda_acc - motorLambdaOld;
// Apply motor constraint impulses
body1.w -= lambda * body1.i_inv;
body2.w += lambda * body2.i_inv;
}
// Solve point-to-point constraint with angular limit
if (this.limitEnabled && this.limitState != Joint.LIMIT_STATE_INACTIVE) {
// Compute lambda for velocity constraint
// Solve J * invM * JT * lambda = -J * V
// in 2D: cross(w, r) = perp(r) * w
var v1 = vec2.mad(body1.v, vec2.perp(this.r1), body1.w);
var v2 = vec2.mad(body2.v, vec2.perp(this.r2), body2.w);
var cdot1 = vec2.sub(v2, v1);
var cdot2 = body2.w - body1.w;
var cdot = vec3.fromVec2(cdot1, cdot2);
var lambda = this.em_inv.solve(cdot.neg());
if (this.limitState == Joint.LIMIT_STATE_EQUAL_LIMITS) {
// Accumulate lambda
this.lambda_acc.addself(lambda);
}
else if (this.limitState == Joint.LIMIT_STATE_AT_LOWER || this.limitState == Joint.LIMIT_STATE_AT_UPPER) {
// Accumulated new lambda.z
var newLambda_z = this.lambda_acc.z + lambda.z;
var lowerLimited = this.limitState == Joint.LIMIT_STATE_AT_LOWER && newLambda_z < 0;
var upperLimited = this.limitState == Joint.LIMIT_STATE_AT_UPPER && newLambda_z > 0;
if (lowerLimited || upperLimited) {
// Modify last equation to get lambda_acc.z to 0
// That is, lambda.z have to be equal -lambda_acc.z
// rhs = -J * V - (K_13, K_23, K_33) * (lambda.z + lambda_acc.z)
// Solve J * invM * JT * reduced_lambda = rhs
var rhs = vec2.add(cdot1, vec2.scale(new vec2(this.em_inv._13, this.em_inv._23), newLambda_z));
var reduced = this.em_inv.solve2x2(rhs.neg());
lambda.x = reduced.x;
lambda.y = reduced.y;
lambda.z = -this.lambda_acc.z;
// Accumulate lambda
this.lambda_acc.x += lambda.x;
this.lambda_acc.y += lambda.y;
this.lambda_acc.z = 0;
}
else {
// Accumulate lambda
this.lambda_acc.addself(lambda);
}
}
// Apply constraint impulses
// V += JT * lambda * invM
var lambda_xy = new vec2(lambda.x, lambda.y);
body1.v.mad(lambda_xy, -body1.m_inv);
body1.w -= (vec2.cross(this.r1, lambda_xy) + lambda.z) * body1.i_inv;
body2.v.mad(lambda_xy, body2.m_inv);
body2.w += (vec2.cross(this.r2, lambda_xy) + lambda.z) * body2.i_inv;
}
// Solve point-to-point constraint
else {
// Compute lambda for velocity constraint
// Solve J1 * invM * J1T * lambda = -J1 * V
// in 2D: cross(w, r) = perp(r) * w
var v1 = vec2.mad(body1.v, vec2.perp(this.r1), body1.w);
var v2 = vec2.mad(body2.v, vec2.perp(this.r2), body2.w);
var cdot = vec2.sub(v2, v1);
var lambda = this.em_inv.solve2x2(cdot.neg());
// Accumulate lambda
this.lambda_acc.addself(vec3.fromVec2(lambda, 0));
// Apply constraint impulses
// V += J1T * lambda * invM
body1.v.mad(lambda, -body1.m_inv);
body1.w -= vec2.cross(this.r1, lambda) * body1.i_inv;
body2.v.mad(lambda, body2.m_inv);
body2.w += vec2.cross(this.r2, lambda) * body2.i_inv;
}
}
RevoluteJoint.prototype.solvePositionConstraints = function() {
var body1 = this.body1;
var body2 = this.body2;
var angularError = 0;
var positionError = 0;
// Solve limit constraint
if (this.limitEnabled && this.limitState != Joint.LIMIT_STATE_INACTIVE) {
var da = body2.a - body1.a - this.refAngle;
// angular lambda = -EM * C / dt
var angularImpulseDt = 0;
if (this.limitState == Joint.LIMIT_STATE_EQUAL_LIMITS) {
var c = Math.clamp(da - this.limitLowerAngle, -Joint.MAX_ANGULAR_CORRECTION, Joint.MAX_ANGULAR_CORRECTION);
angularError = Math.abs(c);
angularImpulseDt = -this.em2 * c;
}
else if (this.limitState == Joint.LIMIT_STATE_AT_LOWER) {
var c = da - this.limitLowerAngle;
angularError = -c;
c = Math.clamp(c + Joint.ANGULAR_SLOP, -Joint.MAX_ANGULAR_CORRECTION, 0);
angularImpulseDt = -this.em2 * c;
}
else if (this.limitState == Joint.LIMIT_STATE_AT_UPPER) {
var c = da - this.limitUpperAngle;
angularError = c;
c = Math.clamp(c - Joint.ANGULAR_SLOP, 0, Joint.MAX_ANGULAR_CORRECTION);
angularImpulseDt = -this.em2 * c;
}
body1.a -= angularImpulseDt * body1.i_inv;
body2.a += angularImpulseDt * body2.i_inv;
}
// Solve point-to-point constraint
{
// Transformed r1, r2
var r1 = vec2.rotate(vec2.sub(this.anchor1, body1.centroid), body1.a);
var r2 = vec2.rotate(vec2.sub(this.anchor2, body2.centroid), body2.a);
// Position constraint
var c = vec2.sub(vec2.add(body2.p, r2), vec2.add(body1.p, r1));
var correction = vec2.truncate(c, Joint.MAX_LINEAR_CORRECTION);
positionError = correction.length();
// Compute lambda for position constraint
// Solve J1 * invM * J1T * lambda = -C / dt
var sum_m_inv = body1.m_inv + body2.m_inv;
var r1y_i = r1.y * body1.i_inv;
var r2y_i = r2.y * body2.i_inv;
var k11 = sum_m_inv + r1.y * r1y_i + r2.y * r2y_i;
var k12 = -r1.x * r1y_i - r2.x * r2y_i;
var k22 = sum_m_inv + r1.x * r1.x * body1.i_inv + r2.x * r2.x * body2.i_inv;
var em_inv = new mat2(k11, k12, k12, k22);
var lambda_dt = em_inv.solve(correction.neg());
// Apply constraint impulses
// impulse = J1T * lambda
// X += impulse * invM * dt
body1.p.mad(lambda_dt, -body1.m_inv);
body1.a -= vec2.cross(r1, lambda_dt) * body1.i_inv;
body2.p.mad(lambda_dt, body2.m_inv);
body2.a += vec2.cross(r2, lambda_dt) * body2.i_inv;
}
return positionError < Joint.LINEAR_SLOP && angularError < Joint.ANGULAR_SLOP;
}
RevoluteJoint.prototype.getReactionForce = function(dt_inv) {
return vec2.scale(this.lambda_acc, dt_inv);
}
RevoluteJoint.prototype.getReactionTorque = function(dt_inv) {
return 0;
}
-211
View File
@@ -1,211 +0,0 @@
/*
* Copyright (c) 2012 Ju Hyung Lee
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
//-------------------------------------------------------------------------------------------------
// Rope Joint
//
// d = p2 - p1
// u = d / norm(d)
// C = norm(d) - l
// C = sqrt(dot(d, d)) - l
// Cdot = dot(u, v2 + cross(w2, r2) - v1 - cross(w1, r1))
// = -dot(u, v1) - dot(w1, cross(r1, u)) + dot(u, v2) + dot(w2, cross(r2, u))
// J = [ -u, -cross(r1, u), u, cross(r2, u) ]
//
// impulse = JT * lambda = [ -u * lambda, -cross(r1, u) * lambda, u * lambda, cross(r1, u) * lambda ]
//-------------------------------------------------------------------------------------------------
RopeJoint = function(body1, body2, anchor1, anchor2) {
Joint.call(this, Joint.TYPE_ROPE, body1, body2, true);
// Local anchor points
this.anchor1 = this.body1.getLocalPoint(anchor1);
this.anchor2 = this.body2.getLocalPoint(anchor2);
// Max distance
this.maxDistance = vec2.dist(anchor1, anchor2);
// Accumulated impulse
this.lambda_acc = 0;
}
RopeJoint.prototype = new Joint;
RopeJoint.prototype.constructor = RopeJoint;
RopeJoint.prototype.setWorldAnchor1 = function(anchor1) {
this.anchor1 = this.body1.getLocalPoint(anchor1);
this.maxDistance = vec2.dist(anchor1, this.getWorldAnchor2());
}
RopeJoint.prototype.setWorldAnchor2 = function(anchor2) {
this.anchor2 = this.body2.getLocalPoint(anchor2);
this.maxDistance = vec2.dist(anchor2, this.getWorldAnchor1());
}
RopeJoint.prototype.serialize = function() {
return {
"type": "RopeJoint",
"body1": this.body1.id,
"body2": this.body2.id,
"anchor1": this.body1.getWorldPoint(this.anchor1),
"anchor2": this.body2.getWorldPoint(this.anchor2),
"collideConnected": this.collideConnected,
"maxForce": this.maxForce,
"breakable": this.breakable,
};
}
RopeJoint.prototype.initSolver = function(dt, warmStarting) {
var body1 = this.body1;
var body2 = this.body2;
// Max impulse
this.maxImpulse = this.maxForce * dt;
// Transformed r1, r2
this.r1 = body1.xf.rotate(vec2.sub(this.anchor1, body1.centroid));
this.r2 = body2.xf.rotate(vec2.sub(this.anchor2, body2.centroid));
// Delta vector between two world anchors
var d = vec2.sub(vec2.add(body2.p, this.r2), vec2.add(body1.p, this.r1));
// Distance between two anchors
this.distance = d.length();
//
var c = this.distance - this.maxDistance;
if (c > 0) {
this.cdt = 0;
this.limitState = Joint.LIMIT_STATE_AT_UPPER;
}
else {
this.cdt = c / dt;
this.limitState = Joint.LIMIT_STATE_INACTIVE;
}
// Unit delta vector
if (this.distance > Joint.LINEAR_SLOP) {
this.u = vec2.scale(d, 1 / this.distance);
}
else {
this.u = vec2.zero;
}
// s1, s2
this.s1 = vec2.cross(this.r1, this.u);
this.s2 = vec2.cross(this.r2, this.u);
// invEM = J * invM * JT
var em_inv = body1.m_inv + body2.m_inv + body1.i_inv * this.s1 * this.s1 + body2.i_inv * this.s2 * this.s2;
this.em = em_inv == 0 ? 0 : 1 / em_inv;
if (warmStarting) {
// linearImpulse = JT * lambda
var impulse = vec2.scale(this.u, this.lambda_acc);
// Apply cached constraint impulses
// V += JT * lambda * invM
body1.v.mad(impulse, -body1.m_inv);
body1.w -= this.s1 * this.lambda_acc * body1.i_inv;
body2.v.mad(impulse, body2.m_inv);
body2.w += this.s2 * this.lambda_acc * body2.i_inv;
}
else {
this.lambda_acc = 0;
}
}
RopeJoint.prototype.solveVelocityConstraints = function() {
var body1 = this.body1;
var body2 = this.body2;
// Compute lambda for velocity constraint
// Solve J * invM * JT * lambda = -(J * V)
var cdot = this.u.dot(vec2.sub(body2.v, body1.v)) + this.s2 * body2.w - this.s1 * body1.w;
var lambda = -this.em * (cdot + this.cdt);
// Accumulate lambda and clamp it to zero
var lambda_old = this.lambda_acc;
this.lambda_acc = Math.min(lambda_old + lambda, 0);
lambda = this.lambda_acc - lambda_old;
// linearImpulse = JT * lambda
var impulse = vec2.scale(this.u, lambda);
// Apply constraint impulses
// V += JT * lambda * invM
body1.v.mad(impulse, -body1.m_inv);
body1.w -= this.s1 * lambda * body1.i_inv;
body2.v.mad(impulse, body2.m_inv);
body2.w += this.s2 * lambda * body2.i_inv;
}
RopeJoint.prototype.solvePositionConstraints = function() {
var body1 = this.body1;
var body2 = this.body2;
// Transformed r1, r2
var r1 = vec2.rotate(vec2.sub(this.anchor1, body1.centroid), body1.a);
var r2 = vec2.rotate(vec2.sub(this.anchor2, body2.centroid), body2.a);
// Delta vector between two anchors
var d = vec2.sub(vec2.add(body2.p, r2), vec2.add(body1.p, r1));
// Distance between two anchors
var dist = d.length();
// Unit delta vector
var u = vec2.scale(d, 1 / dist);
// Position constraint
var c = dist - this.maxDistance;
var correction = Math.clamp(c, 0, Joint.MAX_LINEAR_CORRECTION);
// Compute lambda for correction
// Solve J * invM * JT * lambda = -C / dt
var s1 = vec2.cross(r1, u);
var s2 = vec2.cross(r2, u);
var em_inv = body1.m_inv + body2.m_inv + body1.i_inv * s1 * s1 + body2.i_inv * s2 * s2;
var lambda_dt = em_inv == 0 ? 0 : -correction / em_inv;
// Apply constraint impulses
// impulse = JT * lambda
// X += impulse * invM * dt
var impulse_dt = vec2.scale(u, lambda_dt);
body1.p.mad(impulse_dt, -body1.m_inv);
body1.a -= s1 * lambda_dt * body1.i_inv;
body2.p.mad(impulse_dt, body2.m_inv);
body2.a += s2 * lambda_dt * body2.i_inv;
return c < Joint.LINEAR_SLOP;
}
RopeJoint.prototype.getReactionForce = function(dt_inv) {
return vec2.scale(this.u, this.lambda_acc * dt_inv);
}
RopeJoint.prototype.getReactionTorque = function(dt_inv) {
return 0;
}
-306
View File
@@ -1,306 +0,0 @@
/*
* Copyright (c) 2012 Ju Hyung Lee
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
//-------------------------------------------------------------------------------------------------
// Weld Joint
//
// Point-to-Point Constraint:
// C1 = p2 - p1
// Cdot1 = v2 + cross(w2, r2) - v1 - cross(w1, r1)
// = -v1 + cross(r1, w1) + v2 - cross(r2, w1)
// J1 = [ -I, skew(r1), I, -skew(r2) ]
//
// Angular Constraint:
// C2 = a2 - a1
// C2dot = w2 - w1
// J2 = [ 0, -1, 0, 1 ]
//
// Block Jacobian Matrix:
// J = [ -I, skew(r1), I, -skew(r2) ]
// [ 0, -1, 0, 1 ]
//
// impulse = JT * lambda = [ -lambda_xy, -(cross(r1, lambda_xy) + lambda_z), lambda_xy, cross(r1, lambda_xy) + lambda_z ]
//-------------------------------------------------------------------------------------------------
WeldJoint = function(body1, body2, anchor) {
Joint.call(this, Joint.TYPE_WELD, body1, body2, false);
this.anchor1 = this.body1.getLocalPoint(anchor);
this.anchor2 = this.body2.getLocalPoint(anchor);
// Soft constraint coefficients
this.gamma = 0;
this.beta_c = 0;
// Spring coefficients
this.frequencyHz = 0;
this.dampingRatio = 0;
// Accumulated lambda
this.lambda_acc = new vec3(0, 0, 0);
}
WeldJoint.prototype = new Joint;
WeldJoint.prototype.constructor = WeldJoint;
WeldJoint.prototype.setWorldAnchor1 = function(anchor1) {
this.anchor1 = this.body1.getLocalPoint(anchor1);
this.anchor2 = this.body2.getLocalPoint(anchor1);
}
WeldJoint.prototype.setWorldAnchor2 = function(anchor2) {
this.anchor1 = this.body1.getLocalPoint(anchor2);
this.anchor2 = this.body2.getLocalPoint(anchor2);
}
WeldJoint.prototype.serialize = function() {
return {
"type": "WeldJoint",
"body1": this.body1.id,
"body2": this.body2.id,
"anchor1": this.body1.getWorldPoint(this.anchor1),
"anchor2": this.body2.getWorldPoint(this.anchor2),
"collideConnected": this.collideConnected,
"maxForce": this.maxForce,
"breakable": this.breakable,
"frequencyHz": this.frequencyHz,
"dampingRatio": this.dampingRatio
};
}
WeldJoint.prototype.setSpringFrequencyHz = function(frequencyHz) {
// NOTE: frequencyHz should be limited to under 4 times time steps
this.frequencyHz = frequencyHz;
}
WeldJoint.prototype.setSpringDampingRatio = function(dampingRatio) {
this.dampingRatio = dampingRatio;
}
WeldJoint.prototype.initSolver = function(dt, warmStarting) {
var body1 = this.body1;
var body2 = this.body2;
// Max impulse
this.maxImpulse = this.maxForce * dt;
// Transformed r1, r2
this.r1 = body1.xf.rotate(vec2.sub(this.anchor1, body1.centroid));
this.r2 = body2.xf.rotate(vec2.sub(this.anchor2, body2.centroid));
// invEM = J * invM * JT
var sum_m_inv = body1.m_inv + body2.m_inv;
var r1 = this.r1;
var r2 = this.r2;
var r1x_i = r1.x * body1.i_inv;
var r1y_i = r1.y * body1.i_inv;
var r2x_i = r2.x * body2.i_inv;
var r2y_i = r2.y * body2.i_inv;
var k11 = sum_m_inv + r1.y * r1y_i + r2.y * r2y_i;
var k12 = -r1.x * r1y_i - r2.x * r2y_i;
var k13 = -r1y_i - r2y_i;
var k22 = sum_m_inv + r1.x * r1x_i + r2.x * r2x_i;
var k23 = r1x_i + r2x_i;
var k33 = body1.i_inv + body2.i_inv;
this.em_inv = new mat3(k11, k12, k13, k12, k22, k23, k13, k23, k33);
// Compute soft constraint parameters
if (this.frequencyHz > 0) {
var m = k33 > 0 ? 1 / k33 : 0;
// Frequency
var omega = 2 * Math.PI * this.frequencyHz;
// Spring stiffness
var k = m * omega * omega;
// Damping coefficient
var c = m * 2 * this.dampingRatio * omega;
// Soft constraint formulas
// gamma and beta are divided by dt to reduce computation
this.gamma = (c + k * dt) * dt;
this.gamma = this.gamma == 0 ? 0 : 1 / this.gamma;
var beta = dt * k * this.gamma;
// Position constraint
var pc = body2.a - body1.a;
this.beta_c = beta * pc;
// invEM = invEM + gamma * I (to reduce calculation)
this.em_inv._33 += this.gamma;
}
else {
this.gamma = 0;
this.beta_c = 0;
}
if (warmStarting) {
// Apply cached constraint impulses
// V += JT * lambda * invM
var lambda_xy = new vec2(this.lambda_acc.x, this.lambda_acc.y);
var lambda_z = this.lambda_acc.z;
body1.v.mad(lambda_xy, -body1.m_inv);
body1.w -= (vec2.cross(this.r1, lambda_xy) + lambda_z) * body1.i_inv;
body2.v.mad(lambda_xy, body2.m_inv);
body2.w += (vec2.cross(this.r2, lambda_xy) + lambda_z) * body2.i_inv;
}
else {
this.lambda_acc.set(0, 0, 0);
}
}
WeldJoint.prototype.solveVelocityConstraints = function() {
var body1 = this.body1;
var body2 = this.body2;
if (this.frequencyHz > 0) {
// Compute lambda for angular velocity constraint
// Solve J2 * invM * J2T * lambda = -(J2 * V + beta * C + gamma * (lambda_acc + lambda))
var cdot2 = body2.w - body1.w;
lambda_z = -(cdot2 + this.beta_c + this.gamma * this.lambda_acc.z) / this.em_inv._33;
// Apply angular constraint impulses
// V += J2T * lambda * invM
body1.w -= lambda_z * body1.i_inv;
body2.w += lambda_z * body2.i_inv;
// Compute lambda for velocity constraint
// Solve J1 * invM * J1T * lambda = -J1 * V
var v1 = vec2.mad(body1.v, vec2.perp(this.r1), body1.w);
var v2 = vec2.mad(body2.v, vec2.perp(this.r2), body2.w);
var cdot1 = vec2.sub(v2, v1);
var lambda_xy = this.em_inv.solve2x2(cdot1.neg());
// Accumulate lambda
this.lambda_acc.x += lambda_xy.x;
this.lambda_acc.y += lambda_xy.y;
this.lambda_acc.z += lambda_z;
// Apply constraint impulses
// V += J1T * lambda * invM
body1.v.mad(lambda_xy, -body1.m_inv);
body1.w -= vec2.cross(this.r1, lambda_xy) * body1.i_inv;
body2.v.mad(lambda_xy, body2.m_inv);
body2.w += vec2.cross(this.r2, lambda_xy) * body2.i_inv;
}
else {
// Compute lambda for velocity constraint
// Solve J * invM * JT * lambda = -J * V
// in 2D: cross(w, r) = perp(r) * w
var v1 = vec2.mad(body1.v, vec2.perp(this.r1), body1.w);
var v2 = vec2.mad(body2.v, vec2.perp(this.r2), body2.w);
var cdot1 = vec2.sub(v2, v1);
var cdot2 = body2.w - body1.w;
var cdot = vec3.fromVec2(cdot1, cdot2);
var lambda = this.em_inv.solve(cdot.neg());
// Accumulate lambda
this.lambda_acc.addself(lambda);
// Apply constraint impulses
// V += JT * lambda * invM
var lambda_xy = new vec2(lambda.x, lambda.y);
body1.v.mad(lambda_xy, -body1.m_inv);
body1.w -= (vec2.cross(this.r1, lambda_xy) + lambda.z) * body1.i_inv;
body2.v.mad(lambda_xy, body2.m_inv);
body2.w += (vec2.cross(this.r2, lambda_xy) + lambda.z) * body2.i_inv;
}
}
WeldJoint.prototype.solvePositionConstraints = function() {
var body1 = this.body1;
var body2 = this.body2;
// Transformed r1, r2
var r1 = vec2.rotate(vec2.sub(this.anchor1, body1.centroid), body1.a);
var r2 = vec2.rotate(vec2.sub(this.anchor2, body2.centroid), body2.a);
// Compute J * invM * JT
var sum_m_inv = body1.m_inv + body2.m_inv;
var r1x_i = r1.x * body1.i_inv;
var r1y_i = r1.y * body1.i_inv;
var r2x_i = r2.x * body2.i_inv;
var r2y_i = r2.y * body2.i_inv;
var k11 = sum_m_inv + r1.y * r1y_i + r2.y * r2y_i;
var k12 = -r1.x * r1y_i - r2.x * r2y_i;
var k13 = -r1y_i - r2y_i;
var k22 = sum_m_inv + r1.x * r1x_i + r2.x * r2x_i;
var k23 = r1x_i + r2x_i;
var k33 = body1.i_inv + body2.i_inv;
var em_inv = new mat3(k11, k12, k13, k12, k22, k23, k13, k23, k33);
if (this.frequencyHz > 0) {
// Position constraint
var c1 = vec2.sub(vec2.add(body2.p, r2), vec2.add(body1.p, r1));
var c2 = 0;
var correction = vec2.truncate(c1, Joint.MAX_LINEAR_CORRECTION);
// Compute lambda for position constraint
// Solve J1 * invM * J1T * lambda = -C / dt
var lambda_dt_xy = em_inv.solve2x2(correction.neg());
// Apply constraint impulses
// impulse = J1T * lambda
// X += impulse * invM * dt
body1.p.mad(lambda_dt_xy, -body1.m_inv);
body1.a -= vec2.cross(r1, lambda_dt_xy) * body1.i_inv;
body2.p.mad(lambda_dt_xy, body2.m_inv);
body2.a += vec2.cross(r2, lambda_dt_xy) * body2.i_inv;
}
else {
// Position constraint
var c1 = vec2.sub(vec2.add(body2.p, r2), vec2.add(body1.p, r1));
var c2 = body2.a - body1.a;
var correction = vec3.fromVec2(
vec2.truncate(c1, Joint.MAX_LINEAR_CORRECTION),
Math.clamp(c2, -Joint.MAX_ANGULAR_CORRECTION, Joint.MAX_ANGULAR_CORRECTION));
// Compute lambda for position constraint
// Solve J * invM * JT * lambda = -C / dt
var lambda_dt = em_inv.solve(correction.neg());
// Apply constraint impulses
// impulse = JT * lambda
// X += impulse * invM * dt
var lambda_dt_xy = new vec2(lambda_dt.x, lambda_dt.y);
body1.p.mad(lambda_dt_xy, -body1.m_inv);
body1.a -= (vec2.cross(r1, lambda_dt_xy) + lambda_dt.z) * body1.i_inv;
body2.p.mad(lambda_dt_xy, body2.m_inv);
body2.a += (vec2.cross(r2, lambda_dt_xy) + lambda_dt.z) * body2.i_inv;
}
return c1.length() < Joint.LINEAR_SLOP && Math.abs(c2) <= Joint.ANGULAR_SLOP;
}
WeldJoint.prototype.getReactionForce = function(dt_inv) {
return vec2.scale(this.lambda_acc.toVec2(), dt_inv);
}
WeldJoint.prototype.getReactionTorque = function(dt_inv) {
return this.lambda_acc.z * dt_inv;
}
-376
View File
@@ -1,376 +0,0 @@
/*
* Copyright (c) 2012 Ju Hyung Lee
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
//-------------------------------------------------------------------------------------------------
// Wheel Joint
//
// Point-to-Line constraint:
// d = p2 - p1
// n = normalize(perp(d))
// C = dot(n, d)
// Cdot = dot(d, dn/dt) + dot(n dd/dt)
// = dot(d, cross(w1, n)) + dot(n, v2 + cross(w2, r2) - v1 - cross(w1, r1))
// = dot(d, cross(w1, n)) + dot(n, v2) + dot(n, cross(w2, r2)) - dot(n, v1) - dot(n, cross(w1, r1))
// = -dot(n, v1) - dot(cross(d + r1, n), w1) + dot(n, v2) + dot(cross(r2, n), w2)
// J = [ -n, -sn1, n, sn2 ]
// sn1 = cross(r1 + d, n)
// sn2 = cross(r2, n)
//
// impulse = JT * lambda = [ -n * lambda, -(sn1 * lambda), n * lambda, sn2 * lambda ]
//
// Spring constraint:
// u = normalize(d)
// C = dot(u, d)
// Cdot = -dot(u, v1) - dot(cross(d + r1, u), w1) + dot(u, v2) + dot(cross(r2, u), w2)
// J = [ -u, -su1, u, su2 ]
// su1 = cross(r1 + d, u)
// su2 = cross(r2, u)
//
// impulse = JT * lambda = [ -u * lambda, -(su1 * lambda), u * lambda, su2 * lambda ]
//
// Motor rotational constraint:
// Cdot = w2 - w1
// J = [ 0, -1, 0, 1 ]
//-------------------------------------------------------------------------------------------------
WheelJoint = function(body1, body2, anchor1, anchor2) {
Joint.call(this, Joint.TYPE_WHEEL, body1, body2, true);
// Local anchor points
this.anchor1 = this.body1.getLocalPoint(anchor1);
this.anchor2 = this.body2.getLocalPoint(anchor2);
var d = vec2.sub(anchor2, anchor1);
// Rest length
this.restLength = d.length();
// Body1's local axis
this.u_local = this.body1.getLocalVector(vec2.normalize(d));
this.n_local = vec2.perp(this.u_local);
// Accumulated impulse
this.lambda_acc = 0;
this.motorLambda_acc = 0;
this.springLambda_acc = 0;
// Motor
this.motorEnabled = false;
this.motorSpeed = 0;
this.maxMotorTorque = 0;
// Soft constraint coefficients
this.gamma = 0;
this.beta_c = 0;
// Spring coefficients
this.frequencyHz = 0;
this.dampingRatio = 0;
}
WheelJoint.prototype = new Joint;
WheelJoint.prototype.constructor = WheelJoint;
WheelJoint.prototype.setWorldAnchor1 = function(anchor1) {
this.anchor1 = this.body1.getLocalPoint(anchor1);
var d = vec2.sub(this.getWorldAnchor2(), anchor1);
this.u_local = this.body1.getLocalVector(vec2.normalize(d));
this.n_local = vec2.perp(this.u_local);
}
WheelJoint.prototype.setWorldAnchor2 = function(anchor2) {
this.anchor2 = this.body2.getLocalPoint(anchor2);
var d = vec2.sub(anchor2, this.getWorldAnchor1());
this.u_local = this.body1.getLocalVector(vec2.normalize(d));
this.n_local = vec2.perp(this.u_local);
}
WheelJoint.prototype.serialize = function() {
return {
"type": "WheelJoint",
"body1": this.body1.id,
"body2": this.body2.id,
"anchor1": this.body1.getWorldPoint(this.anchor1),
"anchor2": this.body2.getWorldPoint(this.anchor2),
"collideConnected": this.collideConnected,
"maxForce": this.maxForce,
"breakable": this.breakable,
"motorEnabled": this.motorEnabled,
"motorSpeed": this.motorSpeed,
"maxMotorTorque": this.maxMotorTorque,
"frequencyHz": this.frequencyHz,
"dampingRatio": this.dampingRatio
};
}
WheelJoint.prototype.setSpringFrequencyHz = function(frequencyHz) {
// NOTE: frequencyHz should be limited to under 4 times time steps
this.frequencyHz = frequencyHz;
}
WheelJoint.prototype.setSpringDampingRatio = function(dampingRatio) {
this.dampingRatio = dampingRatio;
}
WheelJoint.prototype.enableMotor = function(flag) {
this.motorEnabled = flag;
}
WheelJoint.prototype.setMotorSpeed = function(speed) {
this.motorSpeed = speed;
}
WheelJoint.prototype.setMaxMotorTorque = function(torque) {
this.maxMotorTorque = torque;
}
WheelJoint.prototype.initSolver = function(dt, warmStarting) {
var body1 = this.body1;
var body2 = this.body2;
// Max impulse
this.maxImpulse = this.maxForce * dt;
// Transformed r1, r2
this.r1 = body1.xf.rotate(vec2.sub(this.anchor1, body1.centroid));
this.r2 = body2.xf.rotate(vec2.sub(this.anchor2, body2.centroid));
// World anchor points
var p1 = vec2.add(body1.p, this.r1);
var p2 = vec2.add(body2.p, this.r2);
// Delta vector between world anchor points
var d = vec2.sub(p2, p1);
// r1 + d
this.r1_d = vec2.add(this.r1, d);
// World line normal
this.n = vec2.rotate(this.n_local, body1.a);
// sn1, sn2
this.sn1 = vec2.cross(this.r1_d, this.n);
this.sn2 = vec2.cross(this.r2, this.n);
// invEM = J * invM * JT
var em_inv = body1.m_inv + body2.m_inv + body1.i_inv * this.sn1 * this.sn1 + body2.i_inv * this.sn2 * this.sn2;
this.em = em_inv > 0 ? 1 / em_inv : em_inv;
// Compute soft constraint parameters
if (this.frequencyHz > 0) {
// World delta axis
this.u = vec2.rotate(this.u_local, body1.a);
// su1, su2
this.su1 = vec2.cross(this.r1_d, this.u);
this.su2 = vec2.cross(this.r2, this.u);
// invEM = J * invM * JT
var springEm_inv = body1.m_inv + body2.m_inv + body1.i_inv * this.su1 * this.su1 + body2.i_inv * this.su2 * this.su2;
springEm = springEm_inv == 0 ? 0 : 1 / springEm_inv;
// Frequency
var omega = 2 * Math.PI * this.frequencyHz;
// Spring stiffness
var k = springEm * omega * omega;
// Damping coefficient
var c = springEm * 2 * this.dampingRatio * omega;
// Soft constraint formulas
// gamma and beta are divided by dt to reduce computation
this.gamma = (c + k * dt) * dt;
this.gamma = this.gamma == 0 ? 0 : 1 / this.gamma;
var beta = dt * k * this.gamma;
// Position constraint
var pc = vec2.dot(d, this.u) - this.restLength;
this.beta_c = beta * pc;
// invEM = invEM + gamma * I (to reduce calculation)
springEm_inv = springEm_inv + this.gamma;
this.springEm = springEm_inv == 0 ? 0 : 1 / springEm_inv;
}
else {
this.gamma = 0;
this.beta_c = 0;
this.springLambda_acc = 0;
}
if (this.motorEnabled) {
this.maxMotorImpulse = this.maxMotorTorque * dt;
// invEM2 = J2 * invM * J2T
var motorEm_inv = body1.i_inv + body2.i_inv;
this.motorEm = motorEm_inv > 0 ? 1 / motorEm_inv : motorEm_inv;
}
else {
this.motorEm = 0;
this.motorLambda_acc = 0;
}
if (warmStarting) {
// impulse = JT * lambda
var linearImpulse = vec2.scale(this.n, this.lambda_acc);
var angularImpulse1 = this.sn1 * this.lambda_acc + this.motorLambda_acc;
var angularImpulse2 = this.sn2 * this.lambda_acc + this.motorLambda_acc;
if (this.frequencyHz > 0) {
linearImpulse.addself(vec2.scale(this.u, this.springLambda_acc));
angularImpulse1 += this.su1 * this.springLambda_acc;
angularImpulse2 += this.su2 * this.springLambda_acc;
}
// Apply cached constraint impulses
// V += JT * lambda * invM
body1.v.mad(linearImpulse, -body1.m_inv);
body1.w -= angularImpulse1 * body1.i_inv;
body2.v.mad(linearImpulse, body2.m_inv);
body2.w += angularImpulse2 * body2.i_inv;
}
else {
this.lambda_acc = 0;
this.springLambda_acc = 0;
this.motorLambda_acc = 0;
}
}
WheelJoint.prototype.solveVelocityConstraints = function() {
var body1 = this.body1;
var body2 = this.body2;
// Solve spring constraint
if (this.frequencyHz > 0) {
// Compute lambda for velocity constraint
// Solve J * invM * JT * lambda = -(J * V + beta * C + gamma * (lambda_acc + lambda))
var cdot = this.u.dot(vec2.sub(body2.v, body1.v)) + this.su2 * body2.w - this.su1 * body1.w;
var soft = this.beta_c + this.gamma * this.springLambda_acc;
var lambda = -this.springEm * (cdot + soft);
// Accumulate lambda
this.springLambda_acc += lambda;
// linearImpulse = JT * lambda
var impulse = vec2.scale(this.u, lambda);
// Apply constraint impulses
// V += JT * lambda * invM
body1.v.mad(impulse, -body1.m_inv);
body1.w -= this.su1 * lambda * body1.i_inv;
body2.v.mad(impulse, body2.m_inv);
body2.w += this.su2 * lambda * body2.i_inv;
}
// Solve motor constraint
if (this.motorEnabled) {
// Compute motor impulse
var cdot = body2.w - body1.w - this.motorSpeed;
var lambda = -this.motorEm * cdot;
var motorLambdaOld = this.motorLambda_acc;
this.motorLambda_acc = Math.clamp(this.motorLambda_acc + lambda, -this.maxMotorImpulse, this.maxMotorImpulse);
lambda = this.motorLambda_acc - motorLambdaOld;
// Apply motor impulses
body1.w -= lambda * body1.i_inv;
body2.w += lambda * body2.i_inv;
}
// Compute lambda for velocity constraint
// Solve J * invM * JT * lambda = -J * V
var cdot = this.n.dot(vec2.sub(body2.v, body1.v)) + this.sn2 * body2.w - this.sn1 * body1.w;
var lambda = -this.em * cdot;
// Accumulate lambda
this.lambda_acc += lambda;
// linearImpulse = JT * lambda
var impulse = vec2.scale(this.n, lambda);
// Apply constraint impulses
// V += JT * lambda * invM
body1.v.mad(impulse, -body1.m_inv);
body1.w -= this.sn1 * lambda * body1.i_inv;
body2.v.mad(impulse, body2.m_inv);
body2.w += this.sn2 * lambda * body2.i_inv;
}
WheelJoint.prototype.solvePositionConstraints = function() {
var body1 = this.body1;
var body2 = this.body2;
// Transformed r1, r2
var r1 = vec2.rotate(vec2.sub(this.anchor1, body1.centroid), body1.a);
var r2 = vec2.rotate(vec2.sub(this.anchor2, body2.centroid), body2.a);
// World anchor points
var p1 = vec2.add(body1.p, r1);
var p2 = vec2.add(body2.p, r2);
// Delta vector between world anchor points
var d = vec2.sub(p2, p1);
// r1 + d
var r1_d = vec2.add(r1, d);
// World line normal
var n = vec2.rotate(this.n_local, body1.a);
// Position constraint
var c = vec2.dot(n, d);
var correction = Math.clamp(c, -Joint.MAX_LINEAR_CORRECTION, Joint.MAX_LINEAR_CORRECTION);
// Compute lambda for position constraint
// Solve J * invM * JT * lambda = -C / dt
var s1 = vec2.cross(r1_d, n);
var s2 = vec2.cross(r2, n);
var em_inv = body1.m_inv + body2.m_inv + body1.i_inv * s1 * s1 + body2.i_inv * s2 * s2;
var k_inv = em_inv == 0 ? 0 : 1 / em_inv;
var lambda_dt = k_inv * (-correction);
// Apply constraint impulses
// impulse = JT * lambda
// X += impulse * invM * dt
var impulse_dt = vec2.scale(n, lambda_dt);
body1.p.mad(impulse_dt, -body1.m_inv);
body1.a -= s1 * lambda_dt * body1.i_inv;
body2.p.mad(impulse_dt, body2.m_inv);
body2.a += s2 * lambda_dt * body2.i_inv;
return Math.abs(c) < Joint.LINEAR_SLOP;
}
WheelJoint.prototype.getReactionForce = function(dt_inv) {
return vec2.scale(this.n, this.lambda_acc * dt_inv);
}
WheelJoint.prototype.getReactionTorque = function(dt_inv) {
return 0;
}
@@ -0,0 +1,81 @@
module.exports = ContactMaterial;
var idCounter = 0;
/**
* Defines a physics material.
* @class ContactMaterial
* @constructor
* @param {Material} materialA
* @param {Material} materialB
* @param {Object} [options]
* @param {Number} options.friction
* @param {Number} options.restitution
* @author schteppe
*/
function ContactMaterial(materialA, materialB, options){
options = options || {};
/**
* The contact material identifier
* @property id
* @type {Number}
*/
this.id = idCounter++;
/**
* First material participating in the contact material
* @property materialA
* @type {Material}
*/
this.materialA = materialA;
/**
* Second material participating in the contact material
* @property materialB
* @type {Material}
*/
this.materialB = materialB;
/**
* Friction to use in the contact of these two materials
* @property friction
* @type {Number}
*/
this.friction = typeof(options.friction) !== "undefined" ? Number(options.friction) : 0.3;
/**
* Restitution to use in the contact of these two materials
* @property restitution
* @type {Number}
*/
this.restitution = typeof(options.restitution) !== "undefined" ? Number(options.restitution) : 0.3;
/**
* Stiffness of the resulting ContactEquation that this ContactMaterial generate
* @property stiffness
* @type {Number}
*/
this.stiffness = typeof(options.stiffness) !== "undefined" ? Number(options.stiffness) : 1e7;
/**
* Relaxation of the resulting ContactEquation that this ContactMaterial generate
* @property relaxation
* @type {Number}
*/
this.relaxation = typeof(options.relaxation) !== "undefined" ? Number(options.relaxation) : 3;
/**
* Stiffness of the resulting FrictionEquation that this ContactMaterial generate
* @property frictionStiffness
* @type {Number}
*/
this.frictionStiffness = typeof(options.frictionStiffness) !== "undefined" ? Number(options.frictionStiffness) : 1e7;
/**
* Relaxation of the resulting FrictionEquation that this ContactMaterial generate
* @property frictionRelaxation
* @type {Number}
*/
this.frictionRelaxation = typeof(options.frictionRelaxation) !== "undefined" ? Number(options.frictionRelaxation) : 3;
};
+19
View File
@@ -0,0 +1,19 @@
module.exports = Material;
var idCounter = 0;
/**
* Defines a physics material.
* @class Material
* @constructor
* @param string name
* @author schteppe
*/
function Material(){
/**
* The material identifier
* @property id
* @type {Number}
*/
this.id = idCounter++;
};
+10
View File
@@ -0,0 +1,10 @@
/**
* The mat2 object from glMatrix, extended with the functions documented here. See http://glmatrix.net for full doc.
* @class mat2
*/
// Only import mat2 from gl-matrix and skip the rest
var mat2 = require('../../node_modules/gl-matrix/src/gl-matrix/mat2').mat2;
// Export everything
module.exports = mat2;
+477
View File
@@ -0,0 +1,477 @@
/*
PolyK library
url: http://polyk.ivank.net
Released under MIT licence.
Copyright (c) 2012 Ivan Kuckir
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
var PolyK = {};
/*
Is Polygon self-intersecting?
O(n^2)
*/
/*
PolyK.IsSimple = function(p)
{
var n = p.length>>1;
if(n<4) return true;
var a1 = new PolyK._P(), a2 = new PolyK._P();
var b1 = new PolyK._P(), b2 = new PolyK._P();
var c = new PolyK._P();
for(var i=0; i<n; i++)
{
a1.x = p[2*i ];
a1.y = p[2*i+1];
if(i==n-1) { a2.x = p[0 ]; a2.y = p[1 ]; }
else { a2.x = p[2*i+2]; a2.y = p[2*i+3]; }
for(var j=0; j<n; j++)
{
if(Math.abs(i-j) < 2) continue;
if(j==n-1 && i==0) continue;
if(i==n-1 && j==0) continue;
b1.x = p[2*j ];
b1.y = p[2*j+1];
if(j==n-1) { b2.x = p[0 ]; b2.y = p[1 ]; }
else { b2.x = p[2*j+2]; b2.y = p[2*j+3]; }
if(PolyK._GetLineIntersection(a1,a2,b1,b2,c) != null) return false;
}
}
return true;
}
PolyK.IsConvex = function(p)
{
if(p.length<6) return true;
var l = p.length - 4;
for(var i=0; i<l; i+=2)
if(!PolyK._convex(p[i], p[i+1], p[i+2], p[i+3], p[i+4], p[i+5])) return false;
if(!PolyK._convex(p[l ], p[l+1], p[l+2], p[l+3], p[0], p[1])) return false;
if(!PolyK._convex(p[l+2], p[l+3], p[0 ], p[1 ], p[2], p[3])) return false;
return true;
}
*/
PolyK.GetArea = function(p)
{
if(p.length <6) return 0;
var l = p.length - 2;
var sum = 0;
for(var i=0; i<l; i+=2)
sum += (p[i+2]-p[i]) * (p[i+1]+p[i+3]);
sum += (p[0]-p[l]) * (p[l+1]+p[1]);
return - sum * 0.5;
}
/*
PolyK.GetAABB = function(p)
{
var minx = Infinity;
var miny = Infinity;
var maxx = -minx;
var maxy = -miny;
for(var i=0; i<p.length; i+=2)
{
minx = Math.min(minx, p[i ]);
maxx = Math.max(maxx, p[i ]);
miny = Math.min(miny, p[i+1]);
maxy = Math.max(maxy, p[i+1]);
}
return {x:minx, y:miny, width:maxx-minx, height:maxy-miny};
}
*/
PolyK.Triangulate = function(p)
{
var n = p.length>>1;
if(n<3) return [];
var tgs = [];
var avl = [];
for(var i=0; i<n; i++) avl.push(i);
var i = 0;
var al = n;
while(al > 3)
{
var i0 = avl[(i+0)%al];
var i1 = avl[(i+1)%al];
var i2 = avl[(i+2)%al];
var ax = p[2*i0], ay = p[2*i0+1];
var bx = p[2*i1], by = p[2*i1+1];
var cx = p[2*i2], cy = p[2*i2+1];
var earFound = false;
if(PolyK._convex(ax, ay, bx, by, cx, cy))
{
earFound = true;
for(var j=0; j<al; j++)
{
var vi = avl[j];
if(vi==i0 || vi==i1 || vi==i2) continue;
if(PolyK._PointInTriangle(p[2*vi], p[2*vi+1], ax, ay, bx, by, cx, cy)) {earFound = false; break;}
}
}
if(earFound)
{
tgs.push(i0, i1, i2);
avl.splice((i+1)%al, 1);
al--;
i= 0;
}
else if(i++ > 3*al) break; // no convex angles :(
}
tgs.push(avl[0], avl[1], avl[2]);
return tgs;
}
/*
PolyK.ContainsPoint = function(p, px, py)
{
var n = p.length>>1;
var ax, ay, bx = p[2*n-2]-px, by = p[2*n-1]-py;
var depth = 0;
for(var i=0; i<n; i++)
{
ax = bx; ay = by;
bx = p[2*i ] - px;
by = p[2*i+1] - py;
if(ay< 0 && by< 0) continue; // both "up" or both "donw"
if(ay>=0 && by>=0) continue; // both "up" or both "donw"
if(ax< 0 && bx< 0) continue;
var lx = ax + (bx-ax)*(-ay)/(by-ay);
if(lx>0) depth++;
}
return (depth & 1) == 1;
}
PolyK.Slice = function(p, ax, ay, bx, by)
{
if(PolyK.ContainsPoint(p, ax, ay) || PolyK.ContainsPoint(p, bx, by)) return [p.slice(0)];
var a = new PolyK._P(ax, ay);
var b = new PolyK._P(bx, by);
var iscs = []; // intersections
var ps = []; // points
for(var i=0; i<p.length; i+=2) ps.push(new PolyK._P(p[i], p[i+1]));
for(var i=0; i<ps.length; i++)
{
var isc = new PolyK._P(0,0);
isc = PolyK._GetLineIntersection(a, b, ps[i], ps[(i+1)%ps.length], isc);
if(isc)
{
isc.flag = true;
iscs.push(isc);
ps.splice(i+1,0,isc);
i++;
}
}
if(iscs.length == 0) return [p.slice(0)];
var comp = function(u,v) {return PolyK._P.dist(a,u) - PolyK._P.dist(a,v); }
iscs.sort(comp);
var pgs = [];
var dir = 0;
while(iscs.length > 0)
{
var n = ps.length;
var i0 = iscs[0];
var i1 = iscs[1];
var ind0 = ps.indexOf(i0);
var ind1 = ps.indexOf(i1);
var solved = false;
if(PolyK._firstWithFlag(ps, ind0) == ind1) solved = true;
else
{
i0 = iscs[1];
i1 = iscs[0];
ind0 = ps.indexOf(i0);
ind1 = ps.indexOf(i1);
if(PolyK._firstWithFlag(ps, ind0) == ind1) solved = true;
}
if(solved)
{
dir--;
var pgn = PolyK._getPoints(ps, ind0, ind1);
pgs.push(pgn);
ps = PolyK._getPoints(ps, ind1, ind0);
i0.flag = i1.flag = false;
iscs.splice(0,2);
if(iscs.length == 0) pgs.push(ps);
}
else { dir++; iscs.reverse(); }
if(dir>1) break;
}
var result = [];
for(var i=0; i<pgs.length; i++)
{
var pg = pgs[i];
var npg = [];
for(var j=0; j<pg.length; j++) npg.push(pg[j].x, pg[j].y);
result.push(npg);
}
return result;
}
PolyK.Raycast = function(p, x, y, dx, dy, isc)
{
var l = p.length - 2;
var tp = PolyK._tp;
var a1 = tp[0], a2 = tp[1],
b1 = tp[2], b2 = tp[3], c = tp[4];
a1.x = x; a1.y = y;
a2.x = x+dx; a2.y = y+dy;
if(isc==null) isc = {dist:0, edge:0, norm:{x:0, y:0}, refl:{x:0, y:0}};
isc.dist = Infinity;
for(var i=0; i<l; i+=2)
{
b1.x = p[i ]; b1.y = p[i+1];
b2.x = p[i+2]; b2.y = p[i+3];
var nisc = PolyK._RayLineIntersection(a1, a2, b1, b2, c);
if(nisc) PolyK._updateISC(dx, dy, a1, b1, b2, c, i/2, isc);
}
b1.x = b2.x; b1.y = b2.y;
b2.x = p[0]; b2.y = p[1];
var nisc = PolyK._RayLineIntersection(a1, a2, b1, b2, c);
if(nisc) PolyK._updateISC(dx, dy, a1, b1, b2, c, p.length/2, isc);
return (isc.dist != Infinity) ? isc : null;
}
PolyK.ClosestEdge = function(p, x, y, isc)
{
var l = p.length - 2;
var tp = PolyK._tp;
var a1 = tp[0],
b1 = tp[2], b2 = tp[3], c = tp[4];
a1.x = x; a1.y = y;
if(isc==null) isc = {dist:0, edge:0, point:{x:0, y:0}, norm:{x:0, y:0}};
isc.dist = Infinity;
for(var i=0; i<l; i+=2)
{
b1.x = p[i ]; b1.y = p[i+1];
b2.x = p[i+2]; b2.y = p[i+3];
PolyK._pointLineDist(a1, b1, b2, i>>1, isc);
}
b1.x = b2.x; b1.y = b2.y;
b2.x = p[0]; b2.y = p[1];
PolyK._pointLineDist(a1, b1, b2, l>>1, isc);
var idst = 1/isc.dist;
isc.norm.x = (x-isc.point.x)*idst;
isc.norm.y = (y-isc.point.y)*idst;
return isc;
}
PolyK._pointLineDist = function(p, a, b, edge, isc)
{
var x = p.x, y = p.y, x1 = a.x, y1 = a.y, x2 = b.x, y2 = b.y;
var A = x - x1;
var B = y - y1;
var C = x2 - x1;
var D = y2 - y1;
var dot = A * C + B * D;
var len_sq = C * C + D * D;
var param = dot / len_sq;
var xx, yy;
if (param < 0 || (x1 == x2 && y1 == y2)) {
xx = x1;
yy = y1;
}
else if (param > 1) {
xx = x2;
yy = y2;
}
else {
xx = x1 + param * C;
yy = y1 + param * D;
}
var dx = x - xx;
var dy = y - yy;
var dst = Math.sqrt(dx * dx + dy * dy);
if(dst<isc.dist)
{
isc.dist = dst;
isc.edge = edge;
isc.point.x = xx;
isc.point.y = yy;
}
}
PolyK._updateISC = function(dx, dy, a1, b1, b2, c, edge, isc)
{
var nrl = PolyK._P.dist(a1, c);
if(nrl<isc.dist)
{
var ibl = 1/PolyK._P.dist(b1, b2);
var nx = -(b2.y-b1.y)*ibl;
var ny = (b2.x-b1.x)*ibl;
var ddot = 2*(dx*nx+dy*ny);
isc.dist = nrl;
isc.norm.x = nx;
isc.norm.y = ny;
isc.refl.x = -ddot*nx+dx;
isc.refl.y = -ddot*ny+dy;
isc.edge = edge;
}
}
PolyK._getPoints = function(ps, ind0, ind1)
{
var n = ps.length;
var nps = [];
if(ind1<ind0) ind1 += n;
for(var i=ind0; i<= ind1; i++) nps.push(ps[i%n]);
return nps;
}
PolyK._firstWithFlag = function(ps, ind)
{
var n = ps.length;
while(true)
{
ind = (ind+1)%n;
if(ps[ind].flag) return ind;
}
}
*/
PolyK._PointInTriangle = function(px, py, ax, ay, bx, by, cx, cy)
{
var v0x = cx-ax;
var v0y = cy-ay;
var v1x = bx-ax;
var v1y = by-ay;
var v2x = px-ax;
var v2y = py-ay;
var dot00 = v0x*v0x+v0y*v0y;
var dot01 = v0x*v1x+v0y*v1y;
var dot02 = v0x*v2x+v0y*v2y;
var dot11 = v1x*v1x+v1y*v1y;
var dot12 = v1x*v2x+v1y*v2y;
var invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
var u = (dot11 * dot02 - dot01 * dot12) * invDenom;
var v = (dot00 * dot12 - dot01 * dot02) * invDenom;
// Check if point is in triangle
return (u >= 0) && (v >= 0) && (u + v < 1);
}
/*
PolyK._RayLineIntersection = function(a1, a2, b1, b2, c)
{
var dax = (a1.x-a2.x), dbx = (b1.x-b2.x);
var day = (a1.y-a2.y), dby = (b1.y-b2.y);
var Den = dax*dby - day*dbx;
if (Den == 0) return null; // parallel
var A = (a1.x * a2.y - a1.y * a2.x);
var B = (b1.x * b2.y - b1.y * b2.x);
var I = c;
var iDen = 1/Den;
I.x = ( A*dbx - dax*B ) * iDen;
I.y = ( A*dby - day*B ) * iDen;
if(!PolyK._InRect(I, b1, b2)) return null;
if((day>0 && I.y>a1.y) || (day<0 && I.y<a1.y)) return null;
if((dax>0 && I.x>a1.x) || (dax<0 && I.x<a1.x)) return null;
return I;
}
PolyK._GetLineIntersection = function(a1, a2, b1, b2, c)
{
var dax = (a1.x-a2.x), dbx = (b1.x-b2.x);
var day = (a1.y-a2.y), dby = (b1.y-b2.y);
var Den = dax*dby - day*dbx;
if (Den == 0) return null; // parallel
var A = (a1.x * a2.y - a1.y * a2.x);
var B = (b1.x * b2.y - b1.y * b2.x);
var I = c;
I.x = ( A*dbx - dax*B ) / Den;
I.y = ( A*dby - day*B ) / Den;
if(PolyK._InRect(I, a1, a2) && PolyK._InRect(I, b1, b2)) return I;
return null;
}
PolyK._InRect = function(a, b, c)
{
if (b.x == c.x) return (a.y>=Math.min(b.y, c.y) && a.y<=Math.max(b.y, c.y));
if (b.y == c.y) return (a.x>=Math.min(b.x, c.x) && a.x<=Math.max(b.x, c.x));
if(a.x >= Math.min(b.x, c.x) && a.x <= Math.max(b.x, c.x)
&& a.y >= Math.min(b.y, c.y) && a.y <= Math.max(b.y, c.y))
return true;
return false;
}
*/
PolyK._convex = function(ax, ay, bx, by, cx, cy)
{
return (ay-by)*(cx-bx) + (bx-ax)*(cy-by) >= 0;
}
/*
PolyK._P = function(x,y)
{
this.x = x;
this.y = y;
this.flag = false;
}
PolyK._P.prototype.toString = function()
{
return "Point ["+this.x+", "+this.y+"]";
}
PolyK._P.dist = function(a,b)
{
var dx = b.x-a.x;
var dy = b.y-a.y;
return Math.sqrt(dx*dx + dy*dy);
}
PolyK._tp = [];
for(var i=0; i<10; i++) PolyK._tp.push(new PolyK._P(0,0));
*/
module.exports = PolyK;
+122
View File
@@ -0,0 +1,122 @@
/**
* The vec2 object from glMatrix, extended with the functions documented here. See http://glmatrix.net for full doc.
* @class vec2
*/
// Only import vec2 from gl-matrix and skip the rest
var vec2 = require('../../node_modules/gl-matrix/src/gl-matrix/vec2').vec2;
// Now add some extensions
/**
* Get the vector x component
* @method getX
* @static
* @param {Float32Array} a
* @return {Number}
*/
vec2.getX = function(a){
return a[0];
};
/**
* Get the vector y component
* @method getY
* @static
* @param {Float32Array} a
* @return {Number}
*/
vec2.getY = function(a){
return a[1];
};
/**
* Make a cross product and only return the z component
* @method crossLength
* @static
* @param {Float32Array} a
* @param {Float32Array} b
* @return {Number}
*/
vec2.crossLength = function(a,b){
return a[0] * b[1] - a[1] * b[0];
};
/**
* Cross product between a vector and the Z component of a vector
* @method crossVZ
* @static
* @param {Float32Array} out
* @param {Float32Array} vec
* @param {Number} zcomp
* @return {Number}
*/
vec2.crossVZ = function(out, vec, zcomp){
vec2.rotate(out,vec,-Math.PI/2);// Rotate according to the right hand rule
vec2.scale(out,out,zcomp); // Scale with z
return out;
};
/**
* Cross product between a vector and the Z component of a vector
* @method crossZV
* @static
* @param {Float32Array} out
* @param {Number} zcomp
* @param {Float32Array} vec
* @return {Number}
*/
vec2.crossZV = function(out, zcomp, vec){
vec2.rotate(out,vec,Math.PI/2); // Rotate according to the right hand rule
vec2.scale(out,out,zcomp); // Scale with z
return out;
};
/**
* Rotate a vector by an angle
* @method rotate
* @static
* @param {Float32Array} out
* @param {Float32Array} a
* @param {Number} angle
*/
vec2.rotate = function(out,a,angle){
var c = Math.cos(angle),
s = Math.sin(angle),
x = a[0],
y = a[1];
out[0] = c*x -s*y;
out[1] = s*x +c*y;
};
vec2.toLocalFrame = function(out, worldPoint, framePosition, frameAngle){
vec2.copy(out, worldPoint);
vec2.sub(out, out, framePosition);
vec2.rotate(out, out, -frameAngle);
};
vec2.toGlobalFrame = function(out, localPoint, framePosition, frameAngle){
vec2.copy(out, localPoint);
vec2.rotate(out, out, frameAngle);
vec2.add(out, out, framePosition);
};
/**
* Compute centroid of a triangle spanned by vectors a,b,c. See http://easycalculation.com/analytical/learn-centroid.php
* @method centroid
* @static
* @param {Float32Array} out
* @param {Float32Array} a
* @param {Float32Array} b
* @param {Float32Array} c
* @return {Float32Array} The out object
*/
vec2.centroid = function(out, a, b, c){
vec2.add(out, a, b);
vec2.add(out, out, c);
vec2.scale(out, out, 1/3);
return out;
};
// Export everything
module.exports = vec2;
+330
View File
@@ -0,0 +1,330 @@
var vec2 = require('../math/vec2');
module.exports = Body;
var zero = vec2.fromValues(0,0);
/**
* A rigid body. Has got a center of mass, position, velocity and a number of
* shapes that are used for collisions.
*
* @class Body
* @constructor
* @param {Object} [options]
* @param {Number} [options.mass=0] A number >= 0. If zero, the .motionState will be set to Body.STATIC.
* @param {Float32Array|Array} [options.position]
* @param {Float32Array|Array} [options.velocity]
* @param {Number} [options.angle=0]
* @param {Number} [options.angularVelocity=0]
* @param {Float32Array|Array} [options.force]
* @param {Number} [options.angularForce=0]
*
* @todo Should not take mass as argument to Body, but as density to each Shape
*/
function Body(options){
options = options || {};
/**
* The body identifyer
* @property id
* @type {Number}
*/
this.id = ++Body._idCounter;
/**
* The shapes of the body. The local transform of the shape in .shapes[i] is
* defined by .shapeOffsets[i] and .shapeAngles[i].
*
* @property shapes
* @type {Array}
*/
this.shapes = [];
/**
* The local shape offsets, relative to the body center of mass. This is an
* array of Float32Array.
* @property shapeOffsets
* @type {Array}
*/
this.shapeOffsets = [];
/**
* The body-local shape angle transforms. This is an array of numbers (angles).
* @property shapeAngles
* @type {Array}
*/
this.shapeAngles = [];
/**
* The mass of the body.
* @property mass
* @type {number}
*/
this.mass = options.mass || 0;
/**
* The inverse mass of the body.
* @property invMass
* @type {number}
*/
this.invMass = 0;
/**
* The inertia of the body around the Z axis.
* @property inertia
* @type {number}
*/
this.inertia = 0;
/**
* The inverse inertia of the body.
* @property invInertia
* @type {number}
*/
this.invInertia = 0;
this.updateMassProperties();
/**
* The position of the body
* @property position
* @type {Float32Array}
*/
this.position = vec2.fromValues(0,0);
if(options.position) vec2.copy(this.position, options.position);
/**
* The velocity of the body
* @property velocity
* @type {Float32Array}
*/
this.velocity = vec2.fromValues(0,0);
if(options.velocity) vec2.copy(this.velocity, options.velocity);
/**
* Constraint velocity that was added to the body during the last step.
* @property vlambda
* @type {Float32Array}
*/
this.vlambda = vec2.fromValues(0,0);
/**
* Angular constraint velocity that was added to the body during last step.
* @property wlambda
* @type {Float32Array}
*/
this.wlambda = 0;
/**
* The angle of the body
* @property angle
* @type {number}
*/
this.angle = options.angle || 0;
/**
* The angular velocity of the body
* @property angularVelocity
* @type {number}
*/
this.angularVelocity = options.angularVelocity || 0;
/**
* The force acting on the body
* @property force
* @type {Float32Array}
*/
this.force = vec2.create();
if(options.force) vec2.copy(this.force, options.force);
/**
* The angular force acting on the body
* @property angularForce
* @type {number}
*/
this.angularForce = options.angularForce || 0;
/**
* The type of motion this body has. Should be one of: Body.STATIC (the body
* does not move), Body.DYNAMIC (body can move and respond to collisions)
* and Body.KINEMATIC (only moves according to its .velocity).
*
* @property motionState
* @type {number}
*
* @example
* // This body will move and interact with other bodies
* var dynamicBody = new Body();
* dynamicBody.motionState = Body.DYNAMIC;
*
* @example
* // This body will not move at all
* var staticBody = new Body();
* staticBody.motionState = Body.STATIC;
*
* @example
* // This body will only move if you change its velocity
* var kinematicBody = new Body();
* kinematicBody.motionState = Body.KINEMATIC;
*/
this.motionState = this.mass == 0 ? Body.STATIC : Body.DYNAMIC;
/**
* Bounding circle radius
* @property boundingRadius
* @type {Number}
*/
this.boundingRadius = 0;
};
Body._idCounter = 0;
/**
* Update the bounding radius of the body. Should be done if any of the shapes
* are changed.
* @method updateBoundingRadius
*/
Body.prototype.updateBoundingRadius = function(){
var shapes = this.shapes,
shapeOffsets = this.shapeOffsets,
N = shapes.length,
radius = 0;
for(var i=0; i!==N; i++){
var shape = shapes[i],
offset = vec2.length(shapeOffsets[i] || zero),
r = shape.boundingRadius;
if(offset + r > radius)
radius = offset + r;
}
this.boundingRadius = radius;
};
/**
* Add a shape to the body. You can pass a local transform when adding a shape,
* so that the shape gets an offset and angle relative to the body center of mass.
* Will automatically update the mass properties and bounding radius.
*
* @method addShape
* @param {Shape} shape
* @param {Float32Array|Array} [offset] Local body offset of the shape.
* @param {Number} [angle] Local body angle.
*
* @example
* var body = new Body(),
* shape = new Circle();
*
* // Add the shape to the body, positioned in the center
* body.addShape(shape);
*
* // Add another shape to the body, positioned 1 unit length from the body center of mass along the local x-axis.
* body.addShape(shape,[1,0]);
*
* // Add another shape to the body, positioned 1 unit length from the body center of mass along the local y-axis, and rotated 90 degrees CCW.
* body.addShape(shape,[0,1],Math.PI/2);
*/
Body.prototype.addShape = function(shape,offset,angle){
this.shapes .push(shape);
this.shapeOffsets.push(offset);
this.shapeAngles .push(angle);
this.updateMassProperties();
this.updateBoundingRadius();
};
/**
* Updates .inertia, .invMass, .invInertia for this Body. Should be called when
* changing the structure or mass of the Body.
*
* @method updateMassProperties
*
* @example
* body.mass += 1;
* body.updateMassProperties();
*/
Body.prototype.updateMassProperties = function(){
var shapes = this.shapes,
N = shapes.length,
m = this.mass / N,
I = 0;
for(var i=0; i<N; i++){
var shape = shapes[i],
r2 = vec2.squaredLength(this.shapeOffsets[i] || zero),
Icm = shape.computeMomentOfInertia(m);
I += Icm + m*r2;
}
this.inertia = I;
// Inverse mass properties are easy
this.invMass = this.mass > 0 ? 1/this.mass : 0;
this.invInertia = I>0 ? 1/I : 0;
};
var Body_applyForce_r = vec2.create();
/**
* Apply force to a world point. This could for example be a point on the RigidBody surface. Applying force this way will add to Body.force and Body.angularForce.
* @method applyForce
* @param {Float32Array} force The force to add.
* @param {Float32Array} worldPoint A world point to apply the force on.
*/
Body.prototype.applyForce = function(force,worldPoint){
// Compute point position relative to the body center
var r = Body_applyForce_r;
vec2.sub(r,worldPoint,this.position);
// Add linear force
vec2.add(this.force,this.force,force);
// Compute produced rotational force
var rotForce = vec2.crossLength(r,force);
// Add rotational force
this.angularForce += rotForce;
};
/**
* Transform a world point to local body frame.
* @method toLocalFrame
* @param {Float32Array|Array} out The vector to store the result in
* @param {Float32Array|Array} worldPoint The input world vector
*/
Body.prototype.toLocalFrame = function(out, worldPoint){
vec2.toLocalFrame(out, worldPoint, this.position, this.angle);
};
/**
* Transform a local point to world frame.
* @method toWorldFrame
* @param {Array} out The vector to store the result in
* @param {Array} localPoint The input local vector
*/
Body.prototype.toWorldFrame = function(out, localPoint){
vec2.toGlobalFrame(out, localPoint, this.position, this.angle);
};
/**
* Dynamic body.
* @property DYNAMIC
* @type {Number}
* @static
*/
Body.DYNAMIC = 1;
/**
* Static body.
* @property STATIC
* @type {Number}
* @static
*/
Body.STATIC = 2;
/**
* Kinematic body.
* @property KINEMATIC
* @type {Number}
* @static
*/
Body.KINEMATIC = 4;
+181
View File
@@ -0,0 +1,181 @@
var vec2 = require('../math/vec2');
module.exports = Spring;
/**
* A spring, connecting two bodies.
*
* @class Spring
* @constructor
* @param {Body} bodyA
* @param {Body} bodyB
* @param {Object} [options]
* @param {number} options.restLength A number > 0. Default: 1
* @param {number} options.stiffness A number >= 0. Default: 100
* @param {number} options.damping A number >= 0. Default: 1
* @param {Array} options.worldAnchorA Where to hook the spring to body A, in world coordinates.
* @param {Array} options.worldAnchorB
* @param {Array} options.localAnchorA Where to hook the spring to body A, in local body coordinates.
* @param {Array} options.localAnchorB
*/
function Spring(bodyA,bodyB,options){
options = options || {};
/**
* Rest length of the spring.
* @property restLength
* @type {number}
*/
this.restLength = typeof(options.restLength)=="number" ? options.restLength : 1;
/**
* Stiffness of the spring.
* @property stiffness
* @type {number}
*/
this.stiffness = options.stiffness || 100;
/**
* Damping of the spring.
* @property damping
* @type {number}
*/
this.damping = options.damping || 1;
/**
* First connected body.
* @property bodyA
* @type {Body}
*/
this.bodyA = bodyA;
/**
* Second connected body.
* @property bodyB
* @type {Body}
*/
this.bodyB = bodyB;
/**
* Anchor for bodyA in local bodyA coordinates.
* @property localAnchorA
* @type {Array}
*/
this.localAnchorA = vec2.fromValues(0,0);
/**
* Anchor for bodyB in local bodyB coordinates.
* @property localAnchorB
* @type {Array}
*/
this.localAnchorB = vec2.fromValues(0,0);
if(options.localAnchorA) vec2.copy(this.localAnchorA, options.localAnchorA);
if(options.localAnchorB) vec2.copy(this.localAnchorB, options.localAnchorB);
if(options.worldAnchorA) this.setWorldAnchorA(options.worldAnchorA);
if(options.worldAnchorB) this.setWorldAnchorB(options.worldAnchorB);
};
/**
* Set the anchor point on body A, using world coordinates.
* @method setWorldAnchorA
* @param {Array} worldAnchorA
*/
Spring.prototype.setWorldAnchorA = function(worldAnchorA){
this.bodyA.toLocalFrame(this.localAnchorA, worldAnchorA);
};
/**
* Set the anchor point on body B, using world coordinates.
* @method setWorldAnchorB
* @param {Array} worldAnchorB
*/
Spring.prototype.setWorldAnchorB = function(worldAnchorB){
this.bodyB.toLocalFrame(this.localAnchorB, worldAnchorB);
};
/**
* Get the anchor point on body A, in world coordinates.
* @method getWorldAnchorA
* @param {Array} result The vector to store the result in.
*/
Spring.prototype.getWorldAnchorA = function(result){
this.bodyA.toWorldFrame(result, this.localAnchorA);
};
/**
* Get the anchor point on body B, in world coordinates.
* @method getWorldAnchorB
* @param {Array} result The vector to store the result in.
*/
Spring.prototype.getWorldAnchorB = function(result){
this.bodyB.toWorldFrame(result, this.localAnchorB);
};
var applyForce_r = vec2.create(),
applyForce_r_unit = vec2.create(),
applyForce_u = vec2.create(),
applyForce_f = vec2.create(),
applyForce_worldAnchorA = vec2.create(),
applyForce_worldAnchorB = vec2.create(),
applyForce_ri = vec2.create(),
applyForce_rj = vec2.create(),
applyForce_tmp = vec2.create();
/**
* Apply the spring force to the connected bodies.
* @method applyForce
*/
Spring.prototype.applyForce = function(){
var k = this.stiffness,
d = this.damping,
l = this.restLength,
bodyA = this.bodyA,
bodyB = this.bodyB,
r = applyForce_r,
r_unit = applyForce_r_unit,
u = applyForce_u,
f = applyForce_f,
tmp = applyForce_tmp;
var worldAnchorA = applyForce_worldAnchorA,
worldAnchorB = applyForce_worldAnchorB,
ri = applyForce_ri,
rj = applyForce_rj;
// Get world anchors
this.getWorldAnchorA(worldAnchorA);
this.getWorldAnchorB(worldAnchorB);
// Get offset points
vec2.sub(ri, worldAnchorA, bodyA.position);
vec2.sub(rj, worldAnchorB, bodyB.position);
// Compute distance vector between world anchor points
vec2.sub(r, worldAnchorB, worldAnchorA);
var rlen = vec2.len(r);
vec2.normalize(r_unit,r);
//console.log(rlen)
//console.log("A",vec2.str(worldAnchorA),"B",vec2.str(worldAnchorB))
// Compute relative velocity of the anchor points, u
vec2.sub(u, bodyB.velocity, bodyA.velocity);
vec2.crossZV(tmp, bodyB.angularVelocity, rj);
vec2.add(u, u, tmp);
vec2.crossZV(tmp, bodyA.angularVelocity, ri);
vec2.sub(u, u, tmp);
// F = - k * ( x - L ) - D * ( u )
vec2.scale(f, r_unit, -k*(rlen-l) - d*vec2.dot(u,r_unit));
// Add forces to bodies
vec2.sub( bodyA.force, bodyA.force, f);
vec2.add( bodyB.force, bodyB.force, f);
// Angular force
var ri_x_f = vec2.crossLength(ri, f);
var rj_x_f = vec2.crossLength(rj, f);
bodyA.angularForce -= ri_x_f;
bodyB.angularForce += rj_x_f;
};
+37
View File
@@ -0,0 +1,37 @@
// Export p2 classes
module.exports = {
Body : require('./objects/Body'),
Broadphase : require('./collision/Broadphase'),
Capsule : require('./shapes/Capsule'),
Circle : require('./shapes/Circle'),
Constraint : require('./constraints/Constraint'),
ContactEquation : require('./constraints/ContactEquation'),
ContactMaterial : require('./material/ContactMaterial'),
Convex : require('./shapes/Convex'),
DistanceConstraint : require('./constraints/DistanceConstraint'),
Equation : require('./constraints/Equation'),
EventEmitter : require('./events/EventEmitter'),
FrictionEquation : require('./constraints/FrictionEquation'),
GridBroadphase : require('./collision/GridBroadphase'),
GSSolver : require('./solver/GSSolver'),
Island : require('./solver/IslandSolver'),
IslandSolver : require('./solver/IslandSolver'),
Line : require('./shapes/Line'),
Material : require('./material/Material'),
NaiveBroadphase : require('./collision/NaiveBroadphase'),
Particle : require('./shapes/Particle'),
Plane : require('./shapes/Plane'),
PointToPointConstraint : require('./constraints/PointToPointConstraint'),
PrismaticConstraint : require('./constraints/PrismaticConstraint'),
Rectangle : require('./shapes/Rectangle'),
RotationalVelocityEquation : require('./constraints/RotationalVelocityEquation'),
SAP1DBroadphase : require('./collision/SAP1DBroadphase'),
Shape : require('./shapes/Shape'),
Solver : require('./solver/Solver'),
Spring : require('./objects/Spring'),
Utils : require('./utils/Utils'),
World : require('./world/World'),
QuadTree : require('./collision/QuadTree').QuadTree,
vec2 : require('./math/vec2'),
version : require('../package.json').version,
};
-19
View File
@@ -1,19 +0,0 @@
//--------------------------------
// Box
//--------------------------------
ShapeBox = function(local_x, local_y, w, h) {
local_x = local_x || 0;
local_y = local_y || 0;
var hw = w * 0.5;
var hh = h * 0.5;
var verts = [
new vec2(-hw + local_x, +hh + local_y),
new vec2(-hw + local_x, -hh + local_y),
new vec2(+hw + local_x, -hh + local_y),
new vec2(+hw + local_x, +hh + local_y)
];
return new ShapePoly(verts);
}
+39
View File
@@ -0,0 +1,39 @@
var Shape = require('./Shape')
, vec2 = require('../math/vec2')
module.exports = Capsule;
/**
* Capsule shape class.
* @class Capsule
* @constructor
* @extends {Shape}
* @param {Number} length The distance between the end points
* @param {Number} radius Radius of the capsule
*/
function Capsule(length,radius){
this.length = length || 1;
this.radius = radius || 1;
Shape.call(this,Shape.CAPSULE);
};
Capsule.prototype = new Shape();
/**
* Compute the mass moment of inertia of the Capsule.
* @method conputeMomentOfInertia
* @param {Number} mass
* @return {Number}
* @todo
*/
Capsule.prototype.computeMomentOfInertia = function(mass){
// Approximate with rectangle
var r = this.radius,
w = this.length + r, // 2*r is too much, 0 is too little
h = r*2;
return mass * (h*h + w*w) / 12;
};
Capsule.prototype.updateBoundingRadius = function(){
this.boundingRadius = this.radius + this.length/2;
};
+26 -97
View File
@@ -1,102 +1,31 @@
/*
* Copyright (c) 2012 Ju Hyung Lee
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var Shape = require('./Shape');
//------------------------------------------
// ShapeCircle
//------------------------------------------
module.exports = Circle;
ShapeCircle = function(local_x, local_y, radius) {
Shape.call(this, Shape.TYPE_CIRCLE);
this.c = new vec2(local_x || 0, local_y || 0);
this.r = radius;
/**
* Circle shape class.
* @class Circle
* @extends {Shape}
* @constructor
* @param {number} radius
*/
function Circle(radius){
this.tc = vec2.zero;
/**
* The radius of the circle.
* @property radius
* @type {number}
*/
this.radius = radius || 1;
this.finishVerts();
}
Shape.call(this,Shape.CIRCLE);
};
Circle.prototype = new Shape();
Circle.prototype.computeMomentOfInertia = function(mass){
var r = this.radius;
return mass * r * r / 2;
};
ShapeCircle.prototype = new Shape;
ShapeCircle.prototype.constructor = ShapeCircle;
ShapeCircle.prototype.finishVerts = function() {
this.r = Math.abs(this.r);
}
ShapeCircle.prototype.duplicate = function() {
return new ShapeCircle(this.c.x, this.c.y, this.r);
}
ShapeCircle.prototype.serialize = function() {
return {
"type": "ShapeCircle",
"e": this.e,
"u": this.u,
"density": this.density,
"center": this.c,
"radius": this.r
};
}
ShapeCircle.prototype.recenter = function(c) {
this.c.subself(c);
}
ShapeCircle.prototype.transform = function(xf) {
this.c = xf.transform(this.c);
}
ShapeCircle.prototype.untransform = function(xf) {
this.c = xf.untransform(this.c);
}
ShapeCircle.prototype.area = function() {
return areaForCircle(this.r, 0);
}
ShapeCircle.prototype.centroid = function() {
return this.c.duplicate();
}
ShapeCircle.prototype.inertia = function(mass) {
return inertiaForCircle(mass, this.c, this.r, 0);
}
ShapeCircle.prototype.cacheData = function(xf) {
this.tc = xf.transform(this.c);
this.bounds.mins.set(this.tc.x - this.r, this.tc.y - this.r);
this.bounds.maxs.set(this.tc.x + this.r, this.tc.y + this.r);
}
ShapeCircle.prototype.pointQuery = function(p) {
return vec2.distsq(this.tc, p) < (this.r * this.r);
}
ShapeCircle.prototype.findVertexByPoint = function(p, minDist) {
var dsq = minDist * minDist;
if (vec2.distsq(this.tc, p) < dsq) {
return 0;
}
return -1;
}
ShapeCircle.prototype.distanceOnPlane = function(n, d) {
return vec2.dot(n, this.tc) - this.r - d;
}
Circle.prototype.updateBoundingRadius = function(){
this.boundingRadius = this.radius;
};
+213
View File
@@ -0,0 +1,213 @@
var Shape = require('./Shape')
, vec2 = require('../math/vec2')
, polyk = require('../math/polyk')
module.exports = Convex;
/**
* Convex shape class.
* @class Convex
* @constructor
* @extends {Shape}
* @param {Array} vertices An array of Float32Array vertices that span this shape. Vertices are given in counter-clockwise (CCW) direction.
*/
function Convex(vertices){
/**
* Vertices defined in the local frame.
* @property vertices
* @type {Array}
*/
this.vertices = vertices || [];
/**
* The center of mass of the Convex
* @property centerOfMass
* @type {Float32Array}
*/
this.centerOfMass = vec2.fromValues(0,0);
/**
* Triangulated version of this convex. The structure is Array of 3-Arrays, and each subarray contains 3 integers, referencing the vertices.
* @property triangles
* @type {Array}
*/
this.triangles = [];
if(this.vertices.length){
this.updateTriangles();
this.updateCenterOfMass();
}
/**
* The bounding radius of the convex
* @property boundingRadius
* @type {Number}
*/
this.boundingRadius = 0;
this.updateBoundingRadius();
Shape.call(this,Shape.CONVEX);
};
Convex.prototype = new Shape();
Convex.prototype.updateTriangles = function(){
this.triangles.length = 0;
// Rewrite on polyk notation, array of numbers
var polykVerts = [];
for(var i=0; i<this.vertices.length; i++){
var v = this.vertices[i];
polykVerts.push(v[0],v[1]);
}
// Triangulate
var triangles = polyk.Triangulate(polykVerts);
// Loop over all triangles, add their inertia contributions to I
for(var i=0; i<triangles.length; i+=3){
var id1 = triangles[i],
id2 = triangles[i+1],
id3 = triangles[i+2];
// Add to triangles
this.triangles.push([id1,id2,id3]);
}
};
var updateCenterOfMass_centroid = vec2.create(),
updateCenterOfMass_centroid_times_mass = vec2.create(),
updateCenterOfMass_a = vec2.create(),
updateCenterOfMass_b = vec2.create(),
updateCenterOfMass_c = vec2.create(),
updateCenterOfMass_ac = vec2.create(),
updateCenterOfMass_ca = vec2.create(),
updateCenterOfMass_cb = vec2.create(),
updateCenterOfMass_n = vec2.create();
Convex.prototype.updateCenterOfMass = function(){
var triangles = this.triangles,
verts = this.vertices,
cm = this.centerOfMass,
centroid = updateCenterOfMass_centroid,
n = updateCenterOfMass_n,
a = updateCenterOfMass_a,
b = updateCenterOfMass_b,
c = updateCenterOfMass_c,
ac = updateCenterOfMass_ac,
ca = updateCenterOfMass_ca,
cb = updateCenterOfMass_cb,
centroid_times_mass = updateCenterOfMass_centroid_times_mass;
vec2.set(cm,0,0);
for(var i=0; i<triangles.length; i++){
var t = triangles[i],
a = verts[t[0]],
b = verts[t[1]],
c = verts[t[2]];
vec2.centroid(centroid,a,b,c);
vec2.sub(ca, c, a);
vec2.sub(cb, c, b);
// Get mass for the triangle (density=1 in this case)
// http://math.stackexchange.com/questions/80198/area-of-triangle-via-vectors
var m = 0.5 * vec2.crossLength(ca,cb);
// Add to center of mass
vec2.scale(centroid_times_mass, centroid, m);
vec2.add(cm, cm, centroid_times_mass);
}
};
/**
* Compute the mass moment of inertia of the Convex.
* @method conputeMomentOfInertia
* @param {Number} mass
* @return {Number}
* @todo should use .triangles
*/
Convex.prototype.computeMomentOfInertia = function(mass){
// In short: Triangulate the Convex, compute centroid and inertia of
// each sub-triangle. Add up to total using parallel axis theorem.
var I = 0;
// Rewrite on polyk notation, array of numbers
var polykVerts = [];
for(var i=0; i<this.vertices.length; i++){
var v = this.vertices[i];
polykVerts.push(v[0],v[1]);
}
// Triangulate
var triangles = polyk.Triangulate(polykVerts);
// Get total convex area and density
var area = polyk.GetArea(polykVerts);
var density = mass / area;
// Temp vectors
var a = vec2.create(),
b = vec2.create(),
c = vec2.create(),
centroid = vec2.create(),
n = vec2.create(),
ac = vec2.create(),
ca = vec2.create(),
cb = vec2.create(),
centroid_times_mass = vec2.create();
// Loop over all triangles, add their inertia contributions to I
for(var i=0; i<triangles.length; i+=3){
var id1 = triangles[i],
id2 = triangles[i+1],
id3 = triangles[i+2];
// a,b,c are triangle corners
vec2.set(a, polykVerts[2*id1], polykVerts[2*id1+1]);
vec2.set(b, polykVerts[2*id2], polykVerts[2*id2+1]);
vec2.set(c, polykVerts[2*id3], polykVerts[2*id3+1]);
vec2.centroid(centroid, a, b, c);
vec2.sub(ca, c, a);
vec2.sub(cb, c, b);
var area_triangle = 0.5 * vec2.crossLength(ca,cb);
var base = vec2.length(ca);
var height = 2*area_triangle / base; // a=b*h/2 => h=2*a/b
// Get inertia for this triangle: http://answers.yahoo.com/question/index?qid=20080721030038AA3oE1m
var I_triangle = (base * (Math.pow(height,3))) / 36;
// Get mass for the triangle
var m = base*height/2 * density;
// Add to total inertia using parallel axis theorem
var r2 = vec2.squaredLength(centroid);
I += I_triangle + m*r2;
}
return I;
};
/**
* Updates the .boundingRadius property
* @method updateBoundingRadius
*/
Convex.prototype.updateBoundingRadius = function(){
var verts = this.vertices,
r2 = 0;
for(var i=0; i!==verts.length; i++){
var l2 = vec2.squaredLength(verts[i]);
if(l2 > r2) r2 = l2;
}
this.boundingRadius = Math.sqrt(r2);
};
+30
View File
@@ -0,0 +1,30 @@
var Shape = require('./Shape');
module.exports = Line;
/**
* Line shape class. The line shape is along the x direction, and stretches from [-length/2, 0] to [length/2,0].
* @class Line
* @extends {Shape}
* @constructor
*/
function Line(length){
/**
* Length of this line
* @property length
* @type {Number}
*/
this.length = length;
Shape.call(this,Shape.LINE);
};
Line.prototype = new Shape();
Line.prototype.computeMomentOfInertia = function(mass){
return mass * Math.pow(this.length,2) / 12;
};
Line.prototype.updateBoundingRadius = function(){
this.boundingRadius = this.length/2;
};
+22
View File
@@ -0,0 +1,22 @@
var Shape = require('./Shape');
module.exports = Particle;
/**
* Particle shape class.
* @class Particle
* @constructor
* @extends {Shape}
*/
function Particle(){
Shape.call(this,Shape.PARTICLE);
};
Particle.prototype = new Shape();
Particle.prototype.computeMomentOfInertia = function(mass){
return 0; // Can't rotate a particle
};
Particle.prototype.updateBoundingRadius = function(){
this.boundingRadius = 0;
};
+22
View File
@@ -0,0 +1,22 @@
var Shape = require('./Shape');
module.exports = Plane;
/**
* Plane shape class. The plane is facing in the Y direction.
* @class Plane
* @extends {Shape}
* @constructor
*/
function Plane(){
Shape.call(this,Shape.PLANE);
};
Plane.prototype = new Shape();
Plane.prototype.computeMomentOfInertia = function(mass){
return 0; // Plane is infinite. The inertia should therefore be infinty but by convention we set 0 here
};
Plane.prototype.updateBoundingRadius = function(){
this.boundingRadius = Number.MAX_VALUE;
};
-254
View File
@@ -1,254 +0,0 @@
/*
* Copyright (c) 2012 Ju Hyung Lee
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
//--------------------------------
// ShapePoly (convex only)
//--------------------------------
ShapePoly = function(verts) {
Shape.call(this, Shape.TYPE_POLY);
this.verts = [];
this.planes = [];
this.tverts = [];
this.tplanes = [];
if (verts) {
for (var i = 0; i < verts.length; i++) {
this.verts[i] = verts[i].duplicate();
this.tverts[i] = this.verts[i];
this.tplanes[i] = {};
this.tplanes[i].n = vec2.zero;
this.tplanes[i].d = 0;
}
}
this.finishVerts();
}
ShapePoly.prototype = new Shape;
ShapePoly.prototype.constructor = ShapePoly;
ShapePoly.prototype.finishVerts = function() {
if (this.verts.length < 2) {
this.convexity = false;
this.planes = [];
return;
}
this.convexity = true;
this.tverts = [];
this.tplanes = [];
// Must be counter-clockwise verts
for (var i = 0; i < this.verts.length; i++) {
var a = this.verts[i];
var b = this.verts[(i + 1) % this.verts.length];
var n = vec2.normalize(vec2.perp(vec2.sub(a, b)));
this.planes[i] = {};
this.planes[i].n = n;
this.planes[i].d = vec2.dot(n, a);
this.tverts[i] = this.verts[i];
this.tplanes[i] = {};
this.tplanes[i].n = vec2.zero;
this.tplanes[i].d = 0;
}
for (var i = 0; i < this.verts.length; i++) {
var b = this.verts[(i + 2) % this.verts.length];
var n = this.planes[i].n;
var d = this.planes[i].d;
if (vec2.dot(n, b) - d > 0) {
this.convexity = false;
}
}
}
ShapePoly.prototype.duplicate = function() {
return new ShapePoly(this.verts);
}
ShapePoly.prototype.serialize = function() {
return {
"type": "ShapePoly",
"e": this.e,
"u": this.u,
"density": this.density,
"verts": this.verts
};
}
ShapePoly.prototype.recenter = function(c) {
for (var i = 0; i < this.verts.length; i++) {
this.verts[i].subself(c);
}
}
ShapePoly.prototype.transform = function(xf) {
for (var i = 0; i < this.verts.length; i++) {
this.verts[i] = xf.transform(this.verts[i]);
}
}
ShapePoly.prototype.untransform = function(xf) {
for (var i = 0; i < this.verts.length; i++) {
this.verts[i] = xf.untransform(this.verts[i]);
}
}
ShapePoly.prototype.area = function() {
return areaForPoly(this.verts);
}
ShapePoly.prototype.centroid = function() {
return centroidForPoly(this.verts);
}
ShapePoly.prototype.inertia = function(mass) {
return inertiaForPoly(mass, this.verts, vec2.zero);
}
ShapePoly.prototype.cacheData = function(xf) {
this.bounds.clear();
var numVerts = this.verts.length;
if (numVerts == 0) {
return;
}
for (var i = 0; i < numVerts; i++) {
this.tverts[i] = xf.transform(this.verts[i]);
}
if (numVerts < 2) {
this.bounds.addPoint(this.tverts[0]);
return;
}
for (var i = 0; i < numVerts; i++) {
var a = this.tverts[i];
var b = this.tverts[(i + 1) % numVerts];
var n = vec2.normalize(vec2.perp(vec2.sub(a, b)));
this.tplanes[i].n = n;
this.tplanes[i].d = vec2.dot(n, a);
this.bounds.addPoint(a);
}
}
ShapePoly.prototype.pointQuery = function(p) {
if (!this.bounds.containPoint(p)) {
return false;
}
return this.containPoint(p);
}
ShapePoly.prototype.findVertexByPoint = function(p, minDist) {
var dsq = minDist * minDist;
for (var i = 0; i < this.tverts.length; i++) {
if (vec2.distsq(this.tverts[i], p) < dsq) {
return i;
}
}
return -1;
}
ShapePoly.prototype.findEdgeByPoint = function(p, minDist) {
var dsq = minDist * minDist;
var numVerts = this.tverts.length;
for (var i = 0; i < this.tverts.length; i++) {
var v1 = this.tverts[i];
var v2 = this.tverts[(i + 1) % numVerts];
var n = this.tplanes[i].n;
var dtv1 = vec2.cross(v1, n);
var dtv2 = vec2.cross(v2, n);
var dt = vec2.cross(p, n);
if (dt > dtv1) {
if (vec2.distsq(v1, p) < dsq) {
return i;
}
}
else if (dt < dtv2) {
if (vec2.distsq(v2, p) < dsq) {
return i;
}
}
else {
var dist = vec2.dot(n, p) - vec2.dot(n, v1);
if (dist * dist < dsq) {
return i;
}
}
}
return -1;
}
ShapePoly.prototype.distanceOnPlane = function(n, d) {
var min = 999999;
for (var i = 0; i < this.verts.length; i++) {
min = Math.min(min, vec2.dot(n, this.tverts[i]));
}
return min - d;
}
ShapePoly.prototype.containPoint = function(p) {
for (var i = 0; i < this.verts.length; i++) {
var plane = this.tplanes[i];
if (vec2.dot(plane.n, p) - plane.d > 0) {
return false;
}
}
return true;
}
ShapePoly.prototype.containPointPartial = function(p, n) {
for (var i = 0; i < this.verts.length; i++) {
var plane = this.tplanes[i];
if (vec2.dot(plane.n, n) < 0.0001) {
continue;
}
if (vec2.dot(plane.n, p) - plane.d > 0) {
return false;
}
}
return true;
}
+43
View File
@@ -0,0 +1,43 @@
var vec2 = require('../math/vec2')
, Shape = require('./Shape')
, Convex = require('./Convex')
module.exports = Rectangle;
/**
* Rectangle shape class.
* @class Rectangle
* @constructor
* @extends {Convex}
*/
function Rectangle(w,h){
var verts = [ vec2.fromValues(-w/2, -h/2),
vec2.fromValues( w/2, -h/2),
vec2.fromValues( w/2, h/2),
vec2.fromValues(-w/2, h/2)];
this.width = w;
this.height = h;
Convex.call(this,verts);
};
Rectangle.prototype = new Convex();
/**
* Compute moment of inertia
* @method computeMomentOfInertia
* @param {Number} mass
* @return {Number}
*/
Rectangle.prototype.computeMomentOfInertia = function(mass){
var w = this.width,
h = this.height;
return mass * (h*h + w*w) / 12;
};
Rectangle.prototype.updateBoundingRadius = function(){
var w = this.width,
h = this.height;
this.boundingRadius = Math.sqrt(w*w + h*h) / 2;
};

Some files were not shown because too many files have changed in this diff Show More