Skip to content

Commit 838027a

Browse files
committed
Timer.timeCap is a new setting allowing your Timers to protect against unexpectedly large delta timers.
1 parent 95fe57e commit 838027a

4 files changed

Lines changed: 71 additions & 16 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ Version 2.0.4 - "Mos Shirare" - in development
130130
* Color.RGBtoString converts an rgba color into a # or 0x color string.
131131
* Color.HSVColorWheel will return an array with 360 color objects for each segment of an HSV color wheel, you can optionally set the saturation and value amounts.
132132
* Color.HSLColorWheel will return an array with 360 color objects for each segment of an HSL color wheel, you can optionally set the saturation and lightness amounts.
133+
* Timer.timeCap is a new setting allowing your Timers to protect against unexpectedly large delta timers.
133134

134135

135136
### Bug Fixes

src/sound/SoundManager.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ Phaser.SoundManager.prototype = {
233233

234234
/**
235235
* Stops all the sounds in the game.
236+
*
236237
* @method Phaser.SoundManager#stopAll
237238
*/
238239
stopAll: function () {
@@ -249,6 +250,7 @@ Phaser.SoundManager.prototype = {
249250

250251
/**
251252
* Pauses all the sounds in the game.
253+
*
252254
* @method Phaser.SoundManager#pauseAll
253255
*/
254256
pauseAll: function () {
@@ -264,7 +266,8 @@ Phaser.SoundManager.prototype = {
264266
},
265267

266268
/**
267-
* resumes every sound in the game.
269+
* Resumes every sound in the game.
270+
*
268271
* @method Phaser.SoundManager#resumeAll
269272
*/
270273
resumeAll: function () {
@@ -281,6 +284,7 @@ Phaser.SoundManager.prototype = {
281284

282285
/**
283286
* Decode a sound by its assets key.
287+
*
284288
* @method Phaser.SoundManager#decode
285289
* @param {string} key - Assets key of the sound to be decoded.
286290
* @param {Phaser.Sound} [sound] - Its buffer will be set to decoded data.
@@ -313,6 +317,7 @@ Phaser.SoundManager.prototype = {
313317

314318
/**
315319
* Updates every sound in the game.
320+
*
316321
* @method Phaser.SoundManager#update
317322
*/
318323
update: function () {
@@ -340,6 +345,7 @@ Phaser.SoundManager.prototype = {
340345

341346
/**
342347
* Adds a new Sound into the SoundManager.
348+
*
343349
* @method Phaser.SoundManager#add
344350
* @param {string} key - Asset key for the sound.
345351
* @param {number} [volume=1] - Default value for the volume.

src/time/Time.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@ Phaser.Time = function (game) {
8686
*/
8787
this.deltaCap = 0;
8888

89+
/**
90+
* @property {number} timeCap - If the difference in time between two frame updates exceeds this value, the frame time is reset to avoid huge elapsed counts.
91+
*/
92+
this.timeCap = 1000;
93+
8994
/**
9095
* @property {number} frames - The number of frames record in the last second. Only calculated if Time.advancedTiming is true.
9196
*/
@@ -212,7 +217,7 @@ Phaser.Time.prototype = {
212217
*
213218
* @method Phaser.Time#update
214219
* @protected
215-
* @param {number} time - The current timestamp, either performance.now or Date.now depending on the browser.
220+
* @param {number} time - The current timestamp.
216221
*/
217222
update: function (time) {
218223

@@ -222,6 +227,15 @@ Phaser.Time.prototype = {
222227

223228
this.elapsed = this.now - this.time;
224229

230+
// spike-dislike
231+
if (this.game.stage.disableVisibilityChange && this.elapsed > this.timeCap)
232+
{
233+
// For some reason the time between now and the last time the game was updated was larger than our timeCap
234+
// This can happen if the Stage.disableVisibilityChange is true and you swap tabs, which makes the raf pause.
235+
// In this case we'll drop to some default values to stop the game timers going nuts.
236+
this.elapsed = 1 / 30;
237+
}
238+
225239
// Calculate physics elapsed, ensure it's > 0, use 1/60 as a fallback
226240
this.physicsElapsed = this.elapsed / 1000 || 1 / 60;
227241

src/time/Timer.js

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ Phaser.Timer = function (game, autoDestroy) {
4242
*/
4343
this.expired = false;
4444

45+
/**
46+
* @property {number} elapsed - Elapsed time since the last frame (in ms).
47+
* @protected
48+
*/
49+
this.elapsed = 0;
50+
4551
/**
4652
* @property {array<Phaser.TimerEvent>} events - An array holding all of this timers Phaser.TimerEvent objects. Use the methods add, repeat and loop to populate it.
4753
*/
@@ -59,6 +65,11 @@ Phaser.Timer = function (game, autoDestroy) {
5965
*/
6066
this.nextTick = 0;
6167

68+
/**
69+
* @property {number} timeCap - If the difference in time between two frame updates exceeds this value, the event times are reset to avoid catch-up situations.
70+
*/
71+
this.timeCap = 1000;
72+
6273
/**
6374
* @property {boolean} paused - The paused state of the Timer. You can pause the timer by calling Timer.pause() and Timer.resume() or by the game pausing.
6475
* @readonly
@@ -369,7 +380,8 @@ Phaser.Timer.prototype = {
369380
},
370381

371382
/**
372-
* The main Timer update event, called automatically by the Game clock.
383+
* The main Timer update event, called automatically by Phaser.Time.update.
384+
*
373385
* @method Phaser.Timer#update
374386
* @protected
375387
* @param {number} time - The time from the core game clock.
@@ -382,7 +394,18 @@ Phaser.Timer.prototype = {
382394
return true;
383395
}
384396

397+
this.elapsed = time - this._now;
385398
this._now = time;
399+
400+
// spike-dislike
401+
if (this.game.stage.disableVisibilityChange && this.elapsed > this.timeCap)
402+
{
403+
// For some reason the time between now and the last time the game was updated was larger than our timeCap
404+
// This can happen if the Stage.disableVisibilityChange is true and you swap tabs, which makes the raf pause.
405+
// In this case we need to adjust the TimerEvents and nextTick.
406+
this.adjustEvents(time - this.elapsed);
407+
}
408+
386409
this._marked = 0;
387410

388411
// Clears events marked for deletion and resets _len and _i to 0.
@@ -488,26 +511,18 @@ Phaser.Timer.prototype = {
488511
},
489512

490513
/**
491-
* Resumes the Timer and updates all pending events.
514+
* Adjusts the time of all pending events and the nextTick by the given baseTime.
492515
*
493-
* @method Phaser.Timer#resume
516+
* @method Phaser.Timer#adjustEvents
494517
*/
495-
resume: function () {
496-
497-
if (!this.paused)
498-
{
499-
return;
500-
}
501-
502-
this._pauseTotal += this.game.time.pauseDuration;
503-
this._now = this.game.time.now;
518+
adjustEvents: function (baseTime) {
504519

505520
for (var i = 0; i < this.events.length; i++)
506521
{
507522
if (!this.events[i].pendingDelete)
508523
{
509524
// Work out how long there would have been from when the game paused until the events next tick
510-
var t = this.events[i].tick - this._pauseStarted;
525+
var t = this.events[i].tick - baseTime;
511526

512527
if (t < 0)
513528
{
@@ -519,7 +534,7 @@ Phaser.Timer.prototype = {
519534
}
520535
}
521536

522-
var d = this.nextTick - this._pauseStarted;
537+
var d = this.nextTick - baseTime;
523538

524539
if (d < 0)
525540
{
@@ -530,6 +545,25 @@ Phaser.Timer.prototype = {
530545
this.nextTick = this._now + d;
531546
}
532547

548+
},
549+
550+
/**
551+
* Resumes the Timer and updates all pending events.
552+
*
553+
* @method Phaser.Timer#resume
554+
*/
555+
resume: function () {
556+
557+
if (!this.paused)
558+
{
559+
return;
560+
}
561+
562+
this._pauseTotal += this.game.time.pauseDuration;
563+
this._now = this.game.time.now;
564+
565+
this.adjustEvents(this._pauseStarted);
566+
533567
this.paused = false;
534568
this._codePaused = false;
535569

0 commit comments

Comments
 (0)