File: src\time\Timer.ts
/**
*
* @module Kiwi
* @submodule Time
*
*/
module Kiwi.Time {
/**
* The Timer class hooks into a game Clock and allows you run code at a specified point in game time.
* Use the start() method to start a timer. Add TimerEvents to set-up code to be run on the timer interval.
* Timer objects can run once or repeat at specified intervals to execute code on a schedule.
*
* @class Timer
* @namespace Kiwi.Time
* @constructor
* @param name {string} The name of the timer.
* @param clock {Kiwi.Time.Clock} The game clock instance this Timer is based on.
* @param delay {Number} The number of clock units to wait between firing events.
* @param [repeatCount=0] {Number} The number of times to repeat the timer before it is expired. If you don't want it to ever expire, set a value of -1.
* @return {Kiwi.Time.Timer} This object.
*
*/
export class Timer {
constructor (name: string, clock: Clock, delay: number, repeatCount: number = 0) {
this._clock = clock;
this._startEvents = [];
this._countEvents = [];
this._stopEvents = [];
this.name = name;
this.delay = delay;
this.repeatCount = repeatCount;
}
/**
* The type of object that this is.
* @method objType
* @return {String} "Timer"
* @public
*/
public objType() {
return "Timer";
}
/**
* The number of times the timer has repeated so far.
* @property _currentCount
* @type Number
* @default 0
* @private
*/
private _currentCount: number = 0;
/**
* Get the number of times the timer has repeated.
* @method getCurrentCount
* @return {Number}
* @public
*/
public currentCount(): number {
return this._currentCount;
}
/**
* A collection of the TimerEvents associated with TimerEvent.TIMER_START
* @property _startEvents
* @type Array
* @private
*/
private _startEvents:TimerEvent[] = null;
/**
* A collection of the TimerEvents associated with TimerEvent.TIMER_COUNT
* @property _countEvents
* @private
* @type Array
*/
private _countEvents:TimerEvent[] = null;
/**
* A collection of the TimerEvents associated with TimerEvent.TIMER_STOP
* @property _stopEvents
* @private
* @type Array
*/
private _stopEvents:TimerEvent[] = null;
/**
* The clock which this timer bases its timing on.
* @property _clock
* @type Kiwi.Time.Clock
* @private
*/
private _clock: Clock = null;
/**
* The time the last repeat occurred in clock units.
* @property _timeLastCount
* @type Number
* @private
* @deprecated Better time handling in 1.2.0 deprecates this data.
*/
private _timeLastCount: number = null;
/**
* Whether the timer is in a running state.
* @property _isRunning
* @type boolean
* @default false
* @private
*/
private _isRunning: boolean = false;
/**
* The Timers current state. True if the Timer is running, otherwise false.
* @method running
* @return {boolean}
* @public
*/
public isRunning(): boolean {
return this._isRunning;
}
/**
* Whether the timer is in a stopped state.
* @property _isStopped
* @type boolean
* @default true
* @private
*/
private _isStopped: boolean = true;
/**
* Whether the timer is in a stopped state.
* @method stopped
* @return {boolean}
* @public
*/
public isStopped(): boolean {
return this._isStopped;
}
/**
* Whether the timer is in a paused state.
* @property _isPaused
* @type boolean
* @default false
* @private
*/
private _isPaused: boolean = false;
/**
* Whether the timer is in a paused state.
* @method paused
* @return {boolean}
* @public
*/
public isPaused(): boolean {
return this._isPaused;
}
/**
* The name of the timer.
* @property name
* @type String
* @default null
* @public
*/
public name: string = null;
/**
* The delay, in game clock units, that the timer will wait before firing the event
* @property _delay
* @type Number
* @default 0.016
* @private
*/
private _delay: number = 0.016;
/**
* The delay, in game clock units, that the timer will wait before firing the event
*
* This property must be greater than 0.
* @property delay
* @type Number
* @default 0.016
* @public
*/
public get delay(): number {
return this._delay;
}
public set delay( value: number ) {
if ( value > 0 ) {
this._delay = value;
} else {
Kiwi.Log.error( "Attempted to set timer delay", value,
"but value must be greater than 0", "#timer" );
}
}
/**
* The number of times the timer will repeat before stopping.
* @property repeatCount
* @type Number
* @default 0
* @public
*/
public repeatCount: number = 0;
/**
* Time elapsed on the current repetition
* @property _elapsed
* @type number
* @private
* @since 1.2.0
*/
private _elapsed: number = 0;
/**
* Clock time on last frame, used to calculate frame length and time elapsed
* @property _lastElapsed
* @type number
* @private
* @since 1.2.0
*/
private _lastElapsed: number;
/**
* Checks the list of TimerEvents added and processes them based on their type.
* @method processEvents
* @param type {Number} The type of events to dispatch
* @private
*/
private processEvents(type: number) {
if (type === TimerEvent.TIMER_START)
{
for (var i = 0; i < this._startEvents.length; i++)
{
this._startEvents[i].run();
}
}
else if (type === TimerEvent.TIMER_COUNT)
{
for (var i = 0; i < this._countEvents.length; i++)
{
this._countEvents[i].run();
}
}
else if (type === TimerEvent.TIMER_STOP)
{
for (var i = 0; i < this._stopEvents.length; i++)
{
this._stopEvents[i].run();
}
}
}
/**
* Internal update loop called by the Clock that this Timer belongs to.
* @method update
* @public
*/
public update() {
var frameLength = this._clock.elapsed() - this._lastElapsed;
this._lastElapsed = this._clock.elapsed();
if ( this._isRunning ) {
this._elapsed += frameLength;
}
while ( this._elapsed >= this.delay ) {
this._currentCount++;
this.processEvents( TimerEvent.TIMER_COUNT );
this._elapsed -= this.delay;
if ( this.repeatCount !== -1 && this._currentCount >= this.repeatCount ) {
this.stop();
}
}
while ( this._elapsed < 0 ) {
this._currentCount--;
this._elapsed += this.delay;
// Do not process events; they can happen when time flows forwards
if ( this._currentCount < 0 ) {
// Timer has regressed before its creation.
// When time flows forward again, the Timer will probably
// be restarted and repopulated.
// There is a potential memory leak: if a Timer is created
// for a single task, and has a TimerEvent that will
// remove it upon completion, but the Timer is rewound to
// before its creation, that removal will never fire.
this.clear();
this.stop();
}
}
}
/**
* Start the Timer. This will reset the timer and start it. The timer can only be started if it is in a stopped state.
* @method start
* @return {Kiwi.Time.Timer} this object.
* @public
*/
public start(): Timer {
if (this._isStopped === true)
{
this._isRunning = true;
this._isPaused = false;
this._isStopped = false;
this._currentCount = 0;
this._elapsed = 0;
this._lastElapsed = this._clock.elapsed() || 0;
this.processEvents(TimerEvent.TIMER_START);
}
return this;
}
/**
* Stop the Timer. Only possible when the timer is running or paused.
* @method stop
* @return {Kiwi.Time.Timer} this object.
* @public
*/
public stop():Timer {
if (this._isRunning === true || this._isPaused === true)
{
this._isRunning = false;
this._isPaused = false;
this._isStopped = true;
this.processEvents(TimerEvent.TIMER_STOP);
}
return this;
}
/**
* Pause the Timer. Only possible when the timer is running.
* @method pause
* @return {Kiwi.Time.Timer} this object.
* @public
*/
public pause():Timer {
if (this._isRunning === true)
{
this._isRunning = false;
this._isPaused = true;
}
return this;
}
/**
* Resume the Timer. Only possible if the timer has been paused.
* @method resume
* @return {Kiwi.Time.Timer} this object.
* @public
*/
public resume(): Timer {
if (this._isPaused === true)
{
this._isRunning = true;
this._isPaused = false;
}
return this;
}
/**
* Adds an existing TimerEvent object to this Timer.
* @method addTimerEvent
* @param {Kiwi.Time.TimerEvent} A TimerEvent object
* @return {Kiwi.Time.TimerEvent} The TimerEvent object
* @public
*/
public addTimerEvent(event:TimerEvent):TimerEvent {
if (event.type === TimerEvent.TIMER_START)
{
this._startEvents.push(event);
}
else if (event.type === TimerEvent.TIMER_COUNT)
{
this._countEvents.push(event);
}
else if (event.type === TimerEvent.TIMER_STOP)
{
this._stopEvents.push(event);
}
return event;
}
/**
* Creates a new TimerEvent and adds it to this Timer
* @method createTimerEvent
* @param type {Number} The type of TimerEvent to create (TIMER_START, TIMER_COUNT or TIMER_STOP).
* @param callback {Function} The function to call when the TimerEvent fires.
* @param context {Function} The context in which the given function will run (usually 'this')
* @return {Kiwi.Time.TimerEvent} The newly created TimerEvent.
* @public
*/
public createTimerEvent(type:number, callback, context):TimerEvent {
if (type === TimerEvent.TIMER_START)
{
this._startEvents.push(new TimerEvent(type, callback, context));
return this._startEvents[this._startEvents.length - 1];
}
else if (type === TimerEvent.TIMER_COUNT)
{
this._countEvents.push(new TimerEvent(type, callback, context));
return this._countEvents[this._countEvents.length - 1];
}
else if (type === TimerEvent.TIMER_STOP)
{
this._stopEvents.push(new TimerEvent(type, callback, context));
return this._stopEvents[this._stopEvents.length - 1];
}
return null;
}
/**
* Removes a TimerEvent object from this Timer
* @method removeTimerEvent
* @param {Kiwi.Time.TimerEvent} The TimerEvent to remove
* @return {boolean} True if the event was removed, otherwise false.
* @public
*/
public removeTimerEvent(event:TimerEvent):boolean {
var removed = [];
if (event.type === TimerEvent.TIMER_START)
{
removed = this._startEvents.splice(this._startEvents.indexOf(event), 1);
}
else if (event.type === TimerEvent.TIMER_COUNT)
{
removed = this._countEvents.splice(this._countEvents.indexOf(event), 1);
}
else if (event.type === TimerEvent.TIMER_STOP)
{
removed = this._stopEvents.splice(this._stopEvents.indexOf(event), 1);
}
if (removed.length === 1)
{
return true;
}
else
{
return false;
}
}
/**
* Removes all TimerEvent objects from this Timer
* @method clear
* @param type {Number} The type of TimerEvents to remove. Set to zero to remove them all.
* @return {boolean} True if the event was removed, otherwise false.
* @public
*/
public clear(type:number = 0) {
if (type === 0)
{
this._startEvents.length = 0;
this._countEvents.length = 0;
this._stopEvents.length = 0;
}
else if (type === TimerEvent.TIMER_START)
{
this._startEvents.length = 0;
}
else if (type === TimerEvent.TIMER_COUNT)
{
this._countEvents.length = 0;
}
else if (type === TimerEvent.TIMER_STOP)
{
this._stopEvents.length = 0;
}
}
/**
* Returns a string representation of this object.
* @method toString
* @return {string} a string representation of the instance.
* @public
*/
public toString(): string {
return "[{Timer (name=" + this.name + " delay=" + this.delay + " repeatCount=" + this.repeatCount + " running=" + this._isRunning + ")}]";
}
}
}