Skip to content

Commit

Permalink
[excaliburjs#905] Add finite execution count limit to timers (excalib…
Browse files Browse the repository at this point in the history
…urjs#974)

Closes excaliburjs#905.

- Allow timers to repeat execution a finite number of times via `new ex.Timer(fn, interval, numTimes);`
- Allow times to be reset with a different limit via `timer.reset(interval, numTimes)`
- Allow access to the number of times a timer has been executed via timer.timesRepeated
  • Loading branch information
alanag13 authored and Roman committed May 8, 2018
1 parent 6cc4de1 commit 80f8756
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
-Property scope `Pointer.actorsUnderPointer` changed to private;

## Added
- Allow timers to limit repeats to a finite number of times ([#957](https://github.com/excaliburjs/Excalibur/pull/974))

-New `PointerEvent.stopPropagation()` method added. Works the same way as (`https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation`)
([#912](https://github.com/excaliburjs/Excalibur/issues/912))
Expand Down
36 changes: 32 additions & 4 deletions src/engine/Timer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,27 @@ export class Timer {
public interval: number = 10;
public fcn: () => void = () => { return; };
public repeats: boolean = false;
public maxNumberOfRepeats: number = -1;
private _elapsedTime: number = 0;
private _totalTimeAlive: number = 0;
private _paused: boolean = false;
private _numberOfTicks: number = 0;
public complete: boolean = false;
public scene: Scene = null;

/**
* @param fcn The callback to be fired after the interval is complete.
* @param interval Interval length
* @param repeats Indicates whether this call back should be fired only once, or repeat after every interval as completed.
* @param repeats Indicates whether this call back should be fired only once, or repeat after every interval as completed.
* @param numberOfRepeats Specifies a maximum number of times that this timer will execute.
*/
constructor(fcn: () => void, interval: number, repeats?: boolean) {
constructor(fcn: () => void, interval: number, repeats?: boolean, numberOfRepeats?: number) {
if (!!numberOfRepeats && numberOfRepeats >= 0) {
this.maxNumberOfRepeats = numberOfRepeats;
if (!repeats) {
throw new Error('repeats must be set to true if numberOfRepeats is set');
}
}
this.id = Timer.id++;
this.interval = interval || this.interval;
this.fcn = fcn || this.fcn;
Expand All @@ -36,8 +45,14 @@ export class Timer {
if (!this._paused) {
this._totalTimeAlive += delta;
this._elapsedTime += delta;

if (this.maxNumberOfRepeats > -1 && this._numberOfTicks >= this.maxNumberOfRepeats) {
this.complete = true;
}

if (!this.complete && this._elapsedTime >= this.interval) {
this.fcn.call(this);
this._numberOfTicks++;
if (this.repeats) {
this._elapsedTime = 0;
} else {
Expand All @@ -50,14 +65,27 @@ export class Timer {
/**
* Resets the timer so that it can be reused, and optionally reconfigure the timers interval.
* @param newInterval If specified, sets a new non-negative interval in milliseconds to refire the callback
* @param newNumberOfRepeats If specified, sets a new non-negative upper limit to the number of time this timer executes
*/
public reset(newInterval?: number) {
public reset(newInterval?: number, newNumberOfRepeats?: number) {
if (!!newInterval && newInterval >= 0) {
this.interval = newInterval;
}

if (!!this.maxNumberOfRepeats && this.maxNumberOfRepeats >= 0) {
this.maxNumberOfRepeats = newNumberOfRepeats;
if (!this.repeats) {
throw new Error('repeats must be set to true if numberOfRepeats is set');
}
}

this.complete = false;
this._elapsedTime = 0;

this._numberOfTicks = 0;
}

public get timesRepeated(): number {
return this._numberOfTicks;
}

public getTimeRunning(): number {
Expand Down
39 changes: 38 additions & 1 deletion src/spec/TimerSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ describe('A Timer', () => {
expect(timer.complete).toBeTruthy();
});

it('can repeat itself multiple number of times', () => {
it('can repeat itself indefinitely at a specified interval', () => {
// count the number of fires
var count = 0;
timer = new ex.Timer(function(){ count++; }, 500, true);
Expand All @@ -47,6 +47,17 @@ describe('A Timer', () => {
expect(count).toBe(3);
});

it('can repeat itself a finite number of times', () => {
// count the number of fires
timer = new ex.Timer(function(){ var dummy = 0; }, 500, true, 2);

timer.update(501);
timer.update(501);
timer.update(501);

expect(timer.timesRepeated).toBe(2);
});

it('can return how long it has been running', () => {
timer.update(372);

Expand Down Expand Up @@ -168,6 +179,32 @@ describe('A Timer', () => {
}
});

it('can be reset with a different number of maximum iterations', () => {
// non-repeating timer
timer = new ex.Timer(function(){ var dummy = 0; }, 500, true, 3);
scene.add(timer);

// tick the timer
scene.update(engine, 501);
scene.update(engine, 501);
scene.update(engine, 501);
expect(timer.timesRepeated).toBe(3);

// tick the timer again, but it shouldn't fire until reset
scene.update(engine, 501);
expect(timer.timesRepeated).toBe(3);
expect(timer.complete).toBe(true);

// once reset the timer should fire again
timer.reset(500, 4);
expect(timer.complete).toBe(false);
scene.update(engine, 501);
scene.update(engine, 501);
scene.update(engine, 501);
scene.update(engine, 501);
expect(timer.timesRepeated).toBe(4);
});

it('can be paused', () => {

var count = 0;
Expand Down

0 comments on commit 80f8756

Please sign in to comment.