Merge pull request #1363 from pnstickne/wip-time-clarify

Time - clarify, Game update expose, and Sprite lifespan update
This commit is contained in:
Richard Davey 2014-11-25 14:15:41 +00:00
commit eec846666a
3 changed files with 118 additions and 52 deletions

View file

@ -287,18 +287,29 @@ Phaser.Game = function (width, height, renderer, parent, state, transparent, ant
*/ */
this._codePaused = false; 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 * @property {number} _deltaTime - accumulate elapsed time until a logic update is due
* @private * @private
*/ */
this._deltaTime = 0; 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 * @property {number} _lastCount - remember how many 'catch-up' iterations were used on the logicUpdate last frame
* @private * @private
@ -312,8 +323,9 @@ Phaser.Game = function (width, height, renderer, parent, state, transparent, ant
this._spiralling = 0; this._spiralling = 0;
/** /**
* @property {Phaser.Signal} fpsProblemNotifier - if the game is struggling to maintain the desiredFps, this signal will be dispatched * If the game is struggling to maintain the desired FPS, this signal will be dispatched.
* to suggest that the program adjust it's fps closer to the Time.suggestedFps value * The desired/chosen FPS should probably be closer to the {@link Phaser.Time#suggestedFps} value.
* @property {Phaser.Signal} fpsProblemNotifier
* @public * @public
*/ */
this.fpsProblemNotifier = new Phaser.Signal(); this.fpsProblemNotifier = new Phaser.Signal();
@ -710,32 +722,39 @@ Phaser.Game.prototype = {
// call the game update logic multiple times if necessary to "catch up" with dropped frames // call the game update logic multiple times if necessary to "catch up" with dropped frames
// unless forceSingleUpdate is true // 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) while (this._deltaTime >= slowStep)
{ {
this._deltaTime -= slowStep; this._deltaTime -= slowStep;
this.updateNumber = count;
this.updateLogic(1.0 / this.time.desiredFps); this.updateLogic(1.0 / this.time.desiredFps);
this.count++; count++;
if (this.forceSingleUpdate && this.count === 1) if (this.forceSingleUpdate && count === 1)
{ {
break; break;
} }
} }
// detect spiralling (if the catch-up loop isn't fast enough, the number of iterations will increase constantly) // 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++; this._spiralling++;
} }
else if (this.count < this._lastCount) else if (count < this._lastCount)
{ {
// looks like it caught up successfully, reset the spiral alert counter // looks like it caught up successfully, reset the spiral alert counter
this._spiralling = 0; 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 // call the game render update exactly once every frame unless we're playing catch-up from a spiral condition
this.updateRender(this._deltaTime / slowStep); this.updateRender(this._deltaTime / slowStep);

View file

@ -116,9 +116,12 @@ Phaser.Sprite = function (game, x, y, key, frame) {
this.health = 1; 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. * 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.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. * 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 * @default
*/ */
this.lifespan = 0; this.lifespan = 0;
@ -249,7 +252,7 @@ Phaser.Sprite.prototype.preUpdate = function() {
// Only apply lifespan decrement in the first updateLogic pass. // Only apply lifespan decrement in the first updateLogic pass.
if (this.lifespan > 0 && this.game.count === 0) 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) if (this.lifespan <= 0)
{ {

View file

@ -16,55 +16,92 @@ Phaser.Time = function (game) {
/** /**
* @property {Phaser.Game} game - Local reference to game. * @property {Phaser.Game} game - Local reference to game.
* @protected
*/ */
this.game = game; 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 * @protected
*/ */
this.time = 0; this.time = 0;
/** /**
* @property {number} prevTime - The time the previous update occurred. * The `now` when the previous update occurred.
* @property {number} prevTime
* @protected * @protected
*/ */
this.prevTime = 0; 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 * @protected
*/ */
this.now = 0; 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 * @see Phaser.Time.time
* @protected * @protected
*/ */
this.elapsed = 0; 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 * @protected
*/ */
this.elapsedMS = 0; this.elapsedMS = 0;
/** /**
* @property {number} pausedTime - Records how long the game has been paused for. Is reset each time the game pauses. * The physics update delta, in fractional seconds.
* @protected *
* 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 * @default
*/ */
this.desiredFps = 60; this.desiredFps = 60;
/** /**
* @property {number} suggestedFps = The suggested frame rate for your game, based on an averaged real frame rate. * 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) *
* _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 * @default
*/ */
this.suggestedFps = null; this.suggestedFps = null;
@ -109,27 +146,34 @@ Phaser.Time = function (game) {
this.msMax = 0; this.msMax = 0;
/** /**
* @property {number} physicsElapsed - The physics motion value as used by Arcade Physics. Equivalent to 1.0 / Time.desiredFps. * The number of render frames record in the last second. Only calculated if Time.advancedTiming is true.
*/ * @property {integer} frames
this.physicsElapsed = 0;
/**
* @property {number} frames - The number of frames record in the last second. Only calculated if Time.advancedTiming is true.
*/ */
this.frames = 0; 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; this.pauseDuration = 0;
/** /**
* @property {number} timeToCall - The value that setTimeout needs to work out when to next update * @property {number} timeToCall - The value that setTimeout needs to work out when to next update
* @protected
*/ */
this.timeToCall = 0; this.timeToCall = 0;
/** /**
* @property {number} timeExpected - The time when the next call is expected when using setTimer to control the update loop * @property {number} timeExpected - The time when the next call is expected when using setTimer to control the update loop
* @protected
*/ */
this.timeExpected = 0; this.timeExpected = 0;
@ -272,13 +316,13 @@ Phaser.Time.prototype = {
update: function (time) { update: function (time) {
// Set to the old Date.now value // 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 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(); this.time = Date.now();
// Adjust accorindlgy. // 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 // 'now' is currently still holding the time of the last call, move it into prevTime
this.prevTime = this.now; this.prevTime = this.now;