diff --git a/src/core/Game.js b/src/core/Game.js index c59f16c20..98acd9495 100644 --- a/src/core/Game.js +++ b/src/core/Game.js @@ -287,6 +287,23 @@ Phaser.Game = function (width, height, renderer, parent, state, transparent, ant */ this._codePaused = false; + /** + * The number of the logic update applied this render frame, starting from 0. + * + * The first update is `updateNumber === 0` and the last update is `updateNumber === updatesThisFrame.` + * @property {number} updateNumber + * @protected + */ + this.updateNumber = 0; + + /** + * Number of logic updates expected to occur this render frame; + * will be 1 unless there are catch-ups required (and allowed). + * @property {integer} updatesThisFrame + * @protected + */ + this.updatesThisFrame = 1; + /** * @property {number} _deltaTime - accumulate elapsed time until a logic update is due * @private @@ -294,39 +311,34 @@ Phaser.Game = function (width, height, renderer, parent, state, transparent, ant this._deltaTime = 0; /** - * @property {number} count - Update iteration counter. - * @protected - */ - this.count = 0; - - /** - * @property {number} _lastCount - remember how many 'catch-up' iterations were used on the logicUpdate last frame - * @private - */ + * @property {number} _lastCount - remember how many 'catch-up' iterations were used on the logicUpdate last frame + * @private + */ this._lastCount = 0; /** - * @property {number} _spiralling - if the 'catch-up' iterations are spiralling out of control, this counter is incremented - * @private - */ + * @property {number} _spiralling - if the 'catch-up' iterations are spiralling out of control, this counter is incremented + * @private + */ this._spiralling = 0; /** - * @property {Phaser.Signal} fpsProblemNotifier - if the game is struggling to maintain the desiredFps, this signal will be dispatched - * to suggest that the program adjust it's fps closer to the Time.suggestedFps value - * @public - */ + * If the game is struggling to maintain the desired FPS, this signal will be dispatched. + * The desired/chosen FPS should probably be closer to the {@link Phaser.Time#suggestedFps} value. + * @property {Phaser.Signal} fpsProblemNotifier + * @public + */ this.fpsProblemNotifier = new Phaser.Signal(); /** - * @property {boolean} forceSingleUpdate - Should the game loop force a logic update, regardless of the delta timer? Set to true if you know you need this. You can toggle it on the fly. - */ + * @property {boolean} forceSingleUpdate - Should the game loop force a logic update, regardless of the delta timer? Set to true if you know you need this. You can toggle it on the fly. + */ this.forceSingleUpdate = false; /** - * @property {number} _nextNotification - the soonest game.time.time value that the next fpsProblemNotifier can be dispatched - * @private - */ + * @property {number} _nextNotification - the soonest game.time.time value that the next fpsProblemNotifier can be dispatched + * @private + */ this._nextFpsNotification = 0; // Parse the configuration object (if any) @@ -710,32 +722,39 @@ Phaser.Game.prototype = { // call the game update logic multiple times if necessary to "catch up" with dropped frames // unless forceSingleUpdate is true - this.count = 0; + var count = 0; + + this.updatesThisFrame = Math.floor(this._deltaTime / slowStep); + if (this.forceSingleUpdate) + { + this.updatesThisFrame = Math.min(1, this.updatesThisFrame); + } while (this._deltaTime >= slowStep) { this._deltaTime -= slowStep; + this.updateNumber = count; this.updateLogic(1.0 / this.time.desiredFps); - this.count++; + count++; - if (this.forceSingleUpdate && this.count === 1) + if (this.forceSingleUpdate && count === 1) { break; } } // detect spiralling (if the catch-up loop isn't fast enough, the number of iterations will increase constantly) - if (this.count > this._lastCount) + if (count > this._lastCount) { this._spiralling++; } - else if (this.count < this._lastCount) + else if (count < this._lastCount) { // looks like it caught up successfully, reset the spiral alert counter this._spiralling = 0; } - this._lastCount = this.count; + this._lastCount = count; // call the game render update exactly once every frame unless we're playing catch-up from a spiral condition this.updateRender(this._deltaTime / slowStep); diff --git a/src/gameobjects/Sprite.js b/src/gameobjects/Sprite.js index 6a92da64a..22b77b647 100644 --- a/src/gameobjects/Sprite.js +++ b/src/gameobjects/Sprite.js @@ -116,9 +116,12 @@ Phaser.Sprite = function (game, x, y, key, frame) { this.health = 1; /** - * If you would like the Sprite to have a lifespan once 'born' you can set this to a positive value. Handy for particles, bullets, etc. - * The lifespan is decremented by game.time.elapsed each update, once it reaches zero the kill() function is called. - * @property {number} lifespan - The lifespan of the Sprite (in ms) before it will be killed. + * To given a Sprite a lifespan, in milliseconds, once 'born' you can set this to a positive value. Handy for particles, bullets, etc. + * + * The lifespan is decremented by `game.time.physicsElapsed` (converted to milliseconds) each logic update, + * and {@link Phaser.Sprite.kill kill} is called once the lifespan reaches 0. + * + * @property {number} lifespan * @default */ this.lifespan = 0; @@ -249,7 +252,7 @@ Phaser.Sprite.prototype.preUpdate = function() { // Only apply lifespan decrement in the first updateLogic pass. if (this.lifespan > 0 && this.game.count === 0) { - this.lifespan -= this.game.time.elapsedMS; + this.lifespan -= 1000 * this.game.time.physicsElapsed; if (this.lifespan <= 0) { diff --git a/src/time/Time.js b/src/time/Time.js index a76f90831..9acff1d3c 100644 --- a/src/time/Time.js +++ b/src/time/Time.js @@ -16,55 +16,92 @@ Phaser.Time = function (game) { /** * @property {Phaser.Game} game - Local reference to game. + * @protected */ this.game = game; /** - * @property {number} time - This always contains the Date.now value. + * The `Date.now()` value when the time was last updated. + * @property {integer} time * @protected */ this.time = 0; /** - * @property {number} prevTime - The time the previous update occurred. + * The `now` when the previous update occurred. + * @property {number} prevTime * @protected */ this.prevTime = 0; /** - * @property {number} now - The high resolution RAF timer value (if RAF is available) or Date.now if using setTimeout. + * An increasing value representing cumulative milliseconds since an undisclosed epoch. + * + * This value must _not_ be used with `Date.now()`. + * + * The source may either be from a high-res source (eg. if RAF is available) or the standard Date.now; + * the value can only be relied upon within a particular game instance. + * + * @property {number} now * @protected */ this.now = 0; /** - * @property {number} elapsed - Elapsed time since the last frame. In ms if running under setTimeout or an integer if using RAF. + * Elapsed time since the last time update, in milliseconds, based on `now`. + * + * This value _may_ include time that the game is paused/inactive. + * + * _Note:_ This is updated only once per game loop - even if multiple logic update steps are done. + * Use {@link Phaser.Timer#physicsTime physicsTime} as a basis of game/logic calculations instead. + * + * @property {number} elapsed * @see Phaser.Time.time * @protected */ this.elapsed = 0; /** - * @property {number} elapsedMS - The time in ms since the last update. Will vary dramatically based on system performance, do not use for physics calculations! + * The time in ms since the last time update, in milliseconds, based on `time`. + * + * This value is corrected for game pauses and will be "about zero" after a game is resumed. + * + * _Note:_ This is updated once per game loop - even if multiple logic update steps are done. + * Use {@link Phaser.Timer#physicsTime physicsTime} as a basis of game/logic calculations instead. + * + * @property {integer} elapsedMS * @protected */ this.elapsedMS = 0; /** - * @property {number} pausedTime - Records how long the game has been paused for. Is reset each time the game pauses. - * @protected + * The physics update delta, in fractional seconds. + * + * This should be used as an applicable multiplier by all logic update steps (eg. `preUpdate/postUpdate/update`) + * to ensure consistent game timing. + * + * With fixed-step updates this normally equivalent to `1.0 / desiredFps`. + * + * @property {number} physicsElapsed */ - this.pausedTime = 0; + this.physicsElapsed = 0; /** - * @property {number} desiredFps - The desired frame rate of your game. + * The desired frame rate of the game. + * + * This is used is used to calculate the physic/logic multiplier and how to apply catch-up logic updates. + * + * @property {number} desiredFps * @default */ this.desiredFps = 60; /** - * @property {number} suggestedFps = The suggested frame rate for your game, based on an averaged real frame rate. - * NOTE: Not available until after a few frames have passed, it is recommended to use this after a few seconds (eg. after the menus) + * The suggested frame rate for your game, based on an averaged real frame rate. + * + * _Note:_ This is not available until after a few frames have passed; use it after a few seconds (eg. after the menus) + * + * @property {number} suggestedFps * @default */ this.suggestedFps = null; @@ -109,27 +146,34 @@ Phaser.Time = function (game) { this.msMax = 0; /** - * @property {number} physicsElapsed - The physics motion value as used by Arcade Physics. Equivalent to 1.0 / Time.desiredFps. - */ - this.physicsElapsed = 0; - - /** - * @property {number} frames - The number of frames record in the last second. Only calculated if Time.advancedTiming is true. + * The number of render frames record in the last second. Only calculated if Time.advancedTiming is true. + * @property {integer} frames */ this.frames = 0; /** - * @property {number} pauseDuration - Records how long the game was paused for in miliseconds. + * The `time` when the game was last paused. + * @property {number} pausedTime + * @protected + */ + this.pausedTime = 0; + + /** + * Records how long the game was last paused, in miliseconds. + * (This is not updated until the game is resumed.) + * @property {number} pauseDuration */ this.pauseDuration = 0; /** * @property {number} timeToCall - The value that setTimeout needs to work out when to next update + * @protected */ this.timeToCall = 0; /** * @property {number} timeExpected - The time when the next call is expected when using setTimer to control the update loop + * @protected */ this.timeExpected = 0; @@ -272,13 +316,13 @@ Phaser.Time.prototype = { update: function (time) { // Set to the old Date.now value - this.elapsedMS = this.time; + var previousDateNow = this.time; // this.time always holds Date.now, this.now may hold the RAF high resolution time value if RAF is available (otherwise it also holds Date.now) this.time = Date.now(); // Adjust accorindlgy. - this.elapsedMS = this.time - this.elapsedMS; + this.elapsedMS = this.time - previousDateNow; // 'now' is currently still holding the time of the last call, move it into prevTime this.prevTime = this.now; @@ -446,4 +490,4 @@ Phaser.Time.prototype = { }; -Phaser.Time.prototype.constructor = Phaser.Time; \ No newline at end of file +Phaser.Time.prototype.constructor = Phaser.Time;