mirror of
https://github.com/photonstorm/phaser
synced 2024-11-27 15:12:18 +00:00
Merge branch 'master' of https://github.com/photonstorm/phaser
This commit is contained in:
commit
ee4e0bf8e9
4 changed files with 41 additions and 277 deletions
|
@ -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 Config = require('./Config');
|
||||||
var DebugHeader = require('./DebugHeader');
|
var DebugHeader = require('./DebugHeader');
|
||||||
|
@ -11,9 +6,7 @@ var Device = require('../device');
|
||||||
var AddToDOM = require('../dom/AddToDOM');
|
var AddToDOM = require('../dom/AddToDOM');
|
||||||
var DOMContentLoaded = require('../dom/DOMContentLoaded');
|
var DOMContentLoaded = require('../dom/DOMContentLoaded');
|
||||||
|
|
||||||
var MainLoop = require('./MainLoop');
|
var TimeStep = require('./TimeStep');
|
||||||
var TickerLoop = require('./TickerLoop');
|
|
||||||
var VariableTimeStep = require('./VariableTimeStep');
|
|
||||||
var CreateRenderer = require('./CreateRenderer');
|
var CreateRenderer = require('./CreateRenderer');
|
||||||
var GlobalInputManager = require('../input/GlobalInputManager');
|
var GlobalInputManager = require('../input/GlobalInputManager');
|
||||||
var GlobalStateManager = require('../state/GlobalStateManager');
|
var GlobalStateManager = require('../state/GlobalStateManager');
|
||||||
|
@ -72,14 +65,7 @@ var Game = function (config)
|
||||||
* @property {Phaser.MainLoop} mainloop - Main Loop handler.
|
* @property {Phaser.MainLoop} mainloop - Main Loop handler.
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
// if (this.config.useTicker)
|
this.loop = new TimeStep(this, this.config.fps);
|
||||||
// {
|
|
||||||
this.loop = new VariableTimeStep(this, this.config.fps);
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// this.loop = new MainLoop(this, this.config.fps);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Wait for the DOM Ready event, then call boot.
|
// Wait for the DOM Ready event, then call boot.
|
||||||
DOMContentLoaded(this.boot.bind(this));
|
DOMContentLoaded(this.boot.bind(this));
|
||||||
|
|
|
@ -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;
|
|
|
@ -1,7 +1,7 @@
|
||||||
var NOOP = require('../utils/NOOP');
|
var NOOP = require('../utils/NOOP');
|
||||||
var RequestAnimationFrame = require('../dom/RequestAnimationFrame');
|
var RequestAnimationFrame = require('../dom/RequestAnimationFrame');
|
||||||
|
|
||||||
var TickerLoop = function (game, framerate)
|
var Ticker = function (game, framerate)
|
||||||
{
|
{
|
||||||
this.game = game;
|
this.game = game;
|
||||||
|
|
||||||
|
@ -29,9 +29,9 @@ var TickerLoop = function (game, framerate)
|
||||||
this.useRAF = true;
|
this.useRAF = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
TickerLoop.prototype.constructor = TickerLoop;
|
Ticker.prototype.constructor = Ticker;
|
||||||
|
|
||||||
TickerLoop.prototype = {
|
Ticker.prototype = {
|
||||||
|
|
||||||
toString: function ()
|
toString: function ()
|
||||||
{
|
{
|
||||||
|
@ -157,4 +157,4 @@ TickerLoop.prototype = {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = TickerLoop;
|
module.exports = Ticker;
|
|
@ -11,7 +11,7 @@ var RequestAnimationFrame = require('../dom/RequestAnimationFrame');
|
||||||
// deltaHistory: 10
|
// deltaHistory: 10
|
||||||
// }
|
// }
|
||||||
|
|
||||||
var VariableTimeStep = function (game, config)
|
var TimeStep = function (game, config)
|
||||||
{
|
{
|
||||||
this.game = game;
|
this.game = game;
|
||||||
|
|
||||||
|
@ -32,6 +32,15 @@ var VariableTimeStep = function (game, config)
|
||||||
// 8.333 / 1000 = 0.008333 (120fps)
|
// 8.333 / 1000 = 0.008333 (120fps)
|
||||||
// 16.666 / 1000 = 0.01666 (60fps)
|
// 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.callback = NOOP;
|
||||||
|
|
||||||
this.forceSetTimeOut = GetValue(config, 'forceSetTimeOut', false);
|
this.forceSetTimeOut = GetValue(config, 'forceSetTimeOut', false);
|
||||||
|
@ -46,9 +55,9 @@ var VariableTimeStep = function (game, config)
|
||||||
this.deltaSmoothingMax = GetValue(config, 'deltaHistory', 10);
|
this.deltaSmoothingMax = GetValue(config, 'deltaHistory', 10);
|
||||||
};
|
};
|
||||||
|
|
||||||
VariableTimeStep.prototype.constructor = VariableTimeStep;
|
TimeStep.prototype.constructor = TimeStep;
|
||||||
|
|
||||||
VariableTimeStep.prototype = {
|
TimeStep.prototype = {
|
||||||
|
|
||||||
start: function (callback)
|
start: function (callback)
|
||||||
{
|
{
|
||||||
|
@ -65,7 +74,8 @@ VariableTimeStep.prototype = {
|
||||||
this.time = now;
|
this.time = now;
|
||||||
this.startTime = now;
|
this.startTime = now;
|
||||||
this.lastTime = now;
|
this.lastTime = now;
|
||||||
this.runOff = 0;
|
this.nextFpsUpdate = now + 1000;
|
||||||
|
this.framesThisSecond = 0;
|
||||||
|
|
||||||
// Pre-populate smoothing array
|
// Pre-populate smoothing array
|
||||||
|
|
||||||
|
@ -137,7 +147,26 @@ VariableTimeStep.prototype = {
|
||||||
// Real-world timer advance
|
// Real-world timer advance
|
||||||
this.time += avg;
|
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
|
// Shift time value over
|
||||||
this.lastTime = time;
|
this.lastTime = time;
|
||||||
|
@ -197,7 +226,6 @@ VariableTimeStep.prototype = {
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = VariableTimeStep;
|
module.exports = TimeStep;
|
Loading…
Reference in a new issue