diff --git a/README.md b/README.md index a03800a03..acbe80448 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,7 @@ Version 2.0.4 - "Mos Shirare" - in development * Color.RGBtoString converts an rgba color into a # or 0x color string. * 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. * 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. +* Timer.timeCap is a new setting allowing your Timers to protect against unexpectedly large delta timers. ### Bug Fixes diff --git a/src/sound/SoundManager.js b/src/sound/SoundManager.js index 1c3f3bf8a..4c231abf9 100644 --- a/src/sound/SoundManager.js +++ b/src/sound/SoundManager.js @@ -233,6 +233,7 @@ Phaser.SoundManager.prototype = { /** * Stops all the sounds in the game. + * * @method Phaser.SoundManager#stopAll */ stopAll: function () { @@ -249,6 +250,7 @@ Phaser.SoundManager.prototype = { /** * Pauses all the sounds in the game. + * * @method Phaser.SoundManager#pauseAll */ pauseAll: function () { @@ -264,7 +266,8 @@ Phaser.SoundManager.prototype = { }, /** - * resumes every sound in the game. + * Resumes every sound in the game. + * * @method Phaser.SoundManager#resumeAll */ resumeAll: function () { @@ -281,6 +284,7 @@ Phaser.SoundManager.prototype = { /** * Decode a sound by its assets key. + * * @method Phaser.SoundManager#decode * @param {string} key - Assets key of the sound to be decoded. * @param {Phaser.Sound} [sound] - Its buffer will be set to decoded data. @@ -313,6 +317,7 @@ Phaser.SoundManager.prototype = { /** * Updates every sound in the game. + * * @method Phaser.SoundManager#update */ update: function () { @@ -340,6 +345,7 @@ Phaser.SoundManager.prototype = { /** * Adds a new Sound into the SoundManager. + * * @method Phaser.SoundManager#add * @param {string} key - Asset key for the sound. * @param {number} [volume=1] - Default value for the volume. diff --git a/src/time/Time.js b/src/time/Time.js index d8c17fb7f..cf66d7dce 100644 --- a/src/time/Time.js +++ b/src/time/Time.js @@ -86,6 +86,11 @@ Phaser.Time = function (game) { */ this.deltaCap = 0; + /** + * @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. + */ + this.timeCap = 1000; + /** * @property {number} frames - The number of frames record in the last second. Only calculated if Time.advancedTiming is true. */ @@ -212,7 +217,7 @@ Phaser.Time.prototype = { * * @method Phaser.Time#update * @protected - * @param {number} time - The current timestamp, either performance.now or Date.now depending on the browser. + * @param {number} time - The current timestamp. */ update: function (time) { @@ -222,6 +227,15 @@ Phaser.Time.prototype = { this.elapsed = this.now - this.time; + // spike-dislike + if (this.game.stage.disableVisibilityChange && this.elapsed > this.timeCap) + { + // For some reason the time between now and the last time the game was updated was larger than our timeCap + // This can happen if the Stage.disableVisibilityChange is true and you swap tabs, which makes the raf pause. + // In this case we'll drop to some default values to stop the game timers going nuts. + this.elapsed = 1 / 30; + } + // Calculate physics elapsed, ensure it's > 0, use 1/60 as a fallback this.physicsElapsed = this.elapsed / 1000 || 1 / 60; diff --git a/src/time/Timer.js b/src/time/Timer.js index 4d57ba068..e081a6f73 100644 --- a/src/time/Timer.js +++ b/src/time/Timer.js @@ -42,6 +42,12 @@ Phaser.Timer = function (game, autoDestroy) { */ this.expired = false; + /** + * @property {number} elapsed - Elapsed time since the last frame (in ms). + * @protected + */ + this.elapsed = 0; + /** * @property {array} events - An array holding all of this timers Phaser.TimerEvent objects. Use the methods add, repeat and loop to populate it. */ @@ -59,6 +65,11 @@ Phaser.Timer = function (game, autoDestroy) { */ this.nextTick = 0; + /** + * @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. + */ + this.timeCap = 1000; + /** * @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. * @readonly @@ -369,7 +380,8 @@ Phaser.Timer.prototype = { }, /** - * The main Timer update event, called automatically by the Game clock. + * The main Timer update event, called automatically by Phaser.Time.update. + * * @method Phaser.Timer#update * @protected * @param {number} time - The time from the core game clock. @@ -382,7 +394,18 @@ Phaser.Timer.prototype = { return true; } + this.elapsed = time - this._now; this._now = time; + + // spike-dislike + if (this.game.stage.disableVisibilityChange && this.elapsed > this.timeCap) + { + // For some reason the time between now and the last time the game was updated was larger than our timeCap + // This can happen if the Stage.disableVisibilityChange is true and you swap tabs, which makes the raf pause. + // In this case we need to adjust the TimerEvents and nextTick. + this.adjustEvents(time - this.elapsed); + } + this._marked = 0; // Clears events marked for deletion and resets _len and _i to 0. @@ -487,6 +510,43 @@ Phaser.Timer.prototype = { }, + /** + * Adjusts the time of all pending events and the nextTick by the given baseTime. + * + * @method Phaser.Timer#adjustEvents + */ + adjustEvents: function (baseTime) { + + for (var i = 0; i < this.events.length; i++) + { + if (!this.events[i].pendingDelete) + { + // Work out how long there would have been from when the game paused until the events next tick + var t = this.events[i].tick - baseTime; + + if (t < 0) + { + t = 0; + } + + // Add the difference on to the time now + this.events[i].tick = this._now + t; + } + } + + var d = this.nextTick - baseTime; + + if (d < 0) + { + this.nextTick = this._now; + } + else + { + this.nextTick = this._now + d; + } + + }, + /** * Resumes the Timer and updates all pending events. * @@ -502,33 +562,7 @@ Phaser.Timer.prototype = { this._pauseTotal += this.game.time.pauseDuration; this._now = this.game.time.now; - for (var i = 0; i < this.events.length; i++) - { - if (!this.events[i].pendingDelete) - { - // Work out how long there would have been from when the game paused until the events next tick - var t = this.events[i].tick - this._pauseStarted; - - if (t < 0) - { - t = 0; - } - - // Add the difference on to the time now - this.events[i].tick = this._now + t; - } - } - - var d = this.nextTick - this._pauseStarted; - - if (d < 0) - { - this.nextTick = this._now; - } - else - { - this.nextTick = this._now + d; - } + this.adjustEvents(this._pauseStarted); this.paused = false; this._codePaused = false;