File: src\animations\Animation.ts
/**
* Is the namespace in which all code that is used to create/provide an animation of various sorts are stored. These could range from animations that change the cell of a SpriteSheet that is displayed every few seconds (Animation/Sequence), to animations that change a numeric value on a object over a period time (Tweens).
*
* @module Kiwi
* @submodule Animations
* @main Animations
*/
module Kiwi.Animations {
/**
* An Animation contains information about a single animation that is held on a AnimationManager.
* The information that is held is unique to this individual animation and will initially be the same as a Sequence,
* but if you do ever modify the information held in this Animation the corresponding Sequence will not be updated.
*
* @class Animation
* @namespace Kiwi.Animations
* @constructor
* @param name {string} The name of this anim.
* @param sequences {Kiwi.Animations.Sequences} The sequence that this anim will be using to animate.
* @param clock {Kiwi.Time.Clock} A game clock that this anim will be using to keep record of the time between frames. (Deprecated in v1.2.0, because there is no way to control it.)
* @param parent {Kiwi.Components.AnimationManager} The animation manager that this animation belongs to.
* @return {Kiwi.Animations.Animation}
*
*/
export class Animation {
constructor(name: string, sequence: Kiwi.Animations.Sequence, clock: Kiwi.Time.Clock, parent: Kiwi.Components.AnimationManager) {
this.name = name;
this._sequence = sequence;
this._speed = sequence.speed;
this._loop = sequence.loop;
this._parent = parent;
this._clock = clock;
this._lastFrameElapsed = this.clock.elapsed();
}
/**
* The type of object that this is.
* @method objType
* @return {String} "Animation"
* @public
*/
public objType(): string {
return "Animation";
}
/**
* The AnimationManager that this animation is a child of.
* @property _parent
* @type Kiwi.Components.AnimationManager
* @private
*/
private _parent: Kiwi.Components.AnimationManager;
/**
* The name of this animation.
* @property name
* @type string
* @public
*/
public name: string;
/**
* The sequence on the texture atlas that this animation is based off.
* @property _sequence
* @type Kiwi.Animations.Sequence
* @private
*/
private _sequence: Kiwi.Animations.Sequence;
/**
* If this animation should loop or not.
* @property _loop
* @type boolean
* @private
*/
private _loop: boolean;
/**
* If once the animation reaches the end, it should start again from the first cell in the sequence or not.
* @property loop
* @type boolean
* @public
*/
public get loop(): boolean {
return this._loop;
}
public set loop(value: boolean) {
this._loop = value;
}
/**
* The current frame index that the animation is currently upto.
* Note: A frame index is the index of a particular cell in the Sequence.
* @property _frameIndex
* @type number
* @private
*/
private _frameIndex: number = 0;
/**
* The current frame index that the animation is currently upto.
* Note: A frame index is the index of a particular cell in the Sequence.
*
* As of v1.3.0, this property will work properly with floating-point
* values. They will be rounded down and stored as integers.
* @property frameIndex
* @type number
* @public
*/
public get frameIndex(): number {
return this._frameIndex;
}
public set frameIndex(val: number) {
val = Math.floor( val );
if (this._validateFrame(val)) {
this._frameIndex = val;
}
}
/**
* Returns the current cell that the animation is up to. This is READ ONLY.
* @property currentCell
* @type number
* @public
*/
public get currentCell(): number {
return this._sequence.cells[ this.frameIndex ];
}
/**
* How fast the transition is between cells. Perhaps change to frames per second to reflect actual game speed?
* @property _speed
* @type number
* @private
*/
private _speed: number;
/**
* How long the each cell should stay on screen for. In seconds.
* @property speed
* @type number
* @public
*/
public get speed(): number {
return this._speed;
}
public set speed(value: number) {
this._speed = value;
}
/**
* The clock that is to be used to calculate the animations.
* @property _clock
* @type Kiwi.Time.Clock
* @private
*/
private _clock: Kiwi.Time.Clock;
/**
* Clock used by this Animation. If it was not set on creation,
* the Animation will use its parent's entity's clock.
* @property clock
* @type Kiwi.Time.Clock
* @public
* @since 1.2.0
*/
public get clock(): Kiwi.Time.Clock {
if ( this._clock ) {
return this._clock;
}
return this._parent.entity.clock;
}
/**
* The starting time of the animation from when it was played. Internal use only.
* @property _startTime
* @type number
* @private
*/
private _startTime: number = null;
/**
* Indicates whether the animation is playing in reverse or not.
* @property _reverse
* @type boolean
* @private
*/
private _reverse: boolean = false;
/**
* Whether the animation is to be played in reverse.
* @property reverse
* @type boolean
* @public
*/
public set reverse(value: boolean) {
this._reverse = value;
}
public get reverse(): boolean {
return this._reverse;
}
/**
* The time at which the animation should change to the next cell
* @property _tick
* @type number
* @private
* @deprecated Different private time management systems implemented in v1.2.0
*/
private _tick: number;
/**
* Whether the animation is currently playing or not.
* @property _isPlaying
* @type boolean
* @default false
* @private
*/
private _isPlaying: boolean = false;
/**
* Whether the animation is currently playing or not. Read-only.
* @property isPlaying
* @type boolean
* @public
*/
public get isPlaying(): boolean {
return this._isPlaying;
}
/**
* A Kiwi.Signal that dispatches an event when the animation has stopped playing.
* @property _onStop
* @type Signal
* @private
*/
private _onStop: Kiwi.Signal = null;
/**
* A Kiwi.Signal that dispatches an event when the animation has stopped playing.
* @property onStop
* @type Signal
* @public
*/
public get onStop(): Kiwi.Signal {
if (this._onStop == null) this._onStop = new Kiwi.Signal;
return this._onStop;
}
/**
* A Kiwi.Signal that dispatches an event when the animation has started playing.
* @property _onPlay
* @type Kiwi.Signal
* @private
*/
private _onPlay: Kiwi.Signal = null;
/**
* A Kiwi.Signal that dispatches an event when the animation has started playing.
* @property onPlay
* @type Kiwi.Signal
* @public
*/
public get onPlay(): Kiwi.Signal {
if (this._onPlay == null) this._onPlay = new Kiwi.Signal;
return this._onPlay;
}
/**
* A Kiwi.Signal that dispatches an event when the animation has updated/changed frameIndexs.
* @property _onUpdate
* @type Kiwi.Signal
* @private
*/
private _onUpdate: Kiwi.Signal = null;
/**
* A Kiwi.Signal that dispatches an event when the animation has updated/changed frameIndexs.
* @property onUpdate
* @type Kiwi.Signal
* @public
*/
public get onUpdate(): Kiwi.Signal {
if (this._onUpdate == null) this._onUpdate = new Kiwi.Signal;
return this._onUpdate;
}
/**
* A Kiwi.Signal that dispatches an event when the animation has come to the end of the animation and is going to play again.
* @property _onLoop
* @type Kiwi.Signal
* @private
*/
private _onLoop: Kiwi.Signal = null;
/**
* A Kiwi.Signal that dispatches an event when the animation has come to the end of the animation and is going to play again.
* @property onLoop
* @type Kiwi.Signal
* @public
*/
public get onLoop(): Kiwi.Signal {
if (this._onLoop == null) this._onLoop = new Kiwi.Signal;
return this._onLoop;
}
/**
* A Kiwi.Signal that dispatches an event when the animation has come to
* the end of the animation but is not going to play again.
* @property _onComplete
* @type Kiwi.Signal
* @private
* @since 1.2.0
*/
private _onComplete: Kiwi.Signal = null;
/**
* A Kiwi.Signal that dispatches an event when the animation has come to
* the end of the animation but is not going to play again.
* @property onComplete
* @type Kiwi.Signal
* @public
* @since 1.2.0
*/
public get onComplete(): Kiwi.Signal {
if (this._onComplete == null) this._onComplete = new Kiwi.Signal;
return this._onComplete;
}
/**
* Clock time on last frame, used to compute current animation frame.
* @property _lastFrameElapsed
* @type number
* @private
* @since 1.2.0
*/
private _lastFrameElapsed: number;
/**
* Start the animation.
* @method _start
* @param [index=null] {number} Index of the frame in the sequence that
* is to play. If left as null it just starts from where it left off.
* @private
*/
private _start(index: number = null) {
if (index !== null) {
this.frameIndex = index;
}
// If the animation is out of range then start it at the beginning
if ( this.frameIndex >= this.length - 1 || this.frameIndex < 0 ) {
this.frameIndex = 0;
}
this._isPlaying = true;
this._startTime = this.clock.elapsed();
this._lastFrameElapsed = this.clock.elapsed();
if ( this._onPlay !== null ) {
this._onPlay.dispatch();
}
}
/**
* Plays the animation.
* @method play
* @public
*/
public play() {
this.playAt( this._frameIndex );
}
/**
* Plays the animation at a particular frame.
* @method playAt
* @param index {number} Index of the cell in the sequence that the
* animation is to start at.
* @public
*/
public playAt( index: number ) {
this._start( index );
}
/**
* Pauses the current animation.
* @method pause
* @public
*/
public pause() {
this.stop();
}
/**
* Resumes the current animation after stopping.
* @method resume
* @public
*/
public resume() {
if (this._startTime !== null) {
this._isPlaying = true;
}
}
/**
* Stops the current animation from playing.
* @method stop
* @public
*/
public stop() {
if (this._isPlaying) {
this._isPlaying = false;
if(this._onStop !== null) this._onStop.dispatch();
}
}
/**
* Makes the animation go to the next frame. If the animation is at the end it goes back to the start.
* @method nextFrame
* @public
*/
public nextFrame() {
this._frameIndex++;
if (this._frameIndex >= this.length) this.frameIndex = 0;
}
/**
* Makes the animation go to the previous frame. If the animation is at the first frame it goes to the end.
* @method prevFrame
* @public
*/
public prevFrame() {
this._frameIndex--;
if (this._frameIndex < 0) this.frameIndex = this.length - 1;
}
/**
* The update loop.
*
* @method update
* @public
*/
public update() {
var frameDelta, i, repeats;
if ( this._isPlaying ) {
// How many frames do we move, ahead or behind?
frameDelta = ( ( this.clock.elapsed() -
this._lastFrameElapsed ) / this._speed ) % ( this.length + 1 );
if ( this._reverse ) {
frameDelta *= -1;
}
// Round delta, towards zero
if ( frameDelta > 0 ) {
frameDelta = Math.floor( frameDelta );
} else {
frameDelta = Math.ceil( frameDelta );
}
if ( frameDelta !== 0 ) {
this._frameIndex += frameDelta;
this._lastFrameElapsed = this.clock.elapsed();
// Loop check
if ( this._loop ) {
if ( this._frameIndex >= this.length ) {
repeats = Math.floor(
this._frameIndex / this.length );
this._frameIndex = this._frameIndex % this.length;
if ( this._onLoop != null ) {
for ( i = 0; i < repeats; i++ ) {
this._onLoop.dispatch();
}
}
} else if ( this._frameIndex < 0 ) {
repeats = Math.ceil(
Math.abs( this._frameIndex ) / this.length );
this._frameIndex = ( this.length +
this._frameIndex % this.length ) % this.length;
if ( this._onLoop != null ) {
for ( i = 0; i < repeats; i++ ) {
this._onLoop.dispatch();
}
}
}
} else if ( this._frameIndex < 0 ) {
this._frameIndex = ( this.length +
this._frameIndex % this.length ) % this.length;
// Execute the stop on the parent
// to allow the isPlaying boolean to remain consistent
this._parent.stop();
return;
} else if ( this._frameIndex >= this.length ) {
this._frameIndex = this._frameIndex % this.length;
// Execute the stop on the parent
// to allow the isPlaying boolean to remain consistent
this._parent.stop();
if ( this._onComplete != null ) {
this._onComplete.dispatch();
}
return;
}
this._parent.updateCellIndex();
if ( this._onUpdate !== null ) {
this._onUpdate.dispatch();
}
}
}
}
/**
* An internal method used to check to see if frame passed is valid or not
* @method _validateFrame
* @param frame {Number} The index of the frame that is to be validated.
* @private
*/
private _validateFrame(frame: number) {
return (frame < this.length && frame >= 0);
}
/**
* Returns the number of frames that in the animation. Thus the animations 'length'. Note this is READ ONLY.
* @property length
* @type number
* @public
*/
public get length():number {
return this._sequence.cells.length;
}
/**
* Destroys the anim and all of the properties that exist on it.
* @method destroy
* @public
*/
public destroy() {
this._isPlaying = false;
delete this._clock;
delete this._sequence;
delete this._parent;
if(this._onLoop) this._onLoop.dispose();
if(this._onStop) this._onStop.dispose();
if(this._onPlay) this._onPlay.dispose();
if(this._onUpdate) this._onUpdate.dispose();
delete this._onLoop;
delete this._onStop ;
delete this._onPlay ;
delete this._onUpdate ;
delete this.frameIndex ;
delete this.loop;
delete this._reverse;
delete this._tick;
}
}
}