This commit is contained in:
Felipe Alfonso 2017-05-03 21:28:18 -03:00
commit ee4e0bf8e9
4 changed files with 41 additions and 277 deletions

View file

@ -1,8 +1,3 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2016 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
var Config = require('./Config');
var DebugHeader = require('./DebugHeader');
@ -11,9 +6,7 @@ var Device = require('../device');
var AddToDOM = require('../dom/AddToDOM');
var DOMContentLoaded = require('../dom/DOMContentLoaded');
var MainLoop = require('./MainLoop');
var TickerLoop = require('./TickerLoop');
var VariableTimeStep = require('./VariableTimeStep');
var TimeStep = require('./TimeStep');
var CreateRenderer = require('./CreateRenderer');
var GlobalInputManager = require('../input/GlobalInputManager');
var GlobalStateManager = require('../state/GlobalStateManager');
@ -72,14 +65,7 @@ var Game = function (config)
* @property {Phaser.MainLoop} mainloop - Main Loop handler.
* @protected
*/
// if (this.config.useTicker)
// {
this.loop = new VariableTimeStep(this, this.config.fps);
// }
// else
// {
// this.loop = new MainLoop(this, this.config.fps);
// }
this.loop = new TimeStep(this, this.config.fps);
// Wait for the DOM Ready event, then call boot.
DOMContentLoaded(this.boot.bind(this));

View file

@ -1,250 +0,0 @@
var RequestAnimationFrame = require('../dom/RequestAnimationFrame');
// My thanks to Isaac Sukin for creating MainLoop.js, on which lots of this is based.
var MainLoop = function (game, framerate)
{
this.game = game;
/**
* @property {Phaser.RequestAnimationFrame} raf - Automatically handles the core game loop via requestAnimationFrame or setTimeout
* @protected
*/
this.raf = new RequestAnimationFrame();
/**
* @property {number} timestep - The amount of time (in milliseconds) to simulate each time update() runs.
*/
this.timestep = 1000 / framerate;
/**
* @property {number} physicsStep - 1 / framerate.
*/
this.physicsStep = 1 / framerate;
/**
* @property {number} frameDelta - The cumulative amount of in-app time that hasn't been simulated yet.
*/
this.frameDelta = 0;
this.discardedTime = 0;
/**
* The timestamp in milliseconds of the last time the main loop was run.
* Used to compute the time elapsed between frames.
* @property {number} lastFrameTimeMs
*/
this.lastFrameTimeMs = 0;
/**
* @property {number} fps - An exponential moving average of the frames per second.
*/
this.fps = 60;
/**
* @property {number} lastFpsUpdate - The timestamp (in milliseconds) of the last time the `fps` moving average was updated.
*/
this.lastFpsUpdate = 0;
/**
* @property {number} framesThisSecond - The number of frames delivered in the current second.
*/
this.framesThisSecond = 0;
/**
* @property {number} numUpdateSteps - The number of times update() is called in a given frame.
*/
this.numUpdateSteps = 0;
/**
* The minimum amount of time in milliseconds that must pass since the last frame was executed
* before another frame can be executed.
* The multiplicative inverse caps the FPS (the default of zero means there is no cap)
* @property {number} minFrameDelay
*/
this.minFrameDelay = 0;
/**
* @property {boolean} running - Whether the main loop is running.
*/
this.running = false;
/**
* `true` if `MainLoop.start()` has been called and the most recent time it
* was called has not been followed by a call to `MainLoop.stop()`. This is
* different than `running` because there is a delay of a few milliseconds
* after `MainLoop.start()` is called before the application is considered
* "running." This delay is due to waiting for the next frame.
* @property {boolean} started
*/
this.started = false;
/**
* Whether the simulation has fallen too far behind real time.
* Specifically, `panic` will be set to `true` if too many updates occur in
* one frame. This is only relevant inside of animate(), but a reference is
* held externally so that this variable is not marked for garbage
* collection every time the main loop runs.
* @property {boolean} panic - Whether the simulation has fallen too far behind real time.
*/
this.panic = false;
};
MainLoop.prototype.constructor = MainLoop;
MainLoop.prototype = {
setMaxFPS: function (fps)
{
if (fps === 0)
{
this.stop();
}
else
{
this.minFrameDelay = 1000 / fps;
}
},
getMaxFPS: function ()
{
return 1000 / this.minFrameDelay;
},
resetFrameDelta: function ()
{
var oldFrameDelta = this.frameDelta;
this.frameDelta = 0;
return oldFrameDelta;
},
start: function ()
{
if (this.started)
{
return this;
}
this.started = true;
this.running = true;
this.lastFrameTimeMs = window.performance.now();
this.lastFpsUpdate = window.performance.now();
this.framesThisSecond = 0;
this.raf.start(this.step.bind(this), this.game.config.forceSetTimeOut);
},
// timestamp = DOMHighResTimeStamp
// active = array containing: ({ index: i, state: state })
step: function (timestamp)
{
var active = this.game.state.active;
var renderer = this.game.renderer;
var len = active.length;
// Throttle the frame rate (if minFrameDelay is set to a non-zero value by
// `MainLoop.setMaxAllowedFPS()`).
if (len === 0 || timestamp < this.lastFrameTimeMs + this.minFrameDelay)
{
return;
}
// frameDelta is the cumulative amount of in-app time that hasn't been
// simulated yet. Add the time since the last frame. We need to track total
// not-yet-simulated time (as opposed to just the time elapsed since the
// last frame) because not all actually elapsed time is guaranteed to be
// simulated each frame. See the comments below for details.
this.frameDelta += timestamp - this.lastFrameTimeMs;
this.lastFrameTimeMs = timestamp;
// Global Managers (Time, Input, etc)
this.game.input.update(timestamp, this.frameDelta);
// Run any updates that are not dependent on time in the simulation.
// Here we'll need to run things like tween.update, input.update, etc.
for (var i = 0; i < len; i++)
{
active[i].state.sys.begin(timestamp, this.frameDelta);
}
// Update the estimate of the frame rate, `fps`. Every second, the number
// of frames that occurred in that second are included in an exponential
// moving average of all frames per second, with an alpha of 0.25. This
// means that more recent seconds affect the estimated frame rate more than
// older seconds.
if (timestamp > this.lastFpsUpdate + 1000)
{
// Compute the new exponential moving average with an alpha of 0.25.
// Using constants inline is okay here.
this.fps = 0.25 * this.framesThisSecond + 0.75 * this.fps;
this.lastFpsUpdate = timestamp;
this.framesThisSecond = 0;
}
this.framesThisSecond++;
this.numUpdateSteps = 0;
while (this.frameDelta >= this.timestep)
{
for (i = 0; i < len; i++)
{
active[i].state.sys.update(this.timestep, this.physicsStep);
}
this.frameDelta -= this.timestep;
if (++this.numUpdateSteps >= 240)
{
this.panic = true;
break;
}
}
// Render
var interpolation = this.frameDelta / this.timestep;
renderer.preRender();
// This uses active.length, in case state.update removed the state from the active list
for (i = 0; i < active.length; i++)
{
active[i].state.sys.render(interpolation, renderer);
}
renderer.postRender();
if (this.panic)
{
// This pattern introduces non-deterministic behavior, but in this case
// it's better than the alternative (the application would look like it
// was running very quickly until the simulation caught up to real
// time).
this.discardedTime = Math.round(this.resetFrameDelta());
// console.warn('Main loop panicked, tab probably put in the background. Discarding ' + discardedTime + 'ms');
}
this.panic = false;
},
stop: function ()
{
this.running = false;
this.started = false;
return this;
}
};
module.exports = MainLoop;

