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]); },