phaser/v3/src/boot/TimeStep.js

232 lines
5.9 KiB
JavaScript
Raw Normal View History

var NOOP = require('../utils/NOOP');
var GetValue = require('../utils/object/GetValue');
var RequestAnimationFrame = require('../dom/RequestAnimationFrame');
// Frame Rate config
// fps: {
// min: 10,
// target: 60,
// max: 120
// forceSetTimeOut: false,
// deltaHistory: 10
// }
var TimeStep = function (game, config)
{
this.game = game;
this.raf = new RequestAnimationFrame();
this.started = false;
this.running = false;
this.minFps = GetValue(config, 'min', 5);
this.maxFps = GetValue(config, 'max', 120);
this.targetFps = GetValue(config, 'target', 60);
this._min = 1000 / this.minFps; // 200ms between frames (i.e. super slow!)
this._max = 1000 / this.maxFps; // 8.333ms between frames (i.e. super fast, 120Hz displays)
this._target = 1000 / this.targetFps; // 16.666ms between frames (i.e. normal)
// 200 / 1000 = 0.2 (5fps)
// 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);
this.time = 0;
this.startTime = 0;
this.lastTime = 0;
this.delta = 0;
this.deltaIndex = 0;
this.deltaHistory = [];
this.deltaSmoothingMax = GetValue(config, 'deltaHistory', 10);
};
TimeStep.prototype.constructor = TimeStep;
TimeStep.prototype = {
start: function (callback)
{
if (this.started)
{
return this;
}
this.started = true;
this.running = true;
2017-04-28 02:14:30 +00:00
var now = window.performance.now();
this.time = now;
this.startTime = now;
this.lastTime = now;
this.nextFpsUpdate = now + 1000;
this.framesThisSecond = 0;
// Pre-populate smoothing array
var history = [];
for (var i = 0; i < this.deltaSmoothingMax; i++)
{
history[i] = this._target;
}
this.delta = 0;
this.deltaIndex = 0;
this.deltaHistory = history;
this.callback = callback;
this.raf.start(this.step.bind(this), this.forceSetTimeOut);
},
// time comes from requestAnimationFrame and is either a high res time value,
// or Date.now if using setTimeout
step: function (time)
{
2017-05-03 00:34:29 +00:00
var idx = this.deltaIndex;
var history = this.deltaHistory;
var max = this.deltaSmoothingMax;
// delta time (time is in ms)
var dt = (time - this.lastTime);
// min / max range (yes, the < and > should be this way around)
if (dt > this._min || dt < this._max)
{
// Probably super bad start time or browser tab context loss,
2017-05-03 00:34:29 +00:00
// so use the last 'sane' dt value
2017-05-03 01:21:32 +00:00
console.log('dt sync', dt, 'ms over', history[idx]);
2017-05-03 00:34:29 +00:00
dt = history[idx];
// Clamp delta to min max range (in case history has become corrupted somehow)
dt = Math.max(Math.min(dt, this._max), this._min);
2017-05-03 01:21:32 +00:00
}
// Smooth out the delta over the previous X frames
// add the delta to the smoothing array
history[idx] = dt;
// adjusts the delta history array index based on the smoothing count
// this stops the array growing beyond the size of deltaSmoothingMax
this.deltaIndex = (idx + 1) % max;
// average
var avg = 0;
2017-04-28 02:14:30 +00:00
// Loop the history array, adding the delta values together
for (var i = 0; i < max; i++)
{
avg += history[i];
}
// Then divide by the array length to get the average delta
avg /= max;
// Set as the world delta value
this.delta = avg;
// Real-world timer advance
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;
},
tick: function ()
{
2017-04-28 02:14:30 +00:00
this.step(window.performance.now());
},
sleep: function ()
{
if (this.running)
{
this.raf.stop();
this.running = false;
}
},
wake: function (seamless)
{
if (this.running)
{
this.sleep();
}
else if (seamless)
{
2017-04-28 02:14:30 +00:00
this.startTime += -this.lastTime + (this.lastTime = window.performance.now());
}
this.raf.start(this.step.bind(this), this.useRAF);
this.running = true;
2017-04-28 02:14:30 +00:00
this.step(window.performance.now());
},
setFps: function (value)
{
this.fps = value;
this.wake();
},
getFps: function ()
{
return this.fps;
},
stop: function ()
{
this.running = false;
this.started = false;
this.raf.stop();
return this;
}
};
module.exports = TimeStep;