View file

@ -1,7 +1,7 @@
var NOOP = require('../utils/NOOP');
var RequestAnimationFrame = require('../dom/RequestAnimationFrame');
var TickerLoop = function (game, framerate)
var Ticker = function (game, framerate)
{
this.game = game;
@ -29,9 +29,9 @@ var TickerLoop = function (game, framerate)
this.useRAF = true;
};
TickerLoop.prototype.constructor = TickerLoop;
Ticker.prototype.constructor = Ticker;
TickerLoop.prototype = {
Ticker.prototype = {
toString: function ()
{
@ -157,4 +157,4 @@ TickerLoop.prototype = {
};
module.exports = TickerLoop;
module.exports = Ticker;

View file

@ -11,7 +11,7 @@ var RequestAnimationFrame = require('../dom/RequestAnimationFrame');
// deltaHistory: 10
// }
var VariableTimeStep = function (game, config)
var TimeStep = function (game, config)
{
this.game = game;
@ -32,6 +32,15 @@ var VariableTimeStep = function (game, config)
// 8.333 / 1000 = 0.008333 (120fps)
// 16.666 / 1000 = 0.01666 (60fps)
/**
* @property {number} fps - An exponential moving average of the frames per second.
* @readOnly
*/
this.actualFps = this.targetFps;
this.nextFpsUpdate = 0;
this.framesThisSecond = 0;
this.callback = NOOP;
this.forceSetTimeOut = GetValue(config, 'forceSetTimeOut', false);
@ -46,9 +55,9 @@ var VariableTimeStep = function (game, config)
this.deltaSmoothingMax = GetValue(config, 'deltaHistory', 10);
};
VariableTimeStep.prototype.constructor = VariableTimeStep;
TimeStep.prototype.constructor = TimeStep;
VariableTimeStep.prototype = {
TimeStep.prototype = {
start: function (callback)
{
@ -65,7 +74,8 @@ VariableTimeStep.prototype = {
this.time = now;
this.startTime = now;
this.lastTime = now;
this.runOff = 0;
this.nextFpsUpdate = now + 1000;
this.framesThisSecond = 0;
// Pre-populate smoothing array
@ -137,7 +147,26 @@ VariableTimeStep.prototype = {
// Real-world timer advance
this.time += avg;
this.callback(this.time, avg);
// Update the estimate of the frame rate, `fps`. Every second, the number
// of frames that occurred in that second are included in an exponential
// moving average of all frames per second, with an alpha of 0.25. This
// means that more recent seconds affect the estimated frame rate more than
// older seconds.
if (time > this.nextFpsUpdate)
{
// Compute the new exponential moving average with an alpha of 0.25.
// Using constants inline is okay here.
this.actualFps = 0.25 * this.framesThisSecond + 0.75 * this.actualFps;
this.nextFpsUpdate = time + 1000;
this.framesThisSecond = 0;
}
this.framesThisSecond++;
// Interpolation - how far between what is expected and where we are?
var interpolation = avg / this._target;
this.callback(this.time, avg, interpolation);
// Shift time value over
this.lastTime = time;
@ -197,7 +226,6 @@ VariableTimeStep.prototype = {
return this;
}
};
module.exports = VariableTimeStep;
module.exports = TimeStep;