mirror of
https://github.com/photonstorm/phaser
synced 2024-11-24 13:43:26 +00:00
249 lines
7.3 KiB
JavaScript
249 lines
7.3 KiB
JavaScript
/**
|
|
* @author Richard Davey <rich@photonstorm.com>
|
|
* @copyright 2016 Photon Storm Ltd.
|
|
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
|
|
*/
|
|
|
|
// My thanks to Isaac Sukin for creating MainLoop.js, on which lots of this is based.
|
|
|
|
Phaser.State.MainLoop = function (state, framerate)
|
|
{
|
|
/**
|
|
* @property {Phaser.State} state
|
|
*/
|
|
this.state = state;
|
|
|
|
/**
|
|
* @property {Phaser.Game} game - A reference to the currently running Game.
|
|
*/
|
|
this.game = state.game;
|
|
|
|
// The amount of time (in milliseconds) to simulate each time update() runs.
|
|
this.timestep = 1000 / framerate;
|
|
|
|
this.physicsStep = 1 / framerate;
|
|
|
|
// The cumulative amount of in-app time that hasn't been simulated yet.
|
|
// See the comments inside animate() for details.
|
|
this.frameDelta = 0;
|
|
|
|
// The timestamp in milliseconds of the last time the main loop was run.
|
|
// Used to compute the time elapsed between frames.
|
|
this.lastFrameTimeMs = 0;
|
|
|
|
// An exponential moving average of the frames per second.
|
|
this.fps = 60;
|
|
|
|
// The timestamp (in milliseconds) of the last time the `fps` moving
|
|
// average was updated.
|
|
this.lastFpsUpdate = 0;
|
|
|
|
// The number of frames delivered in the current second.
|
|
this.framesThisSecond = 0;
|
|
|
|
// The number of times update() is called in a given 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.
|
|
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).
|
|
this.minFrameDelay = 0;
|
|
|
|
// 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.
|
|
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.
|
|
this.panic = false;
|
|
};
|
|
|
|
Phaser.State.MainLoop.prototype.constructor = Phaser.State.MainLoop;
|
|
|
|
Phaser.State.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;
|
|
},
|
|
|
|
// timestamp = DOMHighResTimeStamp
|
|
step: function (timestamp)
|
|
{
|
|
// Throttle the frame rate (if minFrameDelay is set to a non-zero value by
|
|
// `MainLoop.setMaxAllowedFPS()`).
|
|
if (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;
|
|
|
|
// 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.
|
|
this.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;
|
|
|
|
var step = this.timestep;
|
|
|
|
while (this.frameDelta >= step)
|
|
{
|
|
// this.update(this.timestep);
|
|
|
|
this.state.sys.update(step, this.physicsStep);
|
|
|
|
for (var c = 0; c < this.state.sys.children.list.length; c++)
|
|
{
|
|
var child = this.state.sys.children.list[c];
|
|
|
|
if (child.exists)
|
|
{
|
|
child.update(step);
|
|
}
|
|
}
|
|
|
|
// Dev level callback
|
|
this.state.update(step);
|
|
|
|
this.frameDelta -= this.timestep;
|
|
|
|
if (++this.numUpdateSteps >= 240)
|
|
{
|
|
this.panic = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
this.state.sys.preRender();
|
|
|
|
this.state.sys.updates.start();
|
|
|
|
if (this.state.settings.visible && this.state.sys.color.alpha !== 0 && this.state.sys.children.list.length > 0)
|
|
{
|
|
this.game.renderer.render(this.state, this.frameDelta / this.timestep);
|
|
}
|
|
|
|
this.state.sys.updates.stop();
|
|
|
|
// Run any updates that are not dependent on time in the simulation.
|
|
this.state.sys.end(this.fps, this.panic);
|
|
|
|
this.panic = false;
|
|
},
|
|
|
|
/*
|
|
update: function (timestep)
|
|
{
|
|
this.state.sys.update(timestep);
|
|
|
|
var c;
|
|
var child;
|
|
|
|
for (var c = 0; c < this.state.sys.children.list.length; c++)
|
|
{
|
|
var child = this.state.sys.children.list[c];
|
|
|
|
if (child.exists)
|
|
{
|
|
child.update(timestep);
|
|
}
|
|
}
|
|
|
|
// Dev level callback
|
|
this.state.update(timestep);
|
|
|
|
for (c = 0; c < this.state.sys.children.list.length; c++)
|
|
{
|
|
var child = this.state.sys.children.list[c];
|
|
|
|
if (child.exists)
|
|
{
|
|
child.update(timestep);
|
|
}
|
|
}
|
|
},
|
|
*/
|
|
|
|
stop: function ()
|
|
{
|
|
this.running = false;
|
|
this.started = false;
|
|
|
|
return this;
|
|
}
|
|
|
|
};
|