Added phaser build and hellophaser sample

This commit is contained in:
2014-06-09 21:32:48 -07:00
parent 49f23abbe8
commit 48b5c5714a
856 changed files with 954584 additions and 0 deletions

189
src/core/ArrayList.js Normal file
View File

@@ -0,0 +1,189 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2014 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* A set data structure. Allows items to add themselves to and remove themselves from the set. Items can only exist once in the set.
*
* @class Phaser.ArrayList
* @constructor
*/
Phaser.ArrayList = function () {
/**
* @property {number} total - Number of objects in the list.
* @default
*/
this.total = 0;
/**
* @property {number} position - Current cursor position.
* @default
*/
this.position = 0;
/**
* @property {array} list - The list.
*/
this.list = [];
};
Phaser.ArrayList.prototype = {
/**
* Adds a new element to this list. The item can only exist in the list once.
*
* @method Phaser.ArrayList#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 (!this.exists(child))
{
this.list.push(child);
this.total++;
}
return child;
},
/**
* Gets the index of the child in the list, or -1 if it isn't in the list.
*
* @method Phaser.ArrayList#getIndex
* @param {object} child - The element to get the list index for.
* @return {number} The index of the child or -1 if not found.
*/
getIndex: function (child) {
return this.list.indexOf(child);
},
/**
* Checks for the child within this list.
*
* @method Phaser.ArrayList#exists
* @param {object} child - The element to get the list index for.
* @return {boolean} True if the child is found in the list, otherwise false.
*/
exists: function (child) {
return (this.list.indexOf(child) > -1);
},
/**
* Resets the list length and drops all items in the list.
*
* @method Phaser.ArrayList#reset
*/
reset: function () {
this.list.length = 0;
this.total = 0;
},
/**
* Removes the given element from this list if it exists.
*
* @method Phaser.ArrayList#remove
* @param {object} child - The child to be removed from the list.
* @return {object} child - The child that was removed.
*/
remove: function (child) {
var idx = this.list.indexOf(child);
if (idx > -1)
{
this.list.splice(idx, 1);
this.total--;
return child;
}
},
/**
* 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.ArrayList#callAll
* @param {function} callback - The function to call.
* @param {...*} parameter - Additional parameters that will be passed to the callback.
*/
callAll: function (callback) {
var args = Array.prototype.splice.call(arguments, 1);
var i = this.list.length;
while (i--)
{
if (this.list[i] && this.list[i][callback])
{
this.list[i][callback].apply(this.list[i], args);
}
}
}
};
/**
* Resets the cursor to the first item in the list and returns it.
*
* @name Phaser.ArrayList#first
* @property {object} first - The first item in the list.
*/
Object.defineProperty(Phaser.ArrayList.prototype, "first", {
get: function () {
this.position = 0;
if (this.total > 0)
{
return this.list[0];
}
else
{
return null;
}
}
});
/**
* Gets the next item in the list and returns it, advancing the cursor.
*
* @name Phaser.ArrayList#next
* @property {object} next - Advanced the cursor and return.
*/
Object.defineProperty(Phaser.ArrayList.prototype, "next", {
get: function () {
if (this.position < this.total)
{
this.position++;
return this.list[this.position];
}
else
{
return null;
}
}
});
Phaser.ArrayList.prototype.constructor = Phaser.ArrayList;

446
src/core/Camera.js Normal file
View File

@@ -0,0 +1,446 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2014 Photon Storm Ltd.
* @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 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
*/
Phaser.Camera = function (game, id, x, y, width, height) {
/**
* @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 = game.world;
/**
* @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 if set to camera cull.
* @property {Phaser.Rectangle} view
*/
this.view = new Phaser.Rectangle(x, y, width, height);
/**
* @property {Phaser.Rectangle} screenView - Used by Sprites to work out Camera culling.
*/
this.screenView = new Phaser.Rectangle(x, y, width, height);
/**
* 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;
/**
* @property {boolean} visible - Whether this camera is visible or not.
* @default
*/
this.visible = true;
/**
* @property {boolean} atLimit - Whether this camera is flush with the World Bounds or not.
*/
this.atLimit = { x: false, y: false };
/**
* @property {Phaser.Sprite} target - If the camera is tracking a Sprite, this is a reference to it, otherwise null.
* @default
*/
this.target = null;
/**
* @property {number} edge - Edge property.
* @private
* @default
*/
this._edge = 0;
/**
* @property {PIXI.DisplayObject} displayObject - The display object to which all game objects are added. Set by World.boot
*/
this.displayObject = null;
/**
* @property {Phaser.Point} scale - The scale of the display object to which all game objects are added. Set by World.boot
*/
this.scale = null;
};
/**
* @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 Phaser.Camera#follow
* @param {Phaser.Sprite|Phaser.Image|Phaser.Text} 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) {
if (typeof style === "undefined") { style = Phaser.Camera.FOLLOW_LOCKON; }
this.target = target;
var helper;
switch (style) {
case Phaser.Camera.FOLLOW_PLATFORMER:
var w = this.width / 8;
var h = this.height / 3;
this.deadzone = new Phaser.Rectangle((this.width - w) / 2, (this.height - h) / 2 - h * 0.25, w, h);
break;
case Phaser.Camera.FOLLOW_TOPDOWN:
helper = Math.max(this.width, this.height) / 4;
this.deadzone = new Phaser.Rectangle((this.width - helper) / 2, (this.height - helper) / 2, helper, helper);
break;
case Phaser.Camera.FOLLOW_TOPDOWN_TIGHT:
helper = Math.max(this.width, this.height) / 8;
this.deadzone = new Phaser.Rectangle((this.width - helper) / 2, (this.height - helper) / 2, helper, helper);
break;
case Phaser.Camera.FOLLOW_LOCKON:
this.deadzone = null;
break;
default:
this.deadzone = null;
break;
}
},
/**
* Sets the Camera follow target to null, stopping it from following an object if it's doing so.
*
* @method Phaser.Camera#unfollow
*/
unfollow: function () {
this.target = null;
},
/**
* 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 on a location instantly.
* @method Phaser.Camera#focusOnXY
* @param {number} x - X position.
* @param {number} y - Y position.
*/
focusOnXY: function (x, y) {
this.setPosition(Math.round(x - this.view.halfWidth), Math.round(y - this.view.halfHeight));
},
/**
* Update focusing and scrolling.
* @method Phaser.Camera#update
*/
update: function () {
if (this.target)
{
this.updateTarget();
}
if (this.bounds)
{
this.checkBounds();
}
this.displayObject.position.x = -this.view.x;
this.displayObject.position.y = -this.view.y;
},
/**
* Internal method
* @method Phaser.Camera#updateTarget
* @private
*/
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);
}
},
/**
* Update the Camera bounds to match the game world.
* @method Phaser.Camera#setBoundsToWorld
*/
setBoundsToWorld: function () {
this.bounds.setTo(this.game.world.bounds.x, this.game.world.bounds.y, this.game.world.bounds.width, this.game.world.bounds.height);
},
/**
* Method called to ensure the camera doesn't venture outside of the game world.
* @method Phaser.Camera#checkWorldBounds
*/
checkBounds: function () {
this.atLimit.x = false;
this.atLimit.y = false;
// 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.bounds.x;
}
if (this.view.right >= this.bounds.right)
{
this.atLimit.x = true;
this.view.x = this.bounds.right - this.width;
}
if (this.view.y <= this.bounds.top)
{
this.atLimit.y = true;
this.view.y = this.bounds.top;
}
if (this.view.bottom >= this.bounds.bottom)
{
this.atLimit.y = true;
this.view.y = this.bounds.bottom - this.height;
}
this.view.floor();
},
/**
* 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.
*
* @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;
if (this.bounds)
{
this.checkBounds();
}
},
/**
* Sets the size of the view rectangle given the width and height in parameters.
*
* @method Phaser.Camera#setSize
* @param {number} width - The desired width.
* @param {number} height - The desired height.
*/
setSize: function (width, height) {
this.view.width = width;
this.view.height = height;
},
/**
* Resets the camera back to 0,0 and un-follows any object it may have been tracking.
*
* @method Phaser.Camera#reset
*/
reset: function () {
this.target = null;
this.view.x = 0;
this.view.y = 0;
}
};
Phaser.Camera.prototype.constructor = Phaser.Camera;
/**
* 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", {
get: function () {
return this.view.x;
},
set: function (value) {
this.view.x = value;
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", {
get: function () {
return this.view.y;
},
set: function (value) {
this.view.y = value;
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", {
get: function () {
return this.view.width;
},
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", {
get: function () {
return this.view.height;
},
set: function (value) {
this.view.height = value;
}
});

165
src/core/Filter.js Normal file
View File

@@ -0,0 +1,165 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2014 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* This is a base Filter template to use for any Phaser filter development.
*
* @class Phaser.Filter
* @classdesc Phaser - Filter
* @constructor
* @param {Phaser.Game} game - A reference to the currently running game.
* @param {Object} uniforms - Uniform mappings object
* @param {Array} fragmentSrc - The fragment shader code.
*/
Phaser.Filter = function (game, uniforms, fragmentSrc) {
/**
* @property {Phaser.Game} game - A reference to the currently running game.
*/
this.game = game;
/**
* @property {number} type - The const type of this object, either Phaser.WEBGL_FILTER or Phaser.CANVAS_FILTER.
* @default
*/
this.type = Phaser.WEBGL_FILTER;
/**
* An array of passes - some filters contain a few steps this array simply stores the steps in a linear fashion.
* For example the blur filter has two passes blurX and blurY.
* @property {array} passes - An array of filter objects.
* @private
*/
this.passes = [this];
/**
* @property {array} shaders - Array an array of shaders.
* @private
*/
this.shaders = [];
/**
* @property {boolean} dirty - Internal PIXI var.
* @default
*/
this.dirty = true;
/**
* @property {number} padding - Internal PIXI var.
* @default
*/
this.padding = 0;
/**
* @property {object} uniforms - Default uniform mappings.
*/
this.uniforms = {
time: { type: '1f', value: 0 },
resolution: { type: '2f', value: { x: 256, y: 256 }},
mouse: { type: '2f', value: { x: 0.0, y: 0.0 }}
};
/**
* @property {array} fragmentSrc - The fragment shader code.
*/
this.fragmentSrc = fragmentSrc || [];
};
Phaser.Filter.prototype = {
/**
* Should be over-ridden.
* @method Phaser.Filter#init
*/
init: function () {
// This should be over-ridden. Will receive a variable number of arguments.
},
/**
* Set the resolution uniforms on the filter.
* @method Phaser.Filter#setResolution
* @param {number} width - The width of the display.
* @param {number} height - The height of the display.
*/
setResolution: function (width, height) {
this.uniforms.resolution.value.x = width;
this.uniforms.resolution.value.y = height;
},
/**
* Updates the filter.
* @method Phaser.Filter#update
* @param {Phaser.Pointer} [pointer] - A Pointer object to use for the filter. The coordinates are mapped to the mouse uniform.
*/
update: function (pointer) {
if (typeof pointer !== 'undefined')
{
if (pointer.x > 0)
{
this.uniforms.mouse.x = pointer.x.toFixed(2);
}
if (pointer.y > 0)
{
this.uniforms.mouse.y = pointer.y.toFixed(2);
}
}
this.uniforms.time.value = this.game.time.totalElapsedSeconds();
},
/**
* Clear down this Filter and null out references
* @method Phaser.Filter#destroy
*/
destroy: function () {
this.game = null;
}
};
Phaser.Filter.prototype.constructor = Phaser.Filter;
/**
* @name Phaser.Filter#width
* @property {number} width - The width (resolution uniform)
*/
Object.defineProperty(Phaser.Filter.prototype, 'width', {
get: function() {
return this.uniforms.resolution.value.x;
},
set: function(value) {
this.uniforms.resolution.value.x = value;
}
});
/**
* @name Phaser.Filter#height
* @property {number} height - The height (resolution uniform)
*/
Object.defineProperty(Phaser.Filter.prototype, 'height', {
get: function() {
return this.uniforms.resolution.value.y;
},
set: function(value) {
this.uniforms.resolution.value.y = value;
}
});

