/**
* Animation for Sprites.
* @constructor FORGE.SpriteAnimation
* @param {FORGE.Sprite} sprite - The Sprite that is animated.
* @param {string} name - Name of the animation.
* @param {Array} frames - The frames taht compose this animation.
* @param {number=} [frameRate=30] - Frame rate of this animation (default: 30)
* @param {boolean=} [loop=true] - The loop flag
* @extends {FORGE.BaseObject}
*
* @todo Work on trimmed textures
*/
FORGE.SpriteAnimation = function(sprite, name, frames, frameRate, loop)
{
/**
* The sprite attached to this animation.
* @name FORGE.SpriteAnimation#_sprite
* @type {FORGE.Sprite}
* @private
*/
this._sprite = sprite;
/**
* Name of the animation.
* @name FORGE.SpriteAnimation#_name
* @type {string}
* @private
*/
this._name = name;
/**
* Array of frames that compose this animation
* @name FORGE.SpriteAnimation#_frames
* @type {Array<Object>}
* @private
*/
this._frames = frames || [];
/**
* The index of the current frame.
* @name FORGE.SpriteAnimation#_frameIndex
* @type {number}
* @private
*/
this._frameIndex = 0;
/**
* Frame rate of this animation, this is the number of frames per second.
* @name FORGE.SpriteAnimation#_frameRate
* @type {number}
* @private
*/
this._frameRate = frameRate || 30;
/**
* Does this animation loop?
* @name FORGE.SpriteAnimation#_loop
* @type {boolean}
* @private
*/
this._loop = loop || false;
/**
* The number of times this animation has looped.
* @name FORGE.SpriteAnimation#_loopCount
* @type {number}
* @private
*/
this._loopCount = 0;
/**
* The delay in milliseconds between two frames.
* @name FORGE.SpriteAnimation#_delay
* @type {number}
* @private
*/
this._delay = 1000 / frameRate;
/**
* The time of the last frame.
* @name FORGE.SpriteAnimation#_timeLastFrame
* @type {number}
* @private
*/
this._timeLastFrame = 0;
/**
* The time of the next frame.
* @name FORGE.SpriteAnimation#_timeNextFrame
* @type {number}
* @private
*/
this._timeNextFrame = 0;
/**
* The time between the current time and the next frame.
* @name FORGE.SpriteAnimation#_frameDiff
* @type {number}
* @private
*/
this._frameDiff = 0;
/**
* The number of frames to skip when laag occurs.
* @name FORGE.SpriteAnimation#_frameSkip
* @type {number}
* @private
*/
this._frameSkip = 1;
/**
* Is this animation is currently playing?
* @name FORGE.SpriteAnimation#_playing
* @type {boolean}
* @private
*/
this._playing = false;
/**
* Is this animation is currently paused?
* @name FORGE.SpriteAnimation#_paused
* @type {boolean}
* @private
*/
this._paused = false;
/**
* Is this animation is complete?
* @name FORGE.SpriteAnimation#_complete
* @type {boolean}
* @private
*/
this._complete = false;
/**
* On play event dispatcher.
* @name FORGE.SpriteAnimation#_onPlay
* @type {FORGE.EventDispatcher}
* @private
*/
this._onPlay = null;
/**
* On pause event dispatcher.
* @name FORGE.SpriteAnimation#_onPause
* @type {FORGE.EventDispatcher}
* @private
*/
this._onPause = null;
/**
* On resume event dispatcher.
* @name FORGE.SpriteAnimation#_onResume
* @type {FORGE.EventDispatcher}
* @private
*/
this._onResume = null;
/**
* On loop event dispatcher.
* @name FORGE.SpriteAnimation#_onLoop
* @type {FORGE.EventDispatcher}
* @private
*/
this._onLoop = null;
/**
* On stop event dispatcher.
* @name FORGE.SpriteAnimation#_onStop
* @type {FORGE.EventDispatcher}
* @private
*/
this._onStop = null;
/**
* On complete event dispatcher.
* @name FORGE.SpriteAnimation#_onComplete
* @type {FORGE.EventDispatcher}
* @private
*/
this._onComplete = null;
FORGE.BaseObject.call(this, "SpriteAnimation");
};
FORGE.SpriteAnimation.prototype = Object.create(FORGE.BaseObject.prototype);
FORGE.SpriteAnimation.prototype.constructor = FORGE.SpriteAnimation;
/**
* Internal method to notify when animation is complete.
* @method FORGE.SpriteAnimation#_notifyComplete
* @private
*/
FORGE.SpriteAnimation.prototype._notifyComplete = function()
{
this._setFrameIndex(this._frames.length - 1);
this._playing = false;
this._complete = true;
this._paused = false;
if(this._onComplete !== null)
{
this._onComplete.dispatch();
}
};
/**
* Internal method to set the frame index of the animation and update the sprite display.
* @method FORGE.SpriteAnimation#_setFrameIndex
* @private
* @param {number=} [index=0] - The frame index to set.
*/
FORGE.SpriteAnimation.prototype._setFrameIndex = function(index)
{
this._frameIndex = (typeof index === "number" && index >= 0 && index < this._frames.length) ? index : 0;
this._sprite.frame = this._frames[this._frameIndex].frame;
};
/**
* Play this animation.
* @method FORGE.SpriteAnimation#play
* @param {boolean=} loop - Does the animation have to loop on complete?
* @param {number=} index - the index to play at
*/
FORGE.SpriteAnimation.prototype.play = function(loop, index)
{
if(typeof loop === "boolean")
{
this._loop = loop;
}
this._playing = true;
this._complete = false;
this._paused = false;
this._loopCount = 0;
var time = this._sprite.viewer.clock.time;
this._timeLastFrame = time;
this._timeNextFrame = time + this._delay;
this._setFrameIndex(index); //Default will be 0 :)
this._sprite.animations.current = this;
if(this._onPlay !== null)
{
this._onPlay.dispatch();
}
};
/**
* Pause the animation.
* @method FORGE.SpriteAnimation#pause
* @param {number} [index] - The frame index on which to pause the animation. Default will be the current frame.
*/
FORGE.SpriteAnimation.prototype.pause = function(index)
{
if(this._paused === true)
{
return;
}
this._paused = true;
if(typeof index === "number")
{
this._setFrameIndex(index);
}
if(this._onPause !== null)
{
this._onPause.dispatch();
}
};
/**
* Resume the animation
* @method FORGE.SpriteAnimation#resume
* @param {number} [index] - The frame index on which to resume the animation. Default will be the current frame.
*/
FORGE.SpriteAnimation.prototype.resume = function(index)
{
if(this._paused === false)
{
return;
}
this._paused = false;
if(typeof index === "number")
{
this._setFrameIndex(index);
}
if(this._playing)
{
this._timeNextFrame = this._sprite.viewer.clock.time + this._delay;
}
if(this._onResume !== null)
{
this._onResume.dispatch();
}
};
/**
* Stops the animation, reset it to the first frame.
* @method FORGE.SpriteAnimation#stop
*/
FORGE.SpriteAnimation.prototype.stop = function()
{
this._playing = false;
this._complete = true;
this._paused = false;
this._setFrameIndex(0);
if(this._onStop !== null)
{
this._onStop.dispatch();
}
};
/**
* Update loop that will be called by the DisplayList through Sprite & SpriteAnimationManager update
* @method FORGE.SpriteAnimation#update
*/
FORGE.SpriteAnimation.prototype.update = function()
{
if (this._paused === true)
{
return;
}
var time = this._sprite.viewer.clock.time;
if (this._playing === true && time >= this._timeNextFrame)
{
this._frameSkip = 1;
this._frameDiff = time - this._timeNextFrame;
this._timeLastFrame = time;
if (this._frameDiff > this._delay)
{
this._frameSkip = Math.floor(this._frameDiff / this._delay);
this._frameDiff -= (this._frameSkip * this._delay);
}
this._timeNextFrame = time + (this._delay - this._frameDiff);
var frameIndex = this._frameIndex + this._frameSkip;
if (frameIndex >= this._frames.length)
{
if (this._loop === true)
{
frameIndex %= this._frames.length;
this._loopCount++;
if(this._onLoop !== null)
{
this._onLoop.dispatch();
}
this._setFrameIndex(frameIndex);
}
else
{
this._notifyComplete();
}
}
else
{
this._setFrameIndex(frameIndex);
}
}
};
/**
* Destroy method
* @method FORGE.SpriteAnimation#destroy
*/
FORGE.SpriteAnimation.prototype.destroy = function()
{
this._sprite = null;
this._frames = null;
if(this._onPlay !== null)
{
this._onPlay.destroy();
this._onPlay = null;
}
if(this._onPause !== null)
{
this._onPause.destroy();
this._onPause = null;
}
if(this._onResume !== null)
{
this._onResume.destroy();
this._onResume = null;
}
if(this._onComplete !== null)
{
this._onComplete.destroy();
this._onComplete = null;
}
if(this._onLoop !== null)
{
this._onLoop.destroy();
this._onLoop = null;
}
if(this._onStop !== null)
{
this._onStop.destroy();
this._onStop = null;
}
FORGE.BaseObject.prototype.destroy.call(this);
};
/**
* Get the name of the SpriteAnimation.
* @name FORGE.SpriteAnimation#name
* @readonly
* @type {string}
*/
Object.defineProperty(FORGE.SpriteAnimation.prototype, "name",
{
/** @this {FORGE.SpriteAnimation} */
get: function()
{
return this._name;
}
});
/**
* Get the frames array of the SpriteAnimation.
* @name FORGE.SpriteAnimation#frames
* @readonly
* @type {Array<Object>}
*/
Object.defineProperty(FORGE.SpriteAnimation.prototype, "frames",
{
/** @this {FORGE.SpriteAnimation} */
get: function()
{
return this._frames;
}
});
/**
* Get the current frame index of the SpriteAnimation.
* @name FORGE.SpriteAnimation#frameIndex
* @readonly
* @type {number}
*/
Object.defineProperty(FORGE.SpriteAnimation.prototype, "frameIndex",
{
/** @this {FORGE.SpriteAnimation} */
get: function()
{
return this._frameIndex;
}
});
/**
* Get the frame rate of the SpriteAnimation.
* @name FORGE.SpriteAnimation#frameRate
* @readonly
* @type {string}
*/
Object.defineProperty(FORGE.SpriteAnimation.prototype, "frameRate",
{
/** @this {FORGE.SpriteAnimation} */
get: function()
{
return this._frameRate;
},
/** @this {FORGE.SpriteAnimation} */
set: function(value)
{
if(typeof value !== "number")
{
return;
}
this._frameRate = value;
this._delay = 1000 / this._frameRate;
}
});
/**
* Get and set the loop flag of the SpriteAnimation.
* @name FORGE.SpriteAnimation#loop
* @type {boolean}
*/
Object.defineProperty(FORGE.SpriteAnimation.prototype, "loop",
{
/** @this {FORGE.SpriteAnimation} */
get: function()
{
return this._loop;
},
/** @this {FORGE.SpriteAnimation} */
set: function(value)
{
this._loop = Boolean(value);
}
});
/**
* Get the loop count of the SpriteAnimation.
* @name FORGE.SpriteAnimation#loopCount
* @readonly
* @type {boolean}
*/
Object.defineProperty(FORGE.SpriteAnimation.prototype, "loopCount",
{
/** @this {FORGE.SpriteAnimation} */
get: function()
{
return this._loopCount;
}
});
/**
* Get the playing status of the SpriteAnimation.
* @name FORGE.SpriteAnimation#playing
* @readonly
* @type {boolean}
*/
Object.defineProperty(FORGE.SpriteAnimation.prototype, "playing",
{
/** @this {FORGE.SpriteAnimation} */
get: function()
{
return this._playing;
}
});
/**
* Get the paused status of the SpriteAnimation.
* @name FORGE.SpriteAnimation#paused
* @type {boolean}
*/
Object.defineProperty(FORGE.SpriteAnimation.prototype, "paused",
{
/** @this {FORGE.SpriteAnimation} */
get: function()
{
return this._paused;
},
/** @this {FORGE.SpriteAnimation} */
set: function(value)
{
if(Boolean(value) === true)
{
this.pause();
}
else
{
this.resume();
}
}
});
/**
* Get the complete status of the SpriteAnimation.
* @name FORGE.SpriteAnimation#complete
* @readonly
* @type {boolean}
*/
Object.defineProperty(FORGE.SpriteAnimation.prototype, "complete",
{
/** @this {FORGE.SpriteAnimation} */
get: function()
{
return this._complete;
}
});
/**
* Get the onPlay {@link FORGE.EventDispatcher}.
* @name FORGE.SpriteAnimation#onPlay
* @readonly
* @type {FORGE.EventDispatcher}
*/
Object.defineProperty(FORGE.SpriteAnimation.prototype, "onPlay",
{
/** @this {FORGE.SpriteAnimation} */
get: function()
{
if(this._onPlay === null)
{
this._onPlay = new FORGE.EventDispatcher(this);
}
return this._onPlay;
}
});
/**
* Get the onPause {@link FORGE.EventDispatcher}.
* @name FORGE.SpriteAnimation#onPause
* @readonly
* @type {FORGE.EventDispatcher}
*/
Object.defineProperty(FORGE.SpriteAnimation.prototype, "onPause",
{
/** @this {FORGE.SpriteAnimation} */
get: function()
{
if(this._onPause === null)
{
this._onPause = new FORGE.EventDispatcher(this);
}
return this._onPause;
}
});
/**
* Get the onResume {@link FORGE.EventDispatcher}.
* @name FORGE.SpriteAnimation#onResume
* @readonly
* @type {FORGE.EventDispatcher}
*/
Object.defineProperty(FORGE.SpriteAnimation.prototype, "onResume",
{
/** @this {FORGE.SpriteAnimation} */
get: function()
{
if(this._onResume === null)
{
this._onResume = new FORGE.EventDispatcher(this);
}
return this._onResume;
}
});
/**
* Get the onLoop {@link FORGE.EventDispatcher}.
* @name FORGE.SpriteAnimation#onLoop
* @readonly
* @type {FORGE.EventDispatcher}
*/
Object.defineProperty(FORGE.SpriteAnimation.prototype, "onLoop",
{
/** @this {FORGE.SpriteAnimation} */
get: function()
{
if(this._onLoop === null)
{
this._onLoop = new FORGE.EventDispatcher(this);
}
return this._onLoop;
}
});
/**
* Get the onComplete {@link FORGE.EventDispatcher}.
* @name FORGE.SpriteAnimation#onComplete
* @readonly
* @type {FORGE.EventDispatcher}
*/
Object.defineProperty(FORGE.SpriteAnimation.prototype, "onComplete",
{
/** @this {FORGE.SpriteAnimation} */
get: function()
{
if(this._onComplete === null)
{
this._onComplete = new FORGE.EventDispatcher(this);
}
return this._onComplete;
}
});
/**
* Get the onStop {@link FORGE.EventDispatcher}.
* @name FORGE.SpriteAnimation#onStop
* @readonly
* @type {FORGE.EventDispatcher}
*/
Object.defineProperty(FORGE.SpriteAnimation.prototype, "onStop",
{
/** @this {FORGE.SpriteAnimation} */
get: function()
{
if(this._onStop === null)
{
this._onStop = new FORGE.EventDispatcher(this);
}
return this._onStop;
}
});