diff --git a/examples/js.php b/examples/js.php
index 1fc6ee23..a5703add 100644
--- a/examples/js.php
+++ b/examples/js.php
@@ -51,6 +51,8 @@
+
+
@@ -64,6 +66,7 @@
+
diff --git a/examples/sprite1.php b/examples/sprite1.php
new file mode 100644
index 00000000..18b58c5f
--- /dev/null
+++ b/examples/sprite1.php
@@ -0,0 +1,43 @@
+
+
+
+ phaser.js - a new beginning
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/sprite2.php b/examples/sprite2.php
new file mode 100644
index 00000000..16e19c64
--- /dev/null
+++ b/examples/sprite2.php
@@ -0,0 +1,40 @@
+
+
+
+ phaser.js - a new beginning
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/animation/Animation.js b/src/animation/Animation.js
index 9a031144..3be0181a 100644
--- a/src/animation/Animation.js
+++ b/src/animation/Animation.js
@@ -1,3 +1,207 @@
-Phaser.Animation = {
+/**
+* Animation
+*
+* An Animation instance contains a single animation and the controls to play it.
+* It is created by the AnimationManager and belongs to Game Objects such as Sprite.
+*
+* @package Phaser.Animation
+* @author Richard Davey
+* @copyright 2013 Photon Storm Ltd.
+* @license https://github.com/photonstorm/phaser/blob/master/license.txt MIT License
+*
+* @param parent {Sprite} Owner sprite of this animation.
+* @param frameData {FrameData} The FrameData object contains animation data.
+* @param name {string} Unique name of this animation.
+* @param frames {number[]/string[]} An array of numbers or strings indicating what frames to play in what order.
+* @param delay {number} Time between frames in ms.
+* @param looped {bool} Whether or not the animation is looped or just plays once.
+*/
+Phaser.Animation = function (game, parent, frameData, name, frames, delay, looped) {
+
+ this.game = game;
+ this._parent = parent;
+ this._frames = frames;
+ this._frameData = frameData;
+ this.name = name;
+ this.delay = 1000 / delay;
+ this.looped = looped;
+ this.isFinished = false;
+ this.isPlaying = false;
+ this._frameIndex = 0;
+ this.currentFrame = this._frameData.getFrame(this._frames[this._frameIndex]);
-};
\ No newline at end of file
+};
+
+Phaser.Animation.prototype = {
+
+ /**
+ * Play this animation.
+ * @param frameRate {number} FrameRate you want to specify instead of using default.
+ * @param loop {bool} Whether or not the animation is looped or just plays once.
+ */
+ play: function (frameRate, loop) {
+
+ if (typeof frameRate === "undefined") { frameRate = null; }
+ if (typeof loop === "undefined") { loop = null; }
+
+ if (frameRate !== null)
+ {
+ this.delay = 1000 / frameRate;
+ }
+
+ if (loop !== null)
+ {
+ // If they set a new loop value then use it, otherwise use the default set on creation
+ this.looped = loop;
+ }
+
+ this.isPlaying = true;
+ this.isFinished = false;
+
+ this._timeLastFrame = this.game.time.now;
+ this._timeNextFrame = this.game.time.now + this.delay;
+
+ this._frameIndex = 0;
+
+ this.currentFrame = this._frameData.getFrame(this._frames[this._frameIndex]);
+ this._parent.setTexture(PIXI.TextureCache[this.currentFrame.uuid]);
+ // this._parent.events.onAnimationStart.dispatch(this._parent, this);
+
+ return this;
+
+ },
+
+ /**
+ * Play this animation from the first frame.
+ */
+ restart: function () {
+
+ this.isPlaying = true;
+ this.isFinished = false;
+
+ this._timeLastFrame = this.game.time.now;
+ this._timeNextFrame = this.game.time.now + this.delay;
+
+ this._frameIndex = 0;
+
+ this.currentFrame = this._frameData.getFrame(this._frames[this._frameIndex]);
+
+ },
+
+ /**
+ * Stop playing animation and set it finished.
+ */
+ stop: function () {
+
+ this.isPlaying = false;
+ this.isFinished = true;
+
+ },
+
+ /**
+ * Update animation frames.
+ */
+ update: function () {
+
+ if (this.isPlaying == true && this.game.time.now >= this._timeNextFrame)
+ {
+ this._frameIndex++;
+
+ if (this._frameIndex == this._frames.length)
+ {
+ if (this.looped)
+ {
+ this._frameIndex = 0;
+ this.currentFrame = this._frameData.getFrame(this._frames[this._frameIndex]);
+ // this._parent.events.onAnimationLoop.dispatch(this._parent, this);
+ }
+ else
+ {
+ this.onComplete();
+ }
+ }
+ else
+ {
+ this.currentFrame = this._frameData.getFrame(this._frames[this._frameIndex]);
+ this._parent.setTexture(PIXI.TextureCache[this.currentFrame.uuid]);
+ }
+
+ this._timeLastFrame = this.game.time.now;
+ this._timeNextFrame = this.game.time.now + this.delay;
+
+ return true;
+ }
+
+ return false;
+
+ },
+
+ /**
+ * Clean up animation memory.
+ */
+ destroy: function () {
+
+ this.game = null;
+ this._parent = null;
+ this._frames = null;
+ this._frameData = null;
+ this.currentFrame = null;
+ this.isPlaying = false;
+
+ },
+
+ /**
+ * Animation complete callback method.
+ */
+ onComplete: function () {
+
+ this.isPlaying = false;
+ this.isFinished = true;
+ // this._parent.events.onAnimationComplete.dispatch(this._parent, this);
+
+ }
+
+};
+
+Object.defineProperty(Phaser.Animation.prototype, "frameTotal", {
+
+ get: function () {
+ return this._frames.length;
+ },
+
+ enumerable: true,
+ configurable: true
+
+});
+
+Object.defineProperty(Phaser.Animation.prototype, "frame", {
+
+ get: function () {
+
+ if (this.currentFrame !== null)
+ {
+ return this.currentFrame.index;
+ }
+ else
+ {
+ return this._frameIndex;
+ }
+
+ },
+
+ set: function (value) {
+
+ this.currentFrame = this._frameData.getFrame(value);
+
+ if (this.currentFrame !== null)
+ {
+ this._frameIndex = value;
+ this._parent.setTexture(PIXI.TextureCache[this.currentFrame.uuid]);
+ }
+
+ },
+
+ enumerable: true,
+ configurable: true
+
+});
diff --git a/src/animation/AnimationManager.js b/src/animation/AnimationManager.js
new file mode 100644
index 00000000..ea02923d
--- /dev/null
+++ b/src/animation/AnimationManager.js
@@ -0,0 +1,278 @@
+/**
+* AnimationManager
+*
+* Any Game Object that supports animation contains a single AnimationManager instance. It is used to add,
+* play and update Phaser.Animation objects.
+*
+* @package Phaser.Components.AnimationManager
+* @author Richard Davey
+* @copyright 2013 Photon Storm Ltd.
+* @license https://github.com/photonstorm/phaser/blob/master/license.txt MIT License
+*/
+Phaser.AnimationManager = function (parent) {
+
+ /**
+ * Data contains animation frames.
+ * @type {FrameData}
+ */
+ this._frameData = null;
+
+ /**
+ * Keeps track of the current frame of animation.
+ */
+ this.currentFrame = null;
+
+ this._parent = parent;
+
+ this.game = parent.game;
+
+ this._anims = {};
+
+};
+
+Phaser.AnimationManager.prototype = {
+
+ /**
+ * Load animation frame data.
+ * @param frameData Data to be loaded.
+ */
+ loadFrameData: function (frameData) {
+
+ this._frameData = frameData;
+ this.frame = 0;
+
+ },
+
+ /**
+ * Add a new animation.
+ * @param name {string} What this animation should be called (e.g. "run").
+ * @param frames {any[]} An array of numbers/strings indicating what frames to play in what order (e.g. [1, 2, 3] or ['run0', 'run1', run2]).
+ * @param frameRate {number} The speed in frames per second that the animation should play at (e.g. 60 fps).
+ * @param loop {bool} Whether or not the animation is looped or just plays once.
+ * @param useNumericIndex {bool} Use number indexes instead of string indexes?
+ * @return {Animation} The Animation that was created
+ */
+ add: function (name, frames, frameRate, loop, useNumericIndex) {
+
+ if (typeof frames === "undefined") { frames = null; }
+ if (typeof frameRate === "undefined") { frameRate = 60; }
+ if (typeof loop === "undefined") { loop = false; }
+ if (typeof useNumericIndex === "undefined") { useNumericIndex = true; }
+
+ if (this._frameData == null)
+ {
+ return;
+ }
+
+ // Create the signals the AnimationManager will emit
+ if (this._parent.events.onAnimationStart == null)
+ {
+ // this._parent.events.onAnimationStart = new Phaser.Signal();
+ // this._parent.events.onAnimationComplete = new Phaser.Signal();
+ // this._parent.events.onAnimationLoop = new Phaser.Signal();
+ }
+
+ if (frames == null)
+ {
+ frames = this._frameData.getFrameIndexes();
+ }
+ else
+ {
+ if (this.validateFrames(frames, useNumericIndex) == false)
+ {
+ console.warn('Invalid frames given to Phaser.Animation ' + name);
+ return;
+ }
+ }
+
+ if (useNumericIndex == false)
+ {
+ frames = this._frameData.getFrameIndexesByName(frames);
+ }
+
+ this._anims[name] = new Phaser.Animation(this.game, this._parent, this._frameData, name, frames, frameRate, loop);
+ this.currentAnim = this._anims[name];
+ this.currentFrame = this.currentAnim.currentFrame;
+ this._parent.setTexture(PIXI.TextureCache[this.currentFrame.uuid]);
+
+ return this._anims[name];
+
+ },
+
+ /**
+ * Check whether the frames is valid.
+ * @param frames {any[]} Frames to be validated.
+ * @param useNumericIndex {bool} Does these frames use number indexes or string indexes?
+ * @return {bool} True if they're valid, otherwise return false.
+ */
+ validateFrames: function (frames, useNumericIndex) {
+
+ for (var i = 0; i < frames.length; i++)
+ {
+ if (useNumericIndex == true)
+ {
+ if (frames[i] > this._frameData.total)
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if (this._frameData.checkFrameName(frames[i]) == false)
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+
+ },
+
+ /**
+ * Play animation with specific name.
+ * @param name {string} The string name of the animation you want to play.
+ * @param frameRate {number} FrameRate you want to specify instead of using default.
+ * @param loop {bool} Whether or not the animation is looped or just plays once.
+ */
+ play: function (name, frameRate, loop) {
+
+ if (typeof frameRate === "undefined") { frameRate = null; }
+ if (typeof loop === "undefined") { loop = null; }
+
+ if (this._anims[name])
+ {
+ if (this.currentAnim == this._anims[name])
+ {
+ if (this.currentAnim.isPlaying == false)
+ {
+ return this.currentAnim.play(frameRate, loop);
+ }
+ }
+ else
+ {
+ this.currentAnim = this._anims[name];
+ return this.currentAnim.play(frameRate, loop);
+ }
+ }
+
+ },
+
+ /**
+ * Stop animation by name.
+ * Current animation will be automatically set to the stopped one.
+ */
+ stop: function (name) {
+
+ if (this._anims[name])
+ {
+ this.currentAnim = this._anims[name];
+ this.currentAnim.stop();
+ }
+
+ },
+
+ /**
+ * Update animation and parent sprite's bounds.
+ */
+ update: function () {
+
+ if (this.currentAnim && this.currentAnim.update() == true)
+ {
+ this.currentFrame = this.currentAnim.currentFrame;
+ }
+
+ },
+
+ /**
+ * Removes all related references
+ */
+ destroy: function () {
+
+ this._anims = {};
+ this._frameData = null;
+ this._frameIndex = 0;
+ this.currentAnim = null;
+ this.currentFrame = null;
+
+ }
+
+};
+
+Object.defineProperty(Phaser.AnimationManager.prototype, "frameData", {
+
+ get: function () {
+ return this._frameData;
+ },
+
+ enumerable: true,
+ configurable: true
+});
+
+Object.defineProperty(Phaser.AnimationManager.prototype, "frameTotal", {
+
+ get: function () {
+
+ if (this._frameData)
+ {
+ return this._frameData.total;
+ }
+ else
+ {
+ return -1;
+ }
+ },
+
+ enumerable: true,
+ configurable: true
+});
+
+Object.defineProperty(Phaser.AnimationManager.prototype, "frame", {
+
+ get: function () {
+ return this._frameIndex;
+ },
+
+ /**
+ *
+ * @param value
+ */
+ set: function (value) {
+
+ if (this._frameData && this._frameData.getFrame(value) !== null)
+ {
+ this.currentFrame = this._frameData.getFrame(value);
+ this._frameIndex = value;
+ console.log('AM set frame', value, this.currentFrame);
+ this._parent.setTexture(PIXI.TextureCache[this.currentFrame.uuid]);
+ }
+
+ },
+
+ enumerable: true,
+ configurable: true
+});
+
+Object.defineProperty(Phaser.AnimationManager.prototype, "frameName", {
+
+ get: function () {
+ return this.currentFrame.name;
+ },
+
+ set: function (value) {
+
+ if (this._frameData && this._frameData.getFrameByName(value))
+ {
+ this.currentFrame = this._frameData.getFrameByName(value);
+ this._frameIndex = this.currentFrame.index;
+ this._parent.setTexture(PIXI.TextureCache[this.currentFrame.uuid]);
+ }
+ else
+ {
+ console.warn("Cannot set frameName: " + value);
+ }
+ },
+
+ enumerable: true,
+ configurable: true
+});
diff --git a/src/core/Game.js b/src/core/Game.js
index 18ac68b8..e5d4c07b 100644
--- a/src/core/Game.js
+++ b/src/core/Game.js
@@ -264,7 +264,7 @@ Phaser.Game.prototype = {
this.world = new Phaser.World(this);
this.stage = new Phaser.Stage(this);
- // this.add = new Phaser.GameObjectFactory(this);
+ this.add = new Phaser.GameObjectFactory(this);
this.cache = new Phaser.Cache(this);
this.load = new Phaser.Loader(this);
this.time = new Phaser.Time(this);
diff --git a/src/gameobjects/GameObjectFactory.js b/src/gameobjects/GameObjectFactory.js
new file mode 100644
index 00000000..253c24ce
--- /dev/null
+++ b/src/gameobjects/GameObjectFactory.js
@@ -0,0 +1,31 @@
+Phaser.GameObjectFactory = function (game) {
+
+ this.game = game;
+ this._world = this.game.world;
+
+};
+
+Phaser.GameObjectFactory.prototype = {
+
+ _world: null,
+ game: null,
+
+ /**
+ * Create a new Sprite with specific position and sprite sheet key.
+ *
+ * @param x {number} X position of the new sprite.
+ * @param y {number} Y position of the new sprite.
+ * @param [key] {string} The image key as defined in the Game.Cache to use as the texture for this sprite
+ * @param [frame] {string|number} If the sprite uses an image from a texture atlas or sprite sheet you can pass the frame here. Either a number for a frame ID or a string for a frame name.
+ * @returns {Sprite} The newly created sprite object.
+ */
+ sprite: function (x, y, key, frame) {
+
+ if (typeof key === "undefined") { key = ''; }
+ if (typeof frame === "undefined") { frame = null; }
+
+ // return this._world.group.add(new Phaser.Sprite(this.game, x, y, key, frame));
+
+ },
+
+};
\ No newline at end of file
diff --git a/src/gameobjects/Sprite.js b/src/gameobjects/Sprite.js
new file mode 100644
index 00000000..738f20b2
--- /dev/null
+++ b/src/gameobjects/Sprite.js
@@ -0,0 +1,173 @@
+Phaser.Sprite = function (game, x, y, key, frame) {
+
+ if (typeof x === "undefined") { x = 0; }
+ if (typeof y === "undefined") { y = 0; }
+
+ // if null we ought to set to the phaser logo or something :)
+ if (typeof key === "undefined") { key = null; }
+ if (typeof frame === "undefined") { frame = null; }
+
+ this.game = game;
+
+ this.exists = true;
+ this.active = true;
+ this.visible = true;
+ this.alive = true;
+
+ this.group = null;
+
+ this.name = '';
+
+ // this.events = new Phaser.Components.Events(this);
+
+ /**
+ * This manages animations of the sprite. You can modify animations through it. (see AnimationManager)
+ * @type AnimationManager
+ */
+ this.animations = new Phaser.AnimationManager(this);
+
+ PIXI.DisplayObjectContainer.call(this);
+
+ /**
+ * The anchor sets the origin point of the texture.
+ * The default is 0,0 this means the textures origin is the top left
+ * Setting than anchor to 0.5,0.5 means the textures origin is centered
+ * Setting the anchor to 1,1 would mean the textures origin points will be the bottom right
+ *
+ * @property anchor
+ * @type Point
+ */
+ this.anchor = new PIXI.Point();
+
+ /**
+ * The texture that the sprite is using
+ *
+ * @property texture
+ * @type Texture
+ */
+ this.texture = PIXI.TextureCache[key];
+
+ if (frame !== null)
+ {
+ if (typeof frame == 'string')
+ {
+ this.frameName = frame;
+ }
+ else
+ {
+ this.frame = frame;
+ }
+ }
+
+ /**
+ * The blend mode of sprite.
+ * currently supports PIXI.blendModes.NORMAL and PIXI.blendModes.SCREEN
+ *
+ * @property blendMode
+ * @type Number
+ */
+ this.blendMode = PIXI.blendModes.NORMAL;
+
+ /**
+ * The width of the sprite (this is initially set by the texture)
+ *
+ * @property _width
+ * @type Number
+ * @private
+ */
+ this._width = 0;
+
+ /**
+ * The height of the sprite (this is initially set by the texture)
+ *
+ * @property _height
+ * @type Number
+ * @private
+ */
+ this._height = 0;
+
+ // if (texture.baseTexture.hasLoaded)
+ // {
+ this.updateFrame = true;
+ // }
+
+ this.renderable = true;
+
+ this.position.x = x;
+ this.position.y = y;
+
+};
+
+Phaser.Sprite.prototype = Object.create(PIXI.Sprite.prototype);
+Phaser.Sprite.prototype.constructor = Phaser.Sprite;
+
+/**
+ * Automatically called after update() by the game loop for all 'alive' objects.
+ */
+Phaser.Sprite.prototype.postUpdate = function() {
+
+ this.animations.update();
+ // this.checkBounds();
+
+}
+
+Object.defineProperty(Phaser.Sprite.prototype, 'x', {
+
+ get: function() {
+ return this.position.x;
+ },
+
+ set: function(value) {
+ this.position.x = value;
+ }
+
+});
+
+Object.defineProperty(Phaser.Sprite.prototype, 'y', {
+
+ get: function() {
+ return this.position.y;
+ },
+
+ set: function(value) {
+ this.position.y = value;
+ }
+
+});
+
+Object.defineProperty(Phaser.Sprite.prototype, "frame", {
+
+ /**
+ * Get the animation frame number.
+ */
+ get: function () {
+ return this.animations.frame;
+ },
+
+ /**
+ * Set the animation frame by frame number.
+ */
+ set: function (value) {
+ this.animations.frame = value;
+ }
+
+});
+
+Object.defineProperty(Phaser.Sprite.prototype, "frameName", {
+
+ /**
+ * Get the animation frame name.
+ */
+ get: function () {
+ return this.animations.frameName;
+ },
+
+ /**
+ * Set the animation frame by frame name.
+ */
+ set: function (value) {
+ this.animations.frameName = value;
+ }
+
+});
+
diff --git a/src/loader/Cache.js b/src/loader/Cache.js
index 684444f9..6e6d2d6d 100644
--- a/src/loader/Cache.js
+++ b/src/loader/Cache.js
@@ -71,6 +71,7 @@ Phaser.Cache.prototype = {
this._images[key] = { url: url, data: data, spriteSheet: true, frameWidth: frameWidth, frameHeight: frameHeight };
PIXI.BaseTextureCache[key] = new PIXI.BaseTexture(data);
+ PIXI.TextureCache[key] = new PIXI.Texture(PIXI.BaseTextureCache[key]);
this._images[key].frameData = Phaser.Animation.Parser.spriteSheet(this.game, key, frameWidth, frameHeight, frameMax);
@@ -88,6 +89,7 @@ Phaser.Cache.prototype = {
this._images[key] = { url: url, data: data, spriteSheet: true };
PIXI.BaseTextureCache[key] = new PIXI.BaseTexture(data);
+ PIXI.TextureCache[key] = new PIXI.Texture(PIXI.BaseTextureCache[key]);
if (format == Phaser.Loader.TEXTURE_ATLAS_JSON_ARRAY)
{
@@ -115,6 +117,7 @@ Phaser.Cache.prototype = {
this._images[key] = { url: url, data: data, spriteSheet: false };
PIXI.BaseTextureCache[key] = new PIXI.BaseTexture(data);
+ PIXI.TextureCache[key] = new PIXI.Texture(PIXI.BaseTextureCache[key]);
},