824
src/core/Game.js Normal file
View File

@@ -0,0 +1,824 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2014 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.
* "Hell, there are no rules here - we're trying to accomplish something."
* Thomas A. Edison
* @constructor
* @param {number} [width=800] - The width of your game in game pixels.
* @param {number} [height=600] - The height of your game in game pixels.
* @param {number} [renderer=Phaser.AUTO] - Which renderer to use: Phaser.AUTO will auto-detect, Phaser.WEBGL, Phaser.CANVAS or Phaser.HEADLESS (no rendering at all).
* @param {string|HTMLElement} [parent=''] - The DOM element into which this games canvas will be injected. Either a DOM ID (string) or the element itself.
* @param {object} [state=null] - The default state object. A object consisting of Phaser.State functions (preload, create, update, render) or null.
* @param {boolean} [transparent=false] - Use a transparent canvas background or not.
* @param {boolean} [antialias=true] - Draw all image textures anti-aliased or not. The default is for smooth textures, but disable if your game features pixel art.
* @param {object} [physicsConfig=null] - A physics configuration object to pass to the Physics world on creation.
*/
Phaser.Game = function (width, height, renderer, parent, state, transparent, antialias, physicsConfig) {
/**
* @property {number} id - Phaser Game ID (for when Pixi supports multiple instances).
*/
this.id = Phaser.GAMES.push(this) - 1;
/**
* @property {object} config - The Phaser.Game configuration object.
*/
this.config = null;
/**
* @property {object} physicsConfig - The Phaser.Physics.World configuration object.
*/
this.physicsConfig = physicsConfig;
/**
* @property {string|HTMLElement} parent - The Games DOM parent.
* @default
*/
this.parent = '';
/**
* @property {number} width - The Game width (in pixels).
* @default
*/
this.width = 800;
/**
* @property {number} height - The Game height (in pixels).
* @default
*/
this.height = 600;
/**
* @property {boolean} transparent - Use a transparent canvas background or not.
* @default
*/
this.transparent = false;
/**
* @property {boolean} antialias - Anti-alias graphics. By default scaled images are smoothed in Canvas and WebGL, set anti-alias to false to disable this globally.
* @default
*/
this.antialias = true;
/**
* @property {PIXI.CanvasRenderer|PIXI.WebGLRenderer} renderer - The Pixi Renderer.
*/
this.renderer = null;
/**
* @property {number} renderType - The Renderer this game will use. Either Phaser.AUTO, Phaser.CANVAS or Phaser.WEBGL.
*/
this.renderType = Phaser.AUTO;
/**
* @property {Phaser.StateManager} state - The StateManager.
*/
this.state = null;
/**
* @property {boolean} isBooted - Whether the game engine is booted, aka available.
* @default
*/
this.isBooted = false;
/**
* @property {boolean} id -Is game running or paused?
* @default
*/
this.isRunning = false;
/**
* @property {Phaser.RequestAnimationFrame} raf - Automatically handles the core game loop via requestAnimationFrame or setTimeout
*/
this.raf = null;
/**
* @property {Phaser.GameObjectFactory} add - Reference to the Phaser.GameObjectFactory.
*/
this.add = null;
/**
* @property {Phaser.GameObjectCreator} make - Reference to the GameObject Creator.
*/
this.make = null;
/**
* @property {Phaser.Cache} cache - Reference to the assets cache.
*/
this.cache = null;
/**
* @property {Phaser.Input} input - Reference to the input manager
*/
this.input = null;
/**
* @property {Phaser.Loader} load - Reference to the assets loader.
*/
this.load = null;
/**
* @property {Phaser.Math} math - Reference to the math helper.
*/
this.math = null;
/**
* @property {Phaser.Net} net - Reference to the network class.
*/
this.net = null;
/**
* @property {Phaser.ScaleManager} scale - The game scale manager.
*/
this.scale = null;
/**
* @property {Phaser.SoundManager} sound - Reference to the sound manager.
*/
this.sound = null;
/**
* @property {Phaser.Stage} stage - Reference to the stage.
*/
this.stage = null;
/**
* @property {Phaser.Time} time - Reference to the core game clock.
*/
this.time = null;
/**
* @property {Phaser.TweenManager} tweens - Reference to the tween manager.
*/
this.tweens = null;
/**
* @property {Phaser.World} world - Reference to the world.
*/
this.world = null;
/**
* @property {Phaser.Physics} physics - Reference to the physics manager.
*/
this.physics = null;
/**
* @property {Phaser.RandomDataGenerator} rnd - Instance of repeatable random data generator helper.
*/
this.rnd = null;
/**
* @property {Phaser.Device} device - Contains device information and capabilities.
*/
this.device = null;
/**
* @property {Phaser.Camera} camera - A handy reference to world.camera.
*/
this.camera = null;
/**
* @property {HTMLCanvasElement} canvas - A handy reference to renderer.view, the canvas that the game is being rendered in to.
*/
this.canvas = null;
/**
* @property {CanvasRenderingContext2D} context - A handy reference to renderer.context (only set for CANVAS games, not WebGL)
*/
this.context = null;
/**
* @property {Phaser.Utils.Debug} debug - A set of useful debug utilitie.
*/
this.debug = null;
/**
* @property {Phaser.Particles} particles - The Particle Manager.
*/
this.particles = null;
/**
* @property {boolean} stepping - Enable core loop stepping with Game.enableStep().
* @default
* @readonly
*/
this.stepping = false;
/**
* @property {boolean} pendingStep - An internal property used by enableStep, but also useful to query from your own game objects.
* @default
* @readonly
*/
this.pendingStep = false;
/**
* @property {number} stepCount - When stepping is enabled this contains the current step cycle.
* @default
* @readonly
*/
this.stepCount = 0;
/**
* @property {Phaser.Signal} onPause - This event is fired when the game pauses.
*/
this.onPause = null;
/**
* @property {Phaser.Signal} onResume - This event is fired when the game resumes from a paused state.
*/
this.onResume = null;
/**
* @property {Phaser.Signal} onBlur - This event is fired when the game no longer has focus (typically on page hide).
*/
this.onBlur = null;
/**
* @property {Phaser.Signal} onFocus - This event is fired when the game has focus (typically on page show).
*/
this.onFocus = null;
/**
* @property {boolean} _paused - Is game paused?
* @private
*/
this._paused = false;
/**
* @property {boolean} _codePaused - Was the game paused via code or a visibility change?
* @private
*/
this._codePaused = false;
// Parse the configuration object (if any)
if (arguments.length === 1 && typeof arguments[0] === 'object')
{
this.parseConfig(arguments[0]);
}
else
{
if (typeof width !== 'undefined')
{
this.width = width;
}
if (typeof height !== 'undefined')
{
this.height = height;
}
if (typeof renderer !== 'undefined')
{
this.renderer = renderer;
this.renderType = renderer;
}
if (typeof parent !== 'undefined')
{
this.parent = parent;
}
if (typeof transparent !== 'undefined')
{
this.transparent = transparent;
}
if (typeof antialias !== 'undefined')
{
this.antialias = antialias;
}
this.rnd = new Phaser.RandomDataGenerator([(Date.now() * Math.random()).toString()]);
this.state = new Phaser.StateManager(this, state);
}
var _this = this;
this._onBoot = function () {
return _this.boot();
};
if (document.readyState === 'complete' || document.readyState === 'interactive')
{
window.setTimeout(this._onBoot, 0);
}
else
{
document.addEventListener('DOMContentLoaded', this._onBoot, false);
window.addEventListener('load', this._onBoot, false);
}
return this;
};
Phaser.Game.prototype = {
/**
* Parses a Game configuration object.
*
* @method Phaser.Game#parseConfig
* @protected
*/
parseConfig: function (config) {
this.config = config;
if (config['width'])
{
this.width = Phaser.Utils.parseDimension(config['width'], 0);
}
if (config['height'])
{
this.height = Phaser.Utils.parseDimension(config['height'], 1);
}
if (config['renderer'])
{
this.renderer = config['renderer'];
this.renderType = config['renderer'];
}
if (config['parent'])
{
this.parent = config['parent'];
}
if (config['transparent'])
{
this.transparent = config['transparent'];
}
if (config['antialias'])
{
this.antialias = config['antialias'];
}
if (config['physicsConfig'])
{
this.physicsConfig = config['physicsConfig'];
}
var seed = [(Date.now() * Math.random()).toString()];
if (config['seed'])
{
seed = config['seed'];
}
this.rnd = new Phaser.RandomDataGenerator(seed);
var state = null;
if (config['state'])
{
state = config['state'];
}
this.state = new Phaser.StateManager(this, state);
},
/**
* Initialize engine sub modules and start the game.
*
* @method Phaser.Game#boot
* @protected
*/
boot: function () {
if (this.isBooted)
{
return;
}
if (!document.body)
{
window.setTimeout(this._onBoot, 20);
}
else
{
document.removeEventListener('DOMContentLoaded', this._onBoot);
window.removeEventListener('load', this._onBoot);
this.onPause = new Phaser.Signal();
this.onResume = new Phaser.Signal();
this.onBlur = new Phaser.Signal();
this.onFocus = new Phaser.Signal();
this.isBooted = true;
this.device = new Phaser.Device(this);
this.math = Phaser.Math;
this.stage = new Phaser.Stage(this, this.width, this.height);
this.scale = new Phaser.ScaleManager(this, this.width, this.height);
this.setUpRenderer();
this.device.checkFullScreenSupport();
this.world = new Phaser.World(this);
this.add = new Phaser.GameObjectFactory(this);
this.make = new Phaser.GameObjectCreator(this);
this.cache = new Phaser.Cache(this);
this.load = new Phaser.Loader(this);
this.time = new Phaser.Time(this);
this.tweens = new Phaser.TweenManager(this);
this.input = new Phaser.Input(this);
this.sound = new Phaser.SoundManager(this);
this.physics = new Phaser.Physics(this, this.physicsConfig);
this.particles = new Phaser.Particles(this);
this.plugins = new Phaser.PluginManager(this);
this.net = new Phaser.Net(this);
this.debug = new Phaser.Utils.Debug(this);
this.scratch = new Phaser.BitmapData(this, '__root', 1024, 1024);
this.time.boot();
this.stage.boot();
this.world.boot();
this.input.boot();
this.sound.boot();
this.state.boot();
this.debug.boot();
this.showDebugHeader();
this.isRunning = true;
if (this.config && this.config['forceSetTimeOut'])
{
this.raf = new Phaser.RequestAnimationFrame(this, this.config['forceSetTimeOut']);
}
else
{
this.raf = new Phaser.RequestAnimationFrame(this, false);
}
this.raf.start();
}
},
/**
* Displays a Phaser version debug header in the console.
*
* @method Phaser.Game#showDebugHeader
* @protected
*/
showDebugHeader: function () {
var v = Phaser.VERSION;
var r = 'Canvas';
var a = 'HTML Audio';
var c = 1;
if (this.renderType === Phaser.WEBGL)
{
r = 'WebGL';
c++;
}
else if (this.renderType == Phaser.HEADLESS)
{
r = 'Headless';
}
if (this.device.webAudio)
{
a = 'WebAudio';
c++;
}
if (this.device.chrome)
{
var args = [
'%c %c %c Phaser v' + v + ' - ' + r + ' - ' + a + ' %c %c ' + ' http://phaser.io %c %c ♥%c♥%c♥ ',
'background: #0cf300',
'background: #00bc17',
'color: #ffffff; background: #00711f;',
'background: #00bc17',
'background: #0cf300',
'background: #00bc17'
];
for (var i = 0; i < 3; i++)
{
if (i < c)
{
args.push('color: #ff2424; background: #fff');
}
else
{
args.push('color: #959595; background: #fff');
}
}
console.log.apply(console, args);
}
else if (window['console'])
{
console.log('Phaser v' + v + ' - Renderer: ' + r + ' - Audio: ' + a + ' - http://phaser.io');
}
},
/**
* 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.device.trident)
{
// Pixi WebGL renderer on IE11 doesn't work correctly at the moment, the pre-multiplied alpha gets all washed out.
// So we're forcing canvas for now until this is fixed, sorry. It's got to be better than no game appearing at all, right?
this.renderType = Phaser.CANVAS;
}
if (this.renderType === Phaser.HEADLESS || this.renderType === Phaser.CANVAS || (this.renderType === Phaser.AUTO && this.device.webGL === false))
{
if (this.device.canvas)
{
if (this.renderType === Phaser.AUTO)
{
this.renderType = Phaser.CANVAS;
}
this.renderer = new PIXI.CanvasRenderer(this.width, this.height, this.canvas, this.transparent);
this.context = this.renderer.context;
}
else
{
throw new Error('Phaser.Game - cannot create Canvas or WebGL context, aborting.');
}
}
else
{
// They requested WebGL, and their browser supports it
this.renderType = Phaser.WEBGL;
this.renderer = new PIXI.WebGLRenderer(this.width, this.height, this.canvas, this.transparent, this.antialias);
this.context = null;
}
if (this.renderType !== Phaser.HEADLESS)
{
this.stage.smoothed = this.antialias;
Phaser.Canvas.addToDOM(this.canvas, this.parent, true);
Phaser.Canvas.setTouchAction(this.canvas);
}
},
/**
* The core game loop when in a paused state.
*
* @method Phaser.Game#update
* @protected
* @param {number} time - The current time as provided by RequestAnimationFrame.
*/
update: function (time) {
this.time.update(time);
if (!this._paused && !this.pendingStep)
{
if (this.stepping)
{
this.pendingStep = true;
}
this.debug.preUpdate();
this.physics.preUpdate();
this.state.preUpdate();
this.plugins.preUpdate();
this.stage.preUpdate();
this.state.update();
this.stage.update();
this.tweens.update();
this.sound.update();
this.input.update();
this.physics.update();
this.particles.update();
this.plugins.update();
this.stage.postUpdate();
this.plugins.postUpdate();
}
else
{
this.state.pauseUpdate();
// this.input.update();
this.debug.preUpdate();
}
if (this.renderType != Phaser.HEADLESS)
{
this.renderer.render(this.stage);
this.plugins.render();
this.state.render();
this.plugins.postRender();
}
},
/**
* Enable core game loop stepping. When enabled you must call game.step() directly (perhaps via a DOM button?)
* Calling step will advance the game loop by one frame. This is extremely useful for hard to track down errors!
*
* @method Phaser.Game#enableStep
*/
enableStep: function () {
this.stepping = true;
this.pendingStep = false;
this.stepCount = 0;
},
/**
* Disables core game loop stepping.
*
* @method Phaser.Game#disableStep
*/
disableStep: function () {
this.stepping = false;
this.pendingStep = false;
},
/**
* When stepping is enabled you must call this function directly (perhaps via a DOM button?) to advance the game loop by one frame.
* This is extremely useful to hard to track down errors! Use the internal stepCount property to monitor progress.
*
* @method Phaser.Game#step
*/
step: function () {
this.pendingStep = false;
this.stepCount++;
},
/**
* Nuke the entire game from orbit
*
* @method Phaser.Game#destroy
*/
destroy: function () {
this.raf.stop();
this.input.destroy();
this.state.destroy();
this.physics.destroy();
this.state = null;
this.cache = null;
this.input = null;
this.load = null;
this.sound = null;
this.stage = null;
this.time = null;
this.world = null;
this.isBooted = false;
},
/**
* Called by the Stage visibility handler.
*
* @method Phaser.Game#gamePaused
* @param {object} event - The DOM event that caused the game to pause, if any.
* @protected
*/
gamePaused: function (event) {
// If the game is already paused it was done via game code, so don't re-pause it
if (!this._paused)
{
this._paused = true;
this.time.gamePaused();
this.sound.setMute();
this.onPause.dispatch(event);
}
},
/**
* Called by the Stage visibility handler.
*
* @method Phaser.Game#gameResumed
* @param {object} event - The DOM event that caused the game to pause, if any.
* @protected
*/
gameResumed: function (event) {
// Game is paused, but wasn't paused via code, so resume it
if (this._paused && !this._codePaused)
{
this._paused = false;
this.time.gameResumed();
this.input.reset();
this.sound.unsetMute();
this.onResume.dispatch(event);
}
},
/**
* Called by the Stage visibility handler.
*
* @method Phaser.Game#focusLoss
* @param {object} event - The DOM event that caused the game to pause, if any.
* @protected
*/
focusLoss: function (event) {
this.onBlur.dispatch(event);
this.gamePaused(event);
},
/**
* Called by the Stage visibility handler.
*
* @method Phaser.Game#focusGain
* @param {object} event - The DOM event that caused the game to pause, if any.
* @protected
*/
focusGain: function (event) {
this.onFocus.dispatch(event);
this.gameResumed(event);
}
};
Phaser.Game.prototype.constructor = Phaser.Game;
/**
* 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 () {
return this._paused;
},
set: function (value) {
if (value === true)
{
if (this._paused === false)
{
this._paused = true;
this._codePaused = true;
this.sound.setMute();
this.time.gamePaused();
this.onPause.dispatch(this);
}
}
else
{
if (this._paused)
{
this._paused = false;
this._codePaused = false;
this.input.reset();
this.sound.unsetMute();
this.time.gameResumed();
this.onResume.dispatch(this);
}
}
}
});
/**
* "Deleted code is debugged code." - Jeff Sickel
*/

1751
src/core/Group.js Normal file

File diff suppressed because it is too large Load Diff

178
src/core/LinkedList.js Normal file
View File

@@ -0,0 +1,178 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2014 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 {number} total - 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
if (this.total === 0 && this.first === null && this.last === null)
{
this.first = child;
this.last = child;
this.next = child;
child.prev = this;
this.total++;
return child;
}
// Gets appended to the end of the list, regardless of anything, and it won't have any children of its own (non-nested list)
this.last.next = child;
child.prev = this.last;
this.last = child;
this.total++;
return child;
},
/**
* Resets the first, last, next and previous node pointers in this list.
*
* @method Phaser.LinkedList#reset
*/
reset: function () {
this.first = null;
this.last = null;
this.next = null;
this.prev = null;
this.total = 0;
},
/**
* 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 (this.total === 1)
{
this.reset();
child.next = child.prev = null;
return;
}
if (child === this.first)
{
// 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--;
},
/**
* 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)
{
return;
}
var entity = this.first;
do
{
if (entity && entity[callback])
{
entity[callback].call(entity);
}
entity = entity.next;
}
while(entity != this.last.next);
}
};
Phaser.LinkedList.prototype.constructor = Phaser.LinkedList;

123
src/core/Plugin.js Normal file
View File

@@ -0,0 +1,123 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2014 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} hasPostUpdate - A flag to indicate if this plugin has a postUpdate method.
* @default
*/
this.hasPostUpdate = 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;
};
Phaser.Plugin.prototype = {
/**
* 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 () {
},
/**
* 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 () {
},
/**
* 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 () {
},
/**
* 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 () {
this.game = null;
this.parent = null;
this.active = false;
this.visible = false;
}
};
Phaser.Plugin.prototype.constructor = Phaser.Plugin;

288
src/core/PluginManager.js Normal file
View File

@@ -0,0 +1,288 @@
/* jshint newcap: false */
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2014 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* The Plugin Manager is responsible for the loading, running and unloading of Phaser Plugins.
*
* @class Phaser.PluginManager
* @classdesc Phaser - PluginManager
* @constructor
* @param {Phaser.Game} game - A reference to the currently running game.
*/
Phaser.PluginManager = function(game) {
/**
* @property {Phaser.Game} game - A reference to the currently running game.
*/
this.game = game;
/**
* @property {array} plugins - An array of all the plugins being managed by this PluginManager.
*/
this.plugins = [];
/**
* @property {number} _len - Internal cache var.
* @private
*/
this._len = 0;
/**
* @property {number} _i - Internal cache var.
* @private
*/
this._i = 0;
};
Phaser.PluginManager.prototype = {
/**
* Add a new Plugin into the PluginManager.
* The Plugin must have 2 properties: game and parent. Plugin.game is set to ths game reference the PluginManager uses, and parent is set to the PluginManager.
*
* @method Phaser.PluginManager#add
* @param {object|Phaser.Plugin} plugin - The Plugin to add into the PluginManager. This can be a function or an existing object.
* @param {...*} parameter - Additional parameters that will be passed to the Plugin.init method.
* @return {Phaser.Plugin} The Plugin that was added to the manager.
*/
add: function (plugin) {
var args = Array.prototype.splice.call(arguments, 1);
var result = false;
// Prototype?
if (typeof plugin === 'function')
{
plugin = new plugin(this.game, this);
}
else
{
plugin.game = this.game;
plugin.parent = this;
}
// Check for methods now to avoid having to do this every loop
if (typeof plugin['preUpdate'] === 'function')
{
plugin.hasPreUpdate = true;
result = true;
}
if (typeof plugin['update'] === 'function')
{
plugin.hasUpdate = true;
result = true;
}
if (typeof plugin['postUpdate'] === 'function')
{
plugin.hasPostUpdate = true;
result = true;
}
if (typeof plugin['render'] === 'function')
{
plugin.hasRender = true;
result = true;
}
if (typeof plugin['postRender'] === 'function')
{
plugin.hasPostRender = true;
result = true;
}
// The plugin must have at least one of the above functions to be added to the PluginManager.
if (result)
{
if (plugin.hasPreUpdate || plugin.hasUpdate || plugin.hasPostUpdate)
{
plugin.active = true;
}
if (plugin.hasRender || plugin.hasPostRender)
{
plugin.visible = true;
}
this._len = this.plugins.push(plugin);
// Allows plugins to run potentially destructive code outside of the constructor, and only if being added to the PluginManager
if (typeof plugin['init'] === 'function')
{
plugin.init.apply(plugin, args);
}
return plugin;
}
else
{
return null;
}
},
/**
* Remove a Plugin from the PluginManager. It calls Plugin.destroy on the plugin before removing it from the manager.
*
* @method Phaser.PluginManager#remove
* @param {Phaser.Plugin} plugin - The plugin to be removed.
*/
remove: function (plugin) {
this._i = this._len;
while (this._i--)
{
if (this.plugins[this._i] === plugin)
{
plugin.destroy();
this.plugins.splice(this._i, 1);
this._len--;
return;
}
}
},
/**
* Remove all Plugins from the PluginManager. It calls Plugin.destroy on every plugin before removing it from the manager.
*
* @method Phaser.PluginManager#removeAll
*/
removeAll: function() {
this._i = this._len;
while (this._i--)
{
this.plugins[this._i].destroy();
}
this.plugins.length = 0;
this._len = 0;
},
/**
* 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 () {
this._i = this._len;
while (this._i--)
{
if (this.plugins[this._i].active && this.plugins[this._i].hasPreUpdate)
{
this.plugins[this._i].preUpdate();
}
}
},
/**
* 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 () {
this._i = this._len;
while (this._i--)
{
if (this.plugins[this._i].active && this.plugins[this._i].hasUpdate)
{
this.plugins[this._i].update();
}
}
},
/**
* PostUpdate is the last thing to be called before the world render.
* In particular, it is called after the world postUpdate, which means the camera has been adjusted.
* It only calls plugins who have active=true.
*
* @method Phaser.PluginManager#postUpdate
*/
postUpdate: function () {
this._i = this._len;
while (this._i--)
{
if (this.plugins[this._i].active && this.plugins[this._i].hasPostUpdate)
{
this.plugins[this._i].postUpdate();
}
}
},
/**
* 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 () {
this._i = this._len;
while (this._i--)
{
if (this.plugins[this._i].visible && this.plugins[this._i].hasRender)
{
this.plugins[this._i].render();
}
}
},
/**
* 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 () {
this._i = this._len;
while (this._i--)
{
if (this.plugins[this._i].visible && this.plugins[this._i].hasPostRender)
{
this.plugins[this._i].postRender();
}
}
},
/**
* Clear down this PluginManager, calls destroy on every plugin and nulls out references.
*
* @method Phaser.PluginManager#destroy
*/
destroy: function () {
this.removeAll();
this.game = null;
}
};
Phaser.PluginManager.prototype.constructor = Phaser.PluginManager;

806
src/core/ScaleManager.js Normal file
View File

@@ -0,0 +1,806 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2014 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* The ScaleManager object is responsible for helping you manage the scaling, resizing and alignment of your game within the browser.
*
* @class Phaser.ScaleManager
* @constructor
* @param {Phaser.Game} game - A reference to the currently running game.
* @param {number} width - The native width of the game.
* @param {number} height - The native height of the game.
*/
Phaser.ScaleManager = function (game, width, height) {
/**
* @property {Phaser.Game} game - A reference to the currently running game.
*/
this.game = game;
/**
* @property {number} width - Width of the stage after calculation.
*/
this.width = width;
/**
* @property {number} height - Height of the stage after calculation.
*/
this.height = height;
/**
* @property {number} minWidth - Minimum width the canvas should be scaled to (in pixels).
*/
this.minWidth = null;
/**
* @property {number} maxWidth - Maximum width the canvas should be scaled to (in pixels). If null it will scale to whatever width the browser can handle.
*/
this.maxWidth = null;
/**
* @property {number} minHeight - Minimum height the canvas should be scaled to (in pixels).
*/
this.minHeight = null;
/**
* @property {number} maxHeight - Maximum height the canvas should be scaled to (in pixels). If null it will scale to whatever height the browser can handle.
*/
this.maxHeight = null;
/**
* @property {boolean} forceLandscape - If the game should be forced to use Landscape mode, this is set to true by Game.Stage
* @default
*/
this.forceLandscape = false;
/**
* @property {boolean} forcePortrait - If the game should be forced to use Portrait mode, this is set to true by Game.Stage
* @default
*/
this.forcePortrait = false;
/**
* @property {boolean} incorrectOrientation - If the game should be forced to use a specific orientation and the device currently isn't in that orientation this is set to true.
* @default
*/
this.incorrectOrientation = false;
/**
* @property {boolean} pageAlignHorizontally - If you wish to align your game in the middle of the page then you can set this value to true.
* It will place a re-calculated margin-left pixel value onto the canvas element which is updated on orientation/resizing.
* It doesn't care about any other DOM element that may be on the page, it literally just sets the margin.
* @default
*/
this.pageAlignHorizontally = false;
/**
* @property {boolean} pageAlignVertically - If you wish to align your game in the middle of the page then you can set this value to true.
* It will place a re-calculated margin-left pixel value onto the canvas element which is updated on orientation/resizing.
* It doesn't care about any other DOM element that may be on the page, it literally just sets the margin.
* @default
*/
this.pageAlignVertically = false;
/**
* @property {number} maxIterations - The maximum number of times it will try to resize the canvas to fill the browser.
* @default
*/
this.maxIterations = 5;
/**
* @property {PIXI.Sprite} orientationSprite - The Sprite that is optionally displayed if the browser enters an unsupported orientation.
*/
this.orientationSprite = null;
/**
* @property {Phaser.Signal} enterLandscape - The event that is dispatched when the browser enters landscape orientation.
*/
this.enterLandscape = new Phaser.Signal();
/**
* @property {Phaser.Signal} enterPortrait - The event that is dispatched when the browser enters horizontal orientation.
*/
this.enterPortrait = new Phaser.Signal();
/**
* @property {Phaser.Signal} enterIncorrectOrientation - The event that is dispatched when the browser enters an incorrect orientation, as defined by forceOrientation.
*/
this.enterIncorrectOrientation = new Phaser.Signal();
/**
* @property {Phaser.Signal} leaveIncorrectOrientation - The event that is dispatched when the browser leaves an incorrect orientation, as defined by forceOrientation.
*/
this.leaveIncorrectOrientation = new Phaser.Signal();
/**
* @property {Phaser.Signal} hasResized - The event that is dispatched when the game scale changes.
*/
this.hasResized = new Phaser.Signal();
/**
* This is the DOM element that will have the Full Screen mode called on it. It defaults to the game canvas, but can be retargetted to any valid DOM element.
* If you adjust this property it's up to you to see it has the correct CSS applied, and that you have contained the game canvas correctly.
* Note that if you use a scale property of EXACT_FIT then fullScreenTarget will have its width and height style set to 100%.
* @property {any} fullScreenTarget
*/
this.fullScreenTarget = this.game.canvas;
/**
* @property {Phaser.Signal} enterFullScreen - The event that is dispatched when the browser enters full screen mode (if it supports the FullScreen API).
*/
this.enterFullScreen = new Phaser.Signal();
/**
* @property {Phaser.Signal} leaveFullScreen - The event that is dispatched when the browser leaves full screen mode (if it supports the FullScreen API).
*/
this.leaveFullScreen = new Phaser.Signal();
/**
* @property {number} orientation - The orientation value of the game (as defined by window.orientation if set). 90 = landscape. 0 = portrait.
*/
this.orientation = 0;
if (window['orientation'])
{
this.orientation = window['orientation'];
}
else
{
if (window.outerWidth > window.outerHeight)
{
this.orientation = 90;
}
}
/**
* @property {Phaser.Point} scaleFactor - The scale factor based on the game dimensions vs. the scaled dimensions.
* @readonly
*/
this.scaleFactor = new Phaser.Point(1, 1);
/**
* @property {Phaser.Point} scaleFactorInversed - The inversed scale factor. The displayed dimensions divided by the game dimensions.
* @readonly
*/
this.scaleFactorInversed = new Phaser.Point(1, 1);
/**
* @property {Phaser.Point} margin - If the game canvas is set to align by adjusting the margin, the margin calculation values are stored in this Point.
* @readonly
*/
this.margin = new Phaser.Point(0, 0);
/**
* @property {Phaser.Rectangle} bounds - The bounds of the scaled game. The x/y will match the offset of the canvas element and the width/height the scaled width and height.
* @readonly
*/
this.bounds = new Phaser.Rectangle(0, 0, width, height);
/**
* @property {number} aspectRatio - The aspect ratio of the scaled game.
* @readonly
*/
this.aspectRatio = 0;
/**
* @property {number} sourceAspectRatio - The aspect ratio (width / height) of the original game dimensions.
* @readonly
*/
this.sourceAspectRatio = width / height;
/**
* @property {any} event- The native browser events from full screen API changes.
*/
this.event = null;
/**
* @property {number} scaleMode - The current scaleMode.
*/
this.scaleMode = Phaser.ScaleManager.NO_SCALE;
/*
* @property {number} fullScreenScaleMode - Scale mode to be used in fullScreen
*/
this.fullScreenScaleMode = Phaser.ScaleManager.NO_SCALE;
/**
* @property {number} _startHeight - Internal cache var. Stage height when starting the game.
* @private
*/
this._startHeight = 0;
/**
* @property {number} _width - Cached stage width for full screen mode.
* @private
*/
this._width = 0;
/**
* @property {number} _height - Cached stage height for full screen mode.
* @private
*/
this._height = 0;
/**
* @property {number} _check - Cached size interval var.
* @private
*/
this._check = null;
var _this = this;
window.addEventListener('orientationchange', function (event) {
return _this.checkOrientation(event);
}, false);
window.addEventListener('resize', function (event) {
return _this.checkResize(event);
}, false);
document.addEventListener('webkitfullscreenchange', function (event) {
return _this.fullScreenChange(event);
}, false);
document.addEventListener('mozfullscreenchange', function (event) {
return _this.fullScreenChange(event);
}, false);
document.addEventListener('fullscreenchange', function (event) {
return _this.fullScreenChange(event);
}, false);
};
/**
* @constant
* @type {number}
*/
Phaser.ScaleManager.EXACT_FIT = 0;
/**
* @constant
* @type {number}
*/
Phaser.ScaleManager.NO_SCALE = 1;
/**
* @constant
* @type {number}
*/
Phaser.ScaleManager.SHOW_ALL = 2;
Phaser.ScaleManager.prototype = {
/**
* Tries to enter the browser into full screen mode.
* Please note that this needs to be supported by the web browser and isn't the same thing as setting your game to fill the browser.
* @method Phaser.ScaleManager#startFullScreen
* @param {boolean} antialias - You can toggle the anti-alias feature of the canvas before jumping in to full screen (false = retain pixel art, true = smooth art)
*/
startFullScreen: function (antialias) {
if (this.isFullScreen || !this.game.device.fullscreen)
{
return;
}
if (typeof antialias !== 'undefined' && this.game.renderType === Phaser.CANVAS)
{
this.game.stage.smoothed = antialias;
}
this._width = this.width;
this._height = this.height;
if (this.game.device.fullscreenKeyboard)
{
this.fullScreenTarget[this.game.device.requestFullscreen](Element.ALLOW_KEYBOARD_INPUT);
}
else
{
this.fullScreenTarget[this.game.device.requestFullscreen]();
}
},
/**
* Stops full screen mode if the browser is in it.
* @method Phaser.ScaleManager#stopFullScreen
*/
stopFullScreen: function () {
this.fullScreenTarget[this.game.device.cancelFullscreen]();
},
/**
* Called automatically when the browser enters of leaves full screen mode.
* @method Phaser.ScaleManager#fullScreenChange
* @param {Event} event - The fullscreenchange event
* @protected
*/
fullScreenChange: function (event) {
this.event = event;
if (this.isFullScreen)
{
if (this.fullScreenScaleMode === Phaser.ScaleManager.EXACT_FIT)
{
this.fullScreenTarget.style['width'] = '100%';
this.fullScreenTarget.style['height'] = '100%';
this.width = window.outerWidth;
this.height = window.outerHeight;
this.game.input.scale.setTo(this.game.width / this.width, this.game.height / this.height);
this.aspectRatio = this.width / this.height;
this.scaleFactor.x = this.game.width / this.width;
this.scaleFactor.y = this.game.height / this.height;
this.checkResize();
}
else if (this.fullScreenScaleMode === Phaser.ScaleManager.SHOW_ALL)
{
this.setShowAll();
this.refresh();
}
this.enterFullScreen.dispatch(this.width, this.height);
}
else
{
this.fullScreenTarget.style['width'] = this.game.width + 'px';
this.fullScreenTarget.style['height'] = this.game.height + 'px';
this.width = this._width;
this.height = this._height;
this.game.input.scale.setTo(this.game.width / this.width, this.game.height / this.height);
this.aspectRatio = this.width / this.height;
this.scaleFactor.x = this.game.width / this.width;
this.scaleFactor.y = this.game.height / this.height;
this.leaveFullScreen.dispatch(this.width, this.height);
}
},
/**
* If you need your game to run in only one orientation you can force that to happen.
* The optional orientationImage is displayed when the game is in the incorrect orientation.
* @method Phaser.ScaleManager#forceOrientation
* @param {boolean} forceLandscape - true if the game should run in landscape mode only.
* @param {boolean} [forcePortrait=false] - true if the game should run in portrait mode only.
* @param {string} [orientationImage=''] - The string of an image in the Phaser.Cache to display when this game is in the incorrect orientation.
*/
forceOrientation: function (forceLandscape, forcePortrait, orientationImage) {
if (typeof forcePortrait === 'undefined') { forcePortrait = false; }
this.forceLandscape = forceLandscape;
this.forcePortrait = forcePortrait;
if (typeof orientationImage !== 'undefined')
{
if (orientationImage === null || this.game.cache.checkImageKey(orientationImage) === false)
{
orientationImage = '__default';
}
this.orientationSprite = new Phaser.Image(this.game, this.game.width / 2, this.game.height / 2, PIXI.TextureCache[orientationImage]);
this.orientationSprite.anchor.set(0.5);
this.checkOrientationState();
if (this.incorrectOrientation)
{
this.orientationSprite.visible = true;
this.game.world.visible = false;
}
else
{
this.orientationSprite.visible = false;
this.game.world.visible = true;
}
this.game.stage.addChild(this.orientationSprite);
}
},
/**
* Checks if the browser is in the correct orientation for your game (if forceLandscape or forcePortrait have been set)
* @method Phaser.ScaleManager#checkOrientationState
*/
checkOrientationState: function () {
// They are in the wrong orientation
if (this.incorrectOrientation)
{
if ((this.forceLandscape && window.innerWidth > window.innerHeight) || (this.forcePortrait && window.innerHeight > window.innerWidth))
{
// Back to normal
this.incorrectOrientation = false;
this.leaveIncorrectOrientation.dispatch();
if (this.orientationSprite)
{
this.orientationSprite.visible = false;
this.game.world.visible = true;
}
if (this.scaleMode !== Phaser.ScaleManager.NO_SCALE)
{
this.refresh();
}
}
}
else
{
if ((this.forceLandscape && window.innerWidth < window.innerHeight) || (this.forcePortrait && window.innerHeight < window.innerWidth))
{
// Show orientation screen
this.incorrectOrientation = true;
this.enterIncorrectOrientation.dispatch();
if (this.orientationSprite && this.orientationSprite.visible === false)
{
this.orientationSprite.visible = true;
this.game.world.visible = false;
}
if (this.scaleMode !== Phaser.ScaleManager.NO_SCALE)
{
this.refresh();
}
}
}
},
/**
* Handle window.orientationchange events
* @method Phaser.ScaleManager#checkOrientation
* @param {Event} event - The orientationchange event data.
*/
checkOrientation: function (event) {
this.event = event;
this.orientation = window['orientation'];
if (this.isLandscape)
{
this.enterLandscape.dispatch(this.orientation, true, false);
}
else
{
this.enterPortrait.dispatch(this.orientation, false, true);
}
if (this.scaleMode !== Phaser.ScaleManager.NO_SCALE)
{
this.refresh();
}
},
/**
* Handle window.resize events
* @method Phaser.ScaleManager#checkResize
* @param {Event} event - The resize event data.
*/
checkResize: function (event) {
this.event = event;
if (window.outerWidth > window.outerHeight)
{
this.orientation = 90;
}
else
{
this.orientation = 0;
}
if (this.isLandscape)
{
this.enterLandscape.dispatch(this.orientation, true, false);
}
else
{
this.enterPortrait.dispatch(this.orientation, false, true);
}
if (this.scaleMode !== Phaser.ScaleManager.NO_SCALE)
{
this.refresh();
}
this.checkOrientationState();
},
/**
* Re-calculate scale mode and update screen size.
* @method Phaser.ScaleManager#refresh
*/
refresh: function () {
// We can't do anything about the status bars in iPads, web apps or desktops
if (!this.game.device.iPad && !this.game.device.webApp && !this.game.device.desktop)
{
// TODO - Test this
// this._startHeight = window.innerHeight;
if (this.game.device.android && !this.game.device.chrome)
{
window.scrollTo(0, 1);
}
else
{
window.scrollTo(0, 0);
}
}
if (this._check === null && this.maxIterations > 0)
{
this._iterations = this.maxIterations;
var _this = this;
this._check = window.setInterval(function () {
return _this.setScreenSize();
}, 10);
this.setScreenSize();
}
},
/**
* Set screen size automatically based on the scaleMode.
* @param {boolean} force - If force is true it will try to resize the game regardless of the document dimensions.
*/
setScreenSize: function (force) {
if (typeof force === 'undefined')
{
force = false;
}
if (!this.game.device.iPad && !this.game.device.webApp && !this.game.device.desktop)
{
if (this.game.device.android && !this.game.device.chrome)
{
window.scrollTo(0, 1);
}
else
{
window.scrollTo(0, 0);
}
}
this._iterations--;
if (force || window.innerHeight > this._startHeight || this._iterations < 0)
{
// Set minimum height of content to new window height
document.documentElement['style'].minHeight = window.innerHeight + 'px';
if (this.incorrectOrientation)
{
this.setMaximum();
}
else if (!this.isFullScreen)
{
if (this.scaleMode === Phaser.ScaleManager.EXACT_FIT)
{
this.setExactFit();
}
else if (this.scaleMode === Phaser.ScaleManager.SHOW_ALL)
{
this.setShowAll();
}
}
else
{
if (this.fullScreenScaleMode === Phaser.ScaleManager.EXACT_FIT)
{
this.setExactFit();
}
else if (this.fullScreenScaleMode === Phaser.ScaleManager.SHOW_ALL)
{
this.setShowAll();
}
}
this.setSize();
clearInterval(this._check);
this._check = null;
}
},
/**
* Sets the canvas style width and height values based on minWidth/Height and maxWidth/Height.
* @method Phaser.ScaleManager#setSize
*/
setSize: function () {
if (!this.incorrectOrientation)
{
if (this.maxWidth && this.width > this.maxWidth)
{
this.width = this.maxWidth;
}
if (this.maxHeight && this.height > this.maxHeight)
{
this.height = this.maxHeight;
}
if (this.minWidth && this.width < this.minWidth)
{
this.width = this.minWidth;
}
if (this.minHeight && this.height < this.minHeight)
{
this.height = this.minHeight;
}
}
this.game.canvas.style.width = this.width + 'px';
this.game.canvas.style.height = this.height + 'px';
this.game.input.scale.setTo(this.game.width / this.width, this.game.height / this.height);
if (this.pageAlignHorizontally)
{
if (this.width < window.innerWidth && !this.incorrectOrientation)
{
this.margin.x = Math.round((window.innerWidth - this.width) / 2);
this.game.canvas.style.marginLeft = this.margin.x + 'px';
}
else
{
this.margin.x = 0;
this.game.canvas.style.marginLeft = '0px';
}
}
if (this.pageAlignVertically)
{
if (this.height < window.innerHeight && !this.incorrectOrientation)
{
this.margin.y = Math.round((window.innerHeight - this.height) / 2);
this.game.canvas.style.marginTop = this.margin.y + 'px';
}
else
{
this.margin.y = 0;
this.game.canvas.style.marginTop = '0px';
}
}
Phaser.Canvas.getOffset(this.game.canvas, this.game.stage.offset);
this.bounds.setTo(this.game.stage.offset.x, this.game.stage.offset.y, this.width, this.height);
this.aspectRatio = this.width / this.height;
this.scaleFactor.x = this.game.width / this.width;
this.scaleFactor.y = this.game.height / this.height;
this.scaleFactorInversed.x = this.width / this.game.width;
this.scaleFactorInversed.y = this.height / this.game.height;
this.hasResized.dispatch(this.width, this.height);
this.checkOrientationState();
},
/**
* Sets this.width equal to window.innerWidth and this.height equal to window.innerHeight
* @method Phaser.ScaleManager#setMaximum
*/
setMaximum: function () {
this.width = window.innerWidth;
this.height = window.innerHeight;
},
/**
* Calculates the multiplier needed to scale the game proportionally.
* @method Phaser.ScaleManager#setShowAll
*/
setShowAll: function () {
var multiplier = Math.min((window.innerHeight / this.game.height), (window.innerWidth / this.game.width));
this.width = Math.round(this.game.width * multiplier);
this.height = Math.round(this.game.height * multiplier);
},
/**
* Sets the width and height values of the canvas, no larger than the maxWidth/Height.
* @method Phaser.ScaleManager#setExactFit
*/
setExactFit: function () {
var availableWidth = window.innerWidth;
var availableHeight = window.innerHeight;
if (this.maxWidth && availableWidth > this.maxWidth)
{
this.width = this.maxWidth;
}
else
{
this.width = availableWidth;
}
if (this.maxHeight && availableHeight > this.maxHeight)
{
this.height = this.maxHeight;
}
else
{
this.height = availableHeight;
}
}
};
Phaser.ScaleManager.prototype.constructor = Phaser.ScaleManager;
/**
* @name Phaser.ScaleManager#isFullScreen
* @property {boolean} isFullScreen - Returns true if the browser is in full screen mode, otherwise false.
* @readonly
*/
Object.defineProperty(Phaser.ScaleManager.prototype, "isFullScreen", {
get: function () {
return (document['fullscreenElement'] || document['mozFullScreenElement'] || document['webkitFullscreenElement']);
}
});
/**
* @name Phaser.ScaleManager#isPortrait
* @property {boolean} isPortrait - Returns true if the browser dimensions match a portrait display.
* @readonly
*/
Object.defineProperty(Phaser.ScaleManager.prototype, "isPortrait", {
get: function () {
return (this.orientation === 0 || this.orientation === 180);
}
});
/**
* @name Phaser.ScaleManager#isLandscape
* @property {boolean} isLandscape - Returns true if the browser dimensions match a landscape display.
* @readonly
*/
Object.defineProperty(Phaser.ScaleManager.prototype, "isLandscape", {
get: function () {
return (this.orientation === 90 || this.orientation === -90);
}
});

304
src/core/Signal.js Normal file
View File

@@ -0,0 +1,304 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2014 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 () {
/**
* @property {Array.<Phaser.SignalBinding>} _bindings - Internal variable.
* @private
*/
this._bindings = [];
/**
* @property {any} _prevParams - Internal variable.
* @private
*/
this._prevParams = null;
// enforce dispatch to aways work on same context (#47)
var self = this;
/**
* @property {function} dispatch - The dispatch function is what sends the Signal out.
*/
this.dispatch = function(){
Phaser.Signal.prototype.dispatch.apply(self, arguments);
};
};
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.
* @property {boolean} memorize
*/
memorize: false,
/**
* @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>
* @property {boolean} active
* @default
*/
active: true,
/**
* @method Phaser.Signal#validateListener
* @param {function} listener - Signal handler function.
* @param {string} fnName - Function name.
* @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));
}
},
/**
* @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) {
var prevIndex = this._indexOfListener(listener, listenerContext),
binding;
if (prevIndex !== -1) {
binding = this._bindings[prevIndex];
if (binding.isOnce() !== isOnce) {
throw new Error('You cannot add' + (isOnce ? '' : 'Once') + '() then add' + (!isOnce ? '' : 'Once') + '() the same listener without removing the relationship first.');
}
} else {
binding = new Phaser.SignalBinding(this, listener, isOnce, listenerContext, priority);
this._addBinding(binding);
}
if (this.memorize && this._prevParams) {
binding.execute(this._prevParams);
}
return binding;
},
/**
* @method Phaser.Signal#_addBinding
* @param {Phaser.SignalBinding} binding - An Object representing the binding between the Signal and listener.
* @private
*/
_addBinding: function (binding) {
//simplified insertion sort
var n = this._bindings.length;
do { --n; } while (this._bindings[n] && binding._priority <= this._bindings[n]._priority);
this._bindings.splice(n + 1, 0, binding);
},
/**
* @method Phaser.Signal#_indexOfListener
* @param {function} listener - Signal handler function.
* @return {number} Description.
* @private
*/
_indexOfListener: function (listener, context) {
var n = this._bindings.length,
cur;
while (n--) {
cur = this._bindings[n];
if (cur._listener === listener && cur.context === context) {
return n;
}
}
return -1;
},
/**
* Check if listener was attached to Signal.
*
* @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;
},
/**
* Add a listener to the signal.
*
* @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) {
this.validateListener(listener, 'add');
return this._registerListener(listener, false, listenerContext, priority);
},
/**
* 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.
*
* @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)
{
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.
*
* @method Phaser.Signal#removeAll
*/
removeAll: function () {
var n = this._bindings.length;
while (n--) {
this._bindings[n]._destroy();
}
this._bindings.length = 0;
},
/**
* 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.
* IMPORTANT: should be called only during signal dispatch, calling it before/after dispatch won't affect signal broadcast.
* @see Signal.prototype.disable
*
* @method Phaser.Signal#halt
*/
halt: function () {
this._shouldPropagate = false;
},
/**
* 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 () {
if (!this.active)
{
return;
}
var paramsArr = Array.prototype.slice.call(arguments);
var n = this._bindings.length;
var bindings;
if (this.memorize)
{
this._prevParams = paramsArr;
}
if (!n)
{
// Should come after memorize
return;
}
bindings = this._bindings.slice(); //clone array in case add/remove items during dispatch
this._shouldPropagate = true; //in case `halt` was called before dispatch or during the previous dispatch.
//execute all callbacks until end of the list or until a callback returns `false` or stops propagation
//reverse loop since listeners with higher priority will be added at the end of the list
do { n--; } while (bindings[n] && this._shouldPropagate && bindings[n].execute(paramsArr) !== false);
},
/**
* 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).
* IMPORTANT: calling any method on the signal instance after calling dispose will throw errors.
*
* @method Phaser.Signal#dispose
*/
dispose: function () {
this.removeAll();
delete this._bindings;
delete this._prevParams;
},
/**
*
* @method Phaser.Signal#toString
* @return {string} String representation of the object.
*/
toString: function () {
return '[Phaser.Signal active:'+ this.active +' numListeners:'+ this.getNumListeners() +']';
}
};
Phaser.Signal.prototype.constructor = Phaser.Signal;

159
src/core/SignalBinding.js Normal file
View File

@@ -0,0 +1,159 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2014 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* @class Phaser.SignalBinding
* @classdesc Object that represents a binding between a Signal and a listener function.
* This is an internal constructor and shouldn't be called by regular users.
* Inspired by Joa Ebert AS3 SignalBinding and Robert Penner's Slot classes.
*
* @author Miller Medeiros http://millermedeiros.github.com/js-signals/
* @constructor
* @param {Phaser.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) {
/**
* @property {Phaser.Game} _listener - Handler function bound to the signal.
* @private
*/
this._listener = listener;
/**
* @property {boolean} _isOnce - If binding should be executed just once.
* @private
*/
this._isOnce = isOnce;
/**
* @property {object|undefined|null} context - Context on which listener will be executed (object that should represent the `this` variable inside listener function).
*/
this.context = listenerContext;
/**
* @property {Phaser.Signal} _signal - Reference to Signal object that listener is currently bound to.
* @private
*/
this._signal = signal;
/**
* @property {number} _priority - Listener priority.
* @private
*/
this._priority = priority || 0;
};
Phaser.SignalBinding.prototype = {
/**
* 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).
* @property {array|null} params
* @default
*/
params: null,
/**
* Call listener passing arbitrary parameters.
* 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.
* @method Phaser.SignalBinding#execute
* @param {array} [paramsArr] - Array of parameters that should be passed to the listener.
* @return {any} Value returned by the listener.
*/
execute: function(paramsArr) {
var handlerReturn, params;
if (this.active && !!this._listener)
{
params = this.params ? this.params.concat(paramsArr) : paramsArr;
handlerReturn = this._listener.apply(this.context, params);
if (this._isOnce)
{
this.detach();
}
}
return handlerReturn;
},
/**
* Detach binding from signal.
* 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;
},
/**
* @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);
},
/**
* @method Phaser.SignalBinding#isOnce
* @return {boolean} If SignalBinding will only be executed once.
*/
isOnce: function () {
return this._isOnce;
},
/**
* @method Phaser.SignalBinding#getListener
* @return {Function} Handler function bound to the signal.
*/
getListener: function () {
return this._listener;
},
/**
* @method Phaser.SignalBinding#getSignal
* @return {Signal} Signal that listener is currently bound to.
*/
getSignal: function () {
return this._signal;
},
/**
* @method Phaser.SignalBinding#_destroy
* Delete instance properties
* @private
*/
_destroy: function () {
delete this._signal;
delete this._listener;
delete this.context;
},
/**
* @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 + ']';
}
};
Phaser.SignalBinding.prototype.constructor = Phaser.SignalBinding;

421
src/core/Stage.js Normal file
View File

@@ -0,0 +1,421 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2014 Photon Storm Ltd.
* @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 Phaser.Stage
* @extends PIXI.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.
*/
Phaser.Stage = function (game, width, height) {
/**
* @property {Phaser.Game} game - A reference to the currently running Game.
*/
this.game = game;
/**
* @property {Phaser.Point} offset - Holds the offset coordinates of the Game.canvas from the top-left of the browser window (used by Input and other classes)
*/
this.offset = new Phaser.Point();
/**
* @property {Phaser.Rectangle} bounds - The bounds of the Stage. Typically x/y = Stage.offset.x/y and the width/height match the game width and height.
*/
this.bounds = new Phaser.Rectangle(0, 0, width, height);
PIXI.Stage.call(this, 0x000000, false);
/**
* @property {string} name - The name of this object.
* @default
*/
this.name = '_stage_root';
/**
* @property {boolean} interactive - Pixi level var, ignored by Phaser.
* @default
* @private
*/
this.interactive = false;
/**
* @property {boolean} disableVisibilityChange - By default if the browser tab loses focus the game will pause. You can stop that behaviour by setting this property to true.
* @default
*/
this.disableVisibilityChange = false;
/**
* @property {number|false} checkOffsetInterval - The time (in ms) between which the stage should check to see if it has moved.
* @default
*/
this.checkOffsetInterval = 2500;
/**
* @property {boolean} exists - If exists is true the Stage and all children are updated, otherwise it is skipped.
* @default
*/
this.exists = true;
/**
* @property {number} currentRenderOrderID - Reset each frame, keeps a count of the total number of objects updated.
*/
this.currentRenderOrderID = 0;
/**
* @property {string} hiddenVar - The page visibility API event name.
* @private
*/
this._hiddenVar = 'hidden';
/**
* @property {number} _nextOffsetCheck - The time to run the next offset check.
* @private
*/
this._nextOffsetCheck = 0;
/**
* @property {number} _backgroundColor - Stage background color.
* @private
*/
this._backgroundColor = 0x000000;
if (game.config)
{
this.parseConfig(game.config);
}
else
{
this.game.canvas = Phaser.Canvas.create(width, height);
this.game.canvas.style['-webkit-full-screen'] = 'width: 100%; height: 100%';
}
};
Phaser.Stage.prototype = Object.create(PIXI.Stage.prototype);
Phaser.Stage.prototype.constructor = Phaser.Stage;
/**
* This is called automatically after the plugins preUpdate and before the State.update.
* Most objects have preUpdate methods and it's where initial movement and positioning is done.
*
* @method Phaser.Stage#preUpdate
*/
Phaser.Stage.prototype.preUpdate = function () {
this.currentRenderOrderID = 0;
// This can't loop in reverse, we need the orderID to be in sequence
var len = this.children.length;
for (var i = 0; i < len; i++)
{
this.children[i].preUpdate();
}
};
/**
* This is called automatically after the State.update, but before particles or plugins update.
*
* @method Phaser.Stage#update
*/
Phaser.Stage.prototype.update = function () {
var i = this.children.length;
while (i--)
{
this.children[i].update();
}
};
/**
* This is called automatically before the renderer runs and after the plugins have updated.
* In postUpdate this is where all the final physics calculatations and object positioning happens.
* The objects are processed in the order of the display list.
* The only exception to this is if the camera is following an object, in which case that is updated first.
*
* @method Phaser.Stage#postUpdate
*/
Phaser.Stage.prototype.postUpdate = function () {
if (this.game.world.camera.target)
{
this.game.world.camera.target.postUpdate();
this.game.world.camera.update();
var i = this.children.length;
while (i--)
{
if (this.children[i] !== this.game.world.camera.target)
{
this.children[i].postUpdate();
}
}
}
else
{
this.game.world.camera.update();
var i = this.children.length;
while (i--)
{
this.children[i].postUpdate();
}
}
if (this.checkOffsetInterval !== false)
{
if (this.game.time.now > this._nextOffsetCheck)
{
Phaser.Canvas.getOffset(this.game.canvas, this.offset);
this.bounds.x = this.offset.x;
this.bounds.y = this.offset.y;
this._nextOffsetCheck = this.game.time.now + this.checkOffsetInterval;
}
}
};
/**
* Parses a Game configuration object.
*
* @method Phaser.Stage#parseConfig
* @protected
*/
Phaser.Stage.prototype.parseConfig = function (config) {
if (config['canvasID'])
{
this.game.canvas = Phaser.Canvas.create(this.game.width, this.game.height, config['canvasID']);
}
else
{
this.game.canvas = Phaser.Canvas.create(this.game.width, this.game.height);
}
if (config['canvasStyle'])
{
this.game.canvas.stlye = config['canvasStyle'];
}
else
{
this.game.canvas.style['-webkit-full-screen'] = 'width: 100%; height: 100%';
}
if (config['checkOffsetInterval'])
{
this.checkOffsetInterval = config['checkOffsetInterval'];
}
if (config['disableVisibilityChange'])
{
this.disableVisibilityChange = config['disableVisibilityChange'];
}
if (config['fullScreenScaleMode'])
{
this.fullScreenScaleMode = config['fullScreenScaleMode'];
}
if (config['scaleMode'])
{
this.scaleMode = config['scaleMode'];
}
if (config['backgroundColor'])
{
this.backgroundColor = config['backgroundColor'];
}
};
/**
* Initialises the stage and adds the event listeners.
* @method Phaser.Stage#boot
* @private
*/
Phaser.Stage.prototype.boot = function () {
Phaser.Canvas.getOffset(this.game.canvas, this.offset);
this.bounds.setTo(this.offset.x, this.offset.y, this.game.width, this.game.height);
var _this = this;
this._onChange = function (event) {
return _this.visibilityChange(event);
};
Phaser.Canvas.setUserSelect(this.game.canvas, 'none');
Phaser.Canvas.setTouchAction(this.game.canvas, 'none');
this.checkVisibility();
};
/**
* Starts a page visibility event listener running, or window.blur/focus if not supported by the browser.
* @method Phaser.Stage#checkVisibility
*/
Phaser.Stage.prototype.checkVisibility = function () {
if (document.webkitHidden !== undefined)
{
this._hiddenVar = 'webkitvisibilitychange';
}
else if (document.mozHidden !== undefined)
{
this._hiddenVar = 'mozvisibilitychange';
}
else if (document.msHidden !== undefined)
{
this._hiddenVar = 'msvisibilitychange';
}
else if (document.hidden !== undefined)
{
this._hiddenVar = 'visibilitychange';
}
else
{
this._hiddenVar = null;
}
// Does browser support it? If not (like in IE9 or old Android) we need to fall back to blur/focus
if (this._hiddenVar)
{
document.addEventListener(this._hiddenVar, this._onChange, false);
}
window.onpagehide = this._onChange;
window.onpageshow = this._onChange;
window.onblur = this._onChange;
window.onfocus = this._onChange;
};
/**
* This method is called when the document visibility is changed.
* @method Phaser.Stage#visibilityChange
* @param {Event} event - Its type will be used to decide whether the game should be paused or not.
*/
Phaser.Stage.prototype.visibilityChange = function (event) {
if (this.disableVisibilityChange)
{
return;
}
if (event.type === 'pagehide' || event.type === 'blur' || event.type === 'pageshow' || event.type === 'focus')
{
if (event.type === 'pagehide' || event.type === 'blur')
{
this.game.focusLoss(event);
}
else if (event.type === 'pageshow' || event.type === 'focus')
{
this.game.focusGain(event);
}
return;
}
if (document.hidden || document.mozHidden || document.msHidden || document.webkitHidden)
{
this.game.gamePaused(event);
}
else
{
this.game.gameResumed(event);
}
};
/**
* Sets the background color for the Stage. The color can be given as a hex value (#RRGGBB) or a numeric value (0xRRGGBB)
*
* @name Phaser.Stage#setBackgroundColor
* @param {number|string} backgroundColor - The color of the background.
*/
Phaser.Stage.prototype.setBackgroundColor = function(backgroundColor)
{
if (typeof backgroundColor === 'string')
{
var rgb = Phaser.Color.hexToColor(backgroundColor);
this._backgroundColor = Phaser.Color.getColor(rgb.r, rgb.g, rgb.b);
}
else
{
var rgb = Phaser.Color.getRGB(backgroundColor);
this._backgroundColor = backgroundColor;
}
this.backgroundColorSplit = [ rgb.r / 255, rgb.g / 255, rgb.b / 255 ];
this.backgroundColorString = Phaser.Color.RGBtoString(rgb.r, rgb.g, rgb.b, 255, '#');
};
/**
* @name Phaser.Stage#backgroundColor
* @property {number|string} backgroundColor - 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", {
get: function () {
return this._backgroundColor;
},
set: function (color) {
if (!this.game.transparent)
{
this.setBackgroundColor(color);
}
}
});
/**
* Enable or disable texture smoothing for all objects on this Stage. Only works for bitmap/image textures. Smoothing is enabled by default.
*
* @name Phaser.Stage#smoothed
* @property {boolean} smoothed - Set to true to smooth all sprites rendered on this Stage, or false to disable smoothing (great for pixel art)
*/
Object.defineProperty(Phaser.Stage.prototype, "smoothed", {
get: function () {
return !PIXI.scaleModes.LINEAR;
},
set: function (value) {
if (value)
{
PIXI.scaleModes.LINEAR = 0;
}
else
{
PIXI.scaleModes.LINEAR = 1;
}
}
});

188
src/core/State.js Normal file
View File

@@ -0,0 +1,188 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2014 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.
*
* @class Phaser.State
* @constructor
*/
Phaser.State = function () {
/**
* @property {Phaser.Game} game - This is a reference to the currently running Game.
*/
this.game = null;
/**
* @property {Phaser.GameObjectFactory} add - A reference to the GameObjectFactory which can be used to add new objects to the World.
*/
this.add = null;
/**
* @property {Phaser.GameObjectCreator} make - A reference to the GameObjectCreator which can be used to make new objects.
*/
this.make = null;
/**
* @property {Phaser.Camera} camera - A handy reference to World.camera.
*/
this.camera = null;
/**
* @property {Phaser.Cache} cache - A reference to the game cache which contains any loaded or generated assets, such as images, sound and more.
*/
this.cache = null;
/**
* @property {Phaser.Input} input - A reference to the Input Manager.
*/
this.input = null;
/**
* @property {Phaser.Loader} load - A reference to the Loader, which you mostly use in the preload method of your state to load external assets.
*/
this.load = null;
/**
* @property {Phaser.Math} math - A reference to Math class with lots of helpful functions.
*/
this.math = null;
/**
* @property {Phaser.SoundManager} sound - A reference to the Sound Manager which can create, play and stop sounds, as well as adjust global volume.
*/
this.sound = null;
/**
* @property {Phaser.ScaleManager} scale - A reference to the Scale Manager which controls the way the game scales on different displays.
*/
this.scale = null;
/**
* @property {Phaser.Stage} stage - A reference to the Stage.
*/
this.stage = null;
/**
* @property {Phaser.Time} time - A reference to the game clock and timed events system.
*/
this.time = null;
/**
* @property {Phaser.TweenManager} tweens - A reference to the tween manager.
*/
this.tweens = null;
/**
* @property {Phaser.World} world - A reference to the game world. All objects live in the Game World and its size is not bound by the display resolution.
*/
this.world = null;
/**
* @property {Phaser.Particles} particles - The Particle Manager. It is called during the core gameloop and updates any Particle Emitters it has created.
*/
this.particles = null;
/**
* @property {Phaser.Physics} physics - A reference to the physics manager which looks after the different physics systems available within Phaser.
*/
this.physics = null;
/**
* @property {Phaser.RandomDataGenerator} rnd - A reference to the seeded and repeatable random data generator.
*/
this.rnd = null;
};
Phaser.State.prototype = {
/**
* preload is called first. Normally you'd use this to load your game assets (or those needed for the current State)
* You shouldn't create any objects in this method that require assets that you're also loading in this method, as
* they won't yet be available.
*
* @method Phaser.State#preload
*/
preload: function () {
},
/**
* loadUpdate is called during the Loader process. This only happens if you've set one or more assets to load in the preload method.
*
* @method Phaser.State#loadUpdate
*/
loadUpdate: function () {
},
/**
* loadRender is called during the Loader process. This only happens if you've set one or more assets to load in the preload method.
* The difference between loadRender and render is that any objects you render in this method you must be sure their assets exist.
*
* @method Phaser.State#loadRender
*/
loadRender: function () {
},
/**
* create is called once preload has completed, this includes the loading of any assets from the Loader.
* If you don't have a preload method then create is the first method called in your State.
*
* @method Phaser.State#create
*/
create: function () {
},
/**
* The update method is left empty for your own use.
* It is called during the core game loop AFTER debug, physics, plugins and the Stage have had their preUpdate methods called.
* If is called BEFORE Stage, Tweens, Sounds, Input, Physics, Particles and Plugins have had their postUpdate methods called.
*
* @method Phaser.State#update
*/
update: function () {
},
/**
* Nearly all display objects in Phaser render automatically, you don't need to tell them to render.
* However the render method is called AFTER the game renderer and plugins have rendered, so you're able to do any
* final post-processing style effects here. Note that this happens before plugins postRender takes place.
*
* @method Phaser.State#render
*/
render: function () {
},
/**
* This method will be called if the core game loop is paused.
*
* @method Phaser.State#paused
*/
paused: function () {
},
/**
* pauseUpdate is called while the game is paused instead of preUpdate, update and postUpdate.
*
* @method Phaser.State#pauseUpdate
*/
pauseUpdate: function () {
},
/**
* This method will be called when the State is shutdown (i.e. you switch to another state from this one).
*
* @method Phaser.State#shutdown
*/
shutdown: function () {
}
};
Phaser.State.prototype.constructor = Phaser.State;

637
src/core/StateManager.js Normal file
View File

@@ -0,0 +1,637 @@
/* jshint newcap: false */
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2014 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) {
/**
* @property {Phaser.Game} game - A reference to the currently running game.
*/
this.game = game;
/**
* @property {Object} states - The object containing Phaser.States.
*/
this.states = {};
/**
* @property {Phaser.State} _pendingState - The state to be switched to in the next frame.
* @private
*/
this._pendingState = null;
if (typeof pendingState !== 'undefined' && pendingState !== null)
{
this._pendingState = pendingState;
}
/**
* @property {boolean} _clearWorld - Clear the world when we switch state?
* @private
*/
this._clearWorld = false;
/**
* @property {boolean} _clearCache - Clear the cache when we switch state?
* @private
*/
this._clearCache = false;
/**
* @property {boolean} _created - Flag that sets if the State has been created or not.
* @private
*/
this._created = false;
/**
* @property {array} _args - Temporary container when you pass vars from one State to another.
* @private
*/
this._args = [];
/**
* @property {string} current - The current active State object (defaults to null).
*/
this.current = '';
/**
* @property {function} onInitCallback - This will be called when the state is started (i.e. set as the current active state).
*/
this.onInitCallback = null;
/**
* @property {function} onPreloadCallback - This will be called when init states (loading assets...).
*/
this.onPreloadCallback = null;
/**
* @property {function} onCreateCallback - This will be called when create states (setup states...).
*/
this.onCreateCallback = null;
/**
* @property {function} onUpdateCallback - This will be called when State is updated, this doesn't happen during load (@see onLoadUpdateCallback).
*/
this.onUpdateCallback = null;
/**
* @property {function} onRenderCallback - This will be called when the State is rendered, this doesn't happen during load (see onLoadRenderCallback).
*/
this.onRenderCallback = null;
/**
* @property {function} onPreRenderCallback - This will be called before the State is rendered and before the stage is cleared.
*/
this.onPreRenderCallback = null;
/**
* @property {function} onLoadUpdateCallback - This will be called when the State is updated but only during the load process.
*/
this.onLoadUpdateCallback = null;
/**
* @property {function} onLoadRenderCallback - This will be called when the State is rendered but only during the load process.
*/
this.onLoadRenderCallback = null;
/**
* @property {function} onPausedCallback - This will be called once each time the game is paused.
*/
this.onPausedCallback = null;
/**
* @property {function} onResumedCallback - This will be called once each time the game is resumed from a paused state.
*/
this.onResumedCallback = null;
/**
* @property {function} onPauseUpdateCallback - This will be called every frame while the game is paused.
*/
this.onPauseUpdateCallback = null;
/**
* @property {function} onShutDownCallback - This will be called when the state is shut down (i.e. swapped to another state).
*/
this.onShutDownCallback = null;
};
Phaser.StateManager.prototype = {
/**
* The Boot handler is called by Phaser.Game when it first starts up.
* @method Phaser.StateManager#boot
* @private
*/
boot: function () {
this.game.onPause.add(this.pause, this);
this.game.onResume.add(this.resume, this);
this.game.load.onLoadComplete.add(this.loadComplete, this);
if (this._pendingState !== null)
{
if (typeof this._pendingState === 'string')
{
// State was already added, so just start it
this.start(this._pendingState, false, false);
}
else
{
this.add('default', this._pendingState, true);
}
}
},
/**
* Adds a new State into the StateManager. You must give each State a unique key by which you'll identify it.
* The State can be either a Phaser.State object (or an object that extends it), a plain JavaScript object or a function.
* If a function is given a new state object will be created by calling it.
*
* @method Phaser.StateManager#add
* @param {string} key - A unique key you use to reference this state, i.e. "MainMenu", "Level1".
* @param {Phaser.State|object|function} state - The state you want to switch to.
* @param {boolean} [autoStart=false] - If true the State will be started immediately after adding it.
*/
add: function (key, state, autoStart) {
if (typeof autoStart === "undefined") { autoStart = false; }
var newState;
if (state instanceof Phaser.State)
{
newState = state;
}
else if (typeof state === 'object')
{
newState = state;
newState.game = this.game;
}
else if (typeof state === 'function')
{
newState = new state(this.game);
}
this.states[key] = newState;
if (autoStart)
{
if (this.game.isBooted)
{
this.start(key);
}
else
{
this._pendingState = key;
}
}
return newState;
},
/**
* 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)
{
this.callbackContext = null;
this.onInitCallback = null;
this.onShutDownCallback = null;
this.onPreloadCallback = null;
this.onLoadRenderCallback = null;
this.onLoadUpdateCallback = null;
this.onCreateCallback = null;
this.onUpdateCallback = null;
this.onRenderCallback = null;
this.onPausedCallback = null;
this.onResumedCallback = null;
this.onPauseUpdateCallback = null;
}
delete this.states[key];
},
/**
* Start the given State. If a State is already running then State.shutDown will be called (if it exists) before switching to the new State.
*
* @method Phaser.StateManager#start
* @param {string} key - The key of the state you want to start.
* @param {boolean} [clearWorld=true] - Clear everything in the world? This clears the World display list fully (but not the Stage, so if you've added your own objects to the Stage they will need managing directly)
* @param {boolean} [clearCache=false] - Clear the Game.Cache? This purges out all loaded assets. The default is false and you must have clearWorld=true if you want to clearCache as well.
* @param {...*} parameter - Additional parameters that will be passed to the State.init function (if it has one).
*/
start: function (key, clearWorld, clearCache) {
if (typeof clearWorld === "undefined") { clearWorld = true; }
if (typeof clearCache === "undefined") { clearCache = false; }
if (this.checkState(key))
{
// Place the state in the queue. It will be started the next time the game loop starts.
this._pendingState = key;
this._clearWorld = clearWorld;
this._clearCache = clearCache;
if (arguments.length > 3)
{
this._args = Array.prototype.splice.call(arguments, 3);
}
}
},
/**
* Restarts the current State. State.shutDown will be called (if it exists) before the State is restarted.
*
* @method Phaser.StateManager#restart
* @param {boolean} [clearWorld=true] - Clear everything in the world? This clears the World display list fully (but not the Stage, so if you've added your own objects to the Stage they will need managing directly)
* @param {boolean} [clearCache=false] - Clear the Game.Cache? This purges out all loaded assets. The default is false and you must have clearWorld=true if you want to clearCache as well.
* @param {...*} parameter - Additional parameters that will be passed to the State.init function if it has one.
*/
restart: function (clearWorld, clearCache) {
if (typeof clearWorld === "undefined") { clearWorld = true; }
if (typeof clearCache === "undefined") { clearCache = false; }
// Place the state in the queue. It will be started the next time the game loop starts.
this._pendingState = this.current;
this._clearWorld = clearWorld;
this._clearCache = clearCache;
if (arguments.length > 2)
{
this._args = Array.prototype.splice.call(arguments, 2);
}
},
/**
* Used by onInit and onShutdown when those functions don't exist on the state
* @method Phaser.StateManager#dummy
* @private
*/
dummy: function () {
},
/**
* preUpdate is called right at the start of the game loop. It is responsible for changing to a new state that was requested previously.
*
* @method Phaser.StateManager#preUpdate
*/
preUpdate: function () {
if (this._pendingState && this.game.isBooted)
{
// Already got a state running?
if (this.current)
{
this.onShutDownCallback.call(this.callbackContext, this.game);
this.game.tweens.removeAll();
this.game.camera.reset();
this.game.input.reset(true);
this.game.physics.clear();
this.game.time.removeAll();
if (this._clearWorld)
{
this.game.world.shutdown();
if (this._clearCache === true)
{
this.game.cache.destroy();
}
}
}
this.setCurrentState(this._pendingState);
if (this.onPreloadCallback)
{
this.game.load.reset();
this.onPreloadCallback.call(this.callbackContext, this.game);
// Is the loader empty?
if (this.game.load.totalQueuedFiles() === 0)
{
this.loadComplete();
}
else
{
// Start the loader going as we have something in the queue
this.game.load.start();
}
}
else
{
// No init? Then there was nothing to load either
this.loadComplete();
}
if (this.current === this._pendingState)
{
this._pendingState = null;
}
}
},
/**
* Checks if a given phaser state is valid. A State is considered valid if it has at least one of the core functions: preload, create, update or render.
*
* @method Phaser.StateManager#checkState
* @param {string} key - The key of the state you want to check.
* @return {boolean} true if the State has the required functions, otherwise false.
*/
checkState: function (key) {
if (this.states[key])
{
var valid = false;
if (this.states[key]['preload']) { valid = true; }
if (this.states[key]['create']) { valid = true; }
if (this.states[key]['update']) { valid = true; }
if (this.states[key]['render']) { valid = true; }
if (valid === false)
{
console.warn("Invalid Phaser State object given. Must contain at least a one of the required functions: preload, create, update or render");
return false;
}
return true;
}
else
{
console.warn("Phaser.StateManager - No state found with the key: " + key);
return false;
}
},
/**
* Links game properties to the State given by the key.
*
* @method Phaser.StateManager#link
* @param {string} key - State key.
* @protected
*/
link: function (key) {
this.states[key].game = this.game;
this.states[key].add = this.game.add;
this.states[key].make = this.game.make;
this.states[key].camera = this.game.camera;
this.states[key].cache = this.game.cache;
this.states[key].input = this.game.input;
this.states[key].load = this.game.load;
this.states[key].math = this.game.math;
this.states[key].sound = this.game.sound;
this.states[key].scale = this.game.scale;
this.states[key].state = this;
this.states[key].stage = this.game.stage;
this.states[key].time = this.game.time;
this.states[key].tweens = this.game.tweens;
this.states[key].world = this.game.world;
this.states[key].particles = this.game.particles;
this.states[key].rnd = this.game.rnd;
this.states[key].physics = this.game.physics;
},
/**
* Sets the current State. Should not be called directly (use StateManager.start)
*
* @method Phaser.StateManager#setCurrentState
* @param {string} key - State key.
* @private
*/
setCurrentState: function (key) {
this.callbackContext = this.states[key];
this.link(key);
// Used when the state is set as being the current active state
this.onInitCallback = this.states[key]['init'] || this.dummy;
this.onPreloadCallback = this.states[key]['preload'] || null;
this.onLoadRenderCallback = this.states[key]['loadRender'] || null;
this.onLoadUpdateCallback = this.states[key]['loadUpdate'] || null;
this.onCreateCallback = this.states[key]['create'] || null;
this.onUpdateCallback = this.states[key]['update'] || null;
this.onPreRenderCallback = this.states[key]['preRender'] || null;
this.onRenderCallback = this.states[key]['render'] || null;
this.onPausedCallback = this.states[key]['paused'] || null;
this.onResumedCallback = this.states[key]['resumed'] || null;
this.onPauseUpdateCallback = this.states[key]['pauseUpdate'] || null;
// Used when the state is no longer the current active state
this.onShutDownCallback = this.states[key]['shutdown'] || this.dummy;
this.current = key;
this._created = false;
this.onInitCallback.apply(this.callbackContext, this._args);
this._args = [];
},
/**
* Gets the current State.
*
* @method Phaser.StateManager#getCurrentState
* @return Phaser.State
* @public
*/
getCurrentState: function() {
return this.states[this.current];
},
/**
* @method Phaser.StateManager#loadComplete
* @protected
*/
loadComplete: function () {
if (this._created === false && this.onCreateCallback)
{
this._created = true;
this.onCreateCallback.call(this.callbackContext, this.game);
}
else
{
this._created = true;
}
},
/**
* @method Phaser.StateManager#pause
* @protected
*/
pause: function () {
if (this._created && this.onPausedCallback)
{
this.onPausedCallback.call(this.callbackContext, this.game);
}
},
/**
* @method Phaser.StateManager#resume
* @protected
*/
resume: function () {
if (this._created && this.onResumedCallback)
{
this.onResumedCallback.call(this.callbackContext, this.game);
}
},
/**
* @method Phaser.StateManager#update
* @protected
*/
update: function () {
if (this._created && this.onUpdateCallback)
{
this.onUpdateCallback.call(this.callbackContext, this.game);
}
else
{
if (this.onLoadUpdateCallback)
{
this.onLoadUpdateCallback.call(this.callbackContext, this.game);
}
}
},
/**
* @method Phaser.StateManager#pauseUpdate
* @protected
*/
pauseUpdate: function () {
if (this._created && this.onPauseUpdateCallback)
{
this.onPauseUpdateCallback.call(this.callbackContext, this.game);
}
else
{
if (this.onLoadUpdateCallback)
{
this.onLoadUpdateCallback.call(this.callbackContext, this.game);
}
}
},
/**
* @method Phaser.StateManager#preRender
* @protected
*/
preRender: function () {
if (this.onPreRenderCallback)
{
this.onPreRenderCallback.call(this.callbackContext, this.game);
}
},
/**
* @method Phaser.StateManager#render
* @protected
*/
render: function () {
if (this._created && this.onRenderCallback)
{
if (this.game.renderType === Phaser.CANVAS)
{
this.game.context.save();
this.game.context.setTransform(1, 0, 0, 1, 0, 0);
}
this.onRenderCallback.call(this.callbackContext, this.game);
if (this.game.renderType === Phaser.CANVAS)
{
this.game.context.restore();
}
}
else
{
if (this.onLoadRenderCallback)
{
this.onLoadRenderCallback.call(this.callbackContext, this.game);
}
}
},
/**
* Removes all StateManager callback references to the State object, nulls the game reference and clears the States object.
* You don't recover from this without rebuilding the Phaser instance again.
* @method Phaser.StateManager#destroy
*/
destroy: function () {
this.callbackContext = null;
this.onInitCallback = null;
this.onShutDownCallback = null;
this.onPreloadCallback = null;
this.onLoadRenderCallback = null;
this.onLoadUpdateCallback = null;
this.onCreateCallback = null;
this.onUpdateCallback = null;
this.onRenderCallback = null;
this.onPausedCallback = null;
this.onResumedCallback = null;
this.onPauseUpdateCallback = null;
this.game = null;
this.states = {};
this._pendingState = null;
}
};
Phaser.StateManager.prototype.constructor = Phaser.StateManager;

266
src/core/World.js Normal file
View File

@@ -0,0 +1,266 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2014 Photon Storm Ltd.
* @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 Phaser.World
* @extends Phaser.Group
* @constructor
* @param {Phaser.Game} game - Reference to the current game instance.
*/
Phaser.World = function (game) {
Phaser.Group.call(this, game, null, '__world', false);
/**
* 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);
/**
* @property {Phaser.Camera} camera - Camera instance.
*/
this.camera = null;
};
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;
this.camera.scale = this.scale;
this.game.camera = this.camera;
this.game.stage.addChild(this);
};
/**
* Updates the size of this world. Note that this doesn't modify the world x/y coordinates, just the width and height.
*
* @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. Can never be smaller than the Game.width.
* @param {number} height - New height of the world. Can never be smaller than the Game.height.
*/
Phaser.World.prototype.setBounds = function (x, y, width, height) {
if (width < this.game.width)
{
width = this.game.width;
}
if (height < this.game.height)
{
height = this.game.height;
}
this.bounds.setTo(x, y, width, height);
if (this.camera.bounds)
{
// The Camera can never be smaller than the game size
this.camera.bounds.setTo(x, y, width, height);
}
this.game.physics.setBoundsToWorld();
};
/**
* Destroyer of worlds.
*
* @method Phaser.World#shutdown
*/
Phaser.World.prototype.shutdown = function () {
// World is a Group, so run a soft destruction on this and all children.
this.destroy(true, true);
};
/**
* This will take the given game object and check if its x/y coordinates fall outside of the world bounds.
* If they do it will reposition the object to the opposite side of the world, creating a wrap-around effect.
*
* @method Phaser.World#wrap
* @param {Phaser.Sprite|Phaser.Image|Phaser.TileSprite|Phaser.Text} sprite - The object you wish to wrap around the world bounds.
* @param {number} [padding=0] - Extra padding added equally to the sprite.x and y coordinates before checking if within the world bounds. Ignored if useBounds is true.
* @param {boolean} [useBounds=false] - If useBounds is false wrap checks the object.x/y coordinates. If true it does a more accurate bounds check, which is more expensive.
*/
Phaser.World.prototype.wrap = function (sprite, padding, useBounds) {
if (typeof padding === 'undefined') { padding = 0; }
if (typeof useBounds === 'undefined') { useBounds = false; }
if (!useBounds)
{
if (sprite.x + padding < this.bounds.x)
{
sprite.x = this.bounds.right + padding;
}
else if (sprite.x - padding > this.bounds.right)
{
sprite.x = this.bounds.left - padding;
}
if (sprite.y + padding < this.bounds.top)
{
sprite.y = this.bounds.bottom + padding;
}
else if (sprite.y - padding > this.bounds.bottom)
{
sprite.y = this.bounds.top - padding;
}
}
else
{
sprite.getBounds();
if (sprite._currentBounds.right < this.bounds.x)
{
sprite.x = this.bounds.right;
}
else if (sprite._currentBounds.x > this.bounds.right)
{
sprite.x = this.bounds.left;
}
if (sprite._currentBounds.bottom < this.bounds.top)
{
sprite.y = this.bounds.bottom;
}
else if (sprite._currentBounds.top > this.bounds.bottom)
{
sprite.y = this.bounds.top;
}
}
};
/**
* @name Phaser.World#width
* @property {number} width - Gets or sets the current width of the game world.
*/
Object.defineProperty(Phaser.World.prototype, "width", {
get: function () {
return this.bounds.width;
},
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", {
get: function () {
return this.bounds.height;
},
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", {
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", {
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", {
get: function () {
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", {
get: function () {
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);
}
}
});