mirror of
https://github.com/liabru/matter-js.git
synced 2025-01-15 16:38:43 -05:00
commit
e0e3164541
9 changed files with 471 additions and 127 deletions
|
@ -116,7 +116,6 @@ var compare = function(examples, isDev) {
|
|||
ticks += 1;
|
||||
|
||||
var demoBuildInstance = demoBuild.example.instance;
|
||||
runner.isFixed = demoBuildInstance.runner.isFixed = true;
|
||||
runner.delta = demoBuildInstance.runner.delta = 1000 / 60;
|
||||
|
||||
window.Matter = MatterBuild;
|
||||
|
|
|
@ -41,6 +41,22 @@ var demo = function(examples, isDev) {
|
|||
document.title = 'Matter.js Demo' + (isDev ? ' ・ Dev' : '');
|
||||
|
||||
if (isDev) {
|
||||
// add delta control
|
||||
Matter.Common.chainPathAfter(MatterTools, 'Gui.create', function() {
|
||||
this.datGui.__folders["Engine"]
|
||||
.add(demo, "delta", 0, 1000 / 55)
|
||||
.onChange(function() {
|
||||
var runner = demo.example.instance.runner;
|
||||
runner.delta = demo.delta;
|
||||
})
|
||||
.step(0.001)
|
||||
.listen();
|
||||
});
|
||||
|
||||
Matter.after('Runner.create', function() {
|
||||
demo.delta = this.delta;
|
||||
});
|
||||
|
||||
// add compare button
|
||||
var buttonSource = demo.dom.buttonSource,
|
||||
buttonCompare = buttonSource.cloneNode(true);
|
||||
|
|
|
@ -41,6 +41,7 @@ module.exports = {
|
|||
stress2: require('./stress2.js'),
|
||||
stress3: require('./stress3.js'),
|
||||
stress4: require('./stress4.js'),
|
||||
substep: require('./substep.js'),
|
||||
svg: require('./svg.js'),
|
||||
terrain: require('./terrain.js'),
|
||||
timescale: require('./timescale.js'),
|
||||
|
|
|
@ -34,9 +34,7 @@ Example.stress3 = function() {
|
|||
Render.run(render);
|
||||
|
||||
// create runner
|
||||
var runner = Runner.create({
|
||||
isFixed: true
|
||||
});
|
||||
var runner = Runner.create();
|
||||
Runner.run(runner, engine);
|
||||
|
||||
// add bodies
|
||||
|
|
|
@ -35,9 +35,7 @@ Example.stress4 = function() {
|
|||
Render.run(render);
|
||||
|
||||
// create runner
|
||||
var runner = Runner.create({
|
||||
isFixed: true
|
||||
});
|
||||
var runner = Runner.create();
|
||||
|
||||
Runner.run(runner, engine);
|
||||
|
||||
|
|
122
examples/substep.js
Normal file
122
examples/substep.js
Normal file
|
@ -0,0 +1,122 @@
|
|||
var Example = Example || {};
|
||||
|
||||
Example.substep = function() {
|
||||
var Engine = Matter.Engine,
|
||||
Render = Matter.Render,
|
||||
Runner = Matter.Runner,
|
||||
Events = Matter.Events,
|
||||
Composite = Matter.Composite,
|
||||
MouseConstraint = Matter.MouseConstraint,
|
||||
Mouse = Matter.Mouse,
|
||||
Bodies = Matter.Bodies;
|
||||
|
||||
// create engine
|
||||
var engine = Engine.create(),
|
||||
world = engine.world;
|
||||
|
||||
// create renderer
|
||||
var render = Render.create({
|
||||
element: document.body,
|
||||
engine: engine,
|
||||
options: {
|
||||
width: 800,
|
||||
height: 600,
|
||||
wireframes: false,
|
||||
showDebug: true,
|
||||
background: '#000',
|
||||
pixelRatio: 2
|
||||
}
|
||||
});
|
||||
|
||||
Render.run(render);
|
||||
|
||||
// create runner with higher precision timestep (requires >= v0.20.0 beta)
|
||||
var runner = Runner.create({
|
||||
// 600Hz delta = 1.666ms = 10upf @ 60fps (i.e. 10x default precision)
|
||||
delta: 1000 / (60 * 10),
|
||||
// 50fps minimum performance target (i.e. budget allows up to ~20ms execution per frame)
|
||||
maxFrameTime: 1000 / 50
|
||||
});
|
||||
|
||||
Runner.run(runner, engine);
|
||||
|
||||
// demo substepping using very thin bodies (than is typically recommended)
|
||||
Composite.add(world, [
|
||||
Bodies.rectangle(250, 250, 300, 1.25, {
|
||||
frictionAir: 0,
|
||||
friction: 0,
|
||||
restitution: 0.9,
|
||||
angle: 0.5,
|
||||
render: {
|
||||
lineWidth: 0.5,
|
||||
fillStyle: '#f55a3c'
|
||||
}
|
||||
}),
|
||||
Bodies.circle(200, 200, 2.25, {
|
||||
frictionAir: 0,
|
||||
friction: 0,
|
||||
restitution: 0.9,
|
||||
angle: 0.5,
|
||||
render: {
|
||||
fillStyle: '#fff'
|
||||
}
|
||||
})
|
||||
]);
|
||||
|
||||
// add bodies
|
||||
Composite.add(world, [
|
||||
Bodies.rectangle(400, 0, 800, 50, { isStatic: true, render: { visible: false } }),
|
||||
Bodies.rectangle(400, 600, 800, 50, { isStatic: true, render: { visible: false } }),
|
||||
Bodies.rectangle(800, 300, 50, 600, { isStatic: true, render: { visible: false } }),
|
||||
Bodies.rectangle(0, 300, 50, 600, { isStatic: true, render: { visible: false } })
|
||||
]);
|
||||
|
||||
// scene animation
|
||||
Events.on(engine, 'afterUpdate', function(event) {
|
||||
engine.gravity.scale = 0.00035;
|
||||
engine.gravity.x = Math.cos(engine.timing.timestamp * 0.0005);
|
||||
engine.gravity.y = Math.sin(engine.timing.timestamp * 0.0005);
|
||||
});
|
||||
|
||||
// add mouse control
|
||||
var mouse = Mouse.create(render.canvas),
|
||||
mouseConstraint = MouseConstraint.create(engine, {
|
||||
mouse: mouse,
|
||||
constraint: {
|
||||
stiffness: 0.2,
|
||||
render: {
|
||||
visible: false
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Composite.add(world, mouseConstraint);
|
||||
|
||||
// keep the mouse in sync with rendering
|
||||
render.mouse = mouse;
|
||||
|
||||
// fit the render viewport to the scene
|
||||
Render.lookAt(render, {
|
||||
min: { x: 0, y: 0 },
|
||||
max: { x: 800, y: 600 }
|
||||
});
|
||||
|
||||
// context for MatterTools.Demo
|
||||
return {
|
||||
engine: engine,
|
||||
runner: runner,
|
||||
render: render,
|
||||
canvas: render.canvas,
|
||||
stop: function() {
|
||||
Matter.Render.stop(render);
|
||||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
Example.substep.title = 'High Substeps (Low Delta)';
|
||||
Example.substep.for = '>=0.20.0';
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example.substep;
|
||||
}
|
|
@ -24,6 +24,8 @@ var Body = require('../body/Body');
|
|||
|
||||
(function() {
|
||||
|
||||
Engine._deltaMax = 1000 / 60;
|
||||
|
||||
/**
|
||||
* Creates a new engine. The options parameter is an object that specifies any properties you wish to override the defaults.
|
||||
* All properties have default values, and many are pre-calculated automatically based on other properties.
|
||||
|
@ -51,7 +53,8 @@ var Body = require('../body/Body');
|
|||
timestamp: 0,
|
||||
timeScale: 1,
|
||||
lastDelta: 0,
|
||||
lastElapsed: 0
|
||||
lastElapsed: 0,
|
||||
lastUpdatesPerFrame: 0
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -89,6 +92,13 @@ var Body = require('../body/Body');
|
|||
timestamp = timing.timestamp,
|
||||
i;
|
||||
|
||||
// warn if high delta
|
||||
if (delta > Engine._deltaMax) {
|
||||
Common.warnOnce(
|
||||
'Matter.Engine.update: delta argument is recommended to be less than or equal to', Engine._deltaMax.toFixed(3), 'ms.'
|
||||
);
|
||||
}
|
||||
|
||||
delta = typeof delta !== 'undefined' ? delta : Common._baseDelta;
|
||||
delta *= timing.timeScale;
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
/**
|
||||
* The `Matter.Runner` module is an optional utility which provides a game loop,
|
||||
* that handles continuously updating a `Matter.Engine` for you within a browser.
|
||||
* It is intended for development and debugging purposes, but may also be suitable for simple games.
|
||||
* If you are using your own game loop instead, then you do not need the `Matter.Runner` module.
|
||||
* Instead just call `Engine.update(engine, delta)` in your own loop.
|
||||
* The `Matter.Runner` module is an optional utility that provides a game loop for running a `Matter.Engine` inside a browser environment.
|
||||
* A runner will continuously update a `Matter.Engine` whilst synchronising engine updates with the browser frame rate.
|
||||
* This runner favours a smoother user experience over perfect time keeping.
|
||||
* This runner is optional and is used for development and debugging but could be useful as a starting point for implementing some games and experiences.
|
||||
* Alternatively see `Engine.update` to step the engine directly inside your own game loop implementation as may be needed inside other environments.
|
||||
*
|
||||
* See the included usage [examples](https://github.com/liabru/matter-js/tree/master/examples).
|
||||
*
|
||||
|
@ -20,73 +20,59 @@ var Common = require('./Common');
|
|||
|
||||
(function() {
|
||||
|
||||
var _requestAnimationFrame,
|
||||
_cancelAnimationFrame;
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
_requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame
|
||||
|| window.mozRequestAnimationFrame || window.msRequestAnimationFrame;
|
||||
|
||||
_cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame
|
||||
|| window.webkitCancelAnimationFrame || window.msCancelAnimationFrame;
|
||||
}
|
||||
|
||||
if (!_requestAnimationFrame) {
|
||||
var _frameTimeout;
|
||||
|
||||
_requestAnimationFrame = function(callback){
|
||||
_frameTimeout = setTimeout(function() {
|
||||
callback(Common.now());
|
||||
}, 1000 / 60);
|
||||
};
|
||||
|
||||
_cancelAnimationFrame = function() {
|
||||
clearTimeout(_frameTimeout);
|
||||
};
|
||||
}
|
||||
Runner._maxFrameDelta = 1000 / 15;
|
||||
Runner._frameDeltaFallback = 1000 / 60;
|
||||
Runner._timeBufferMargin = 1.5;
|
||||
Runner._elapsedNextEstimate = 1;
|
||||
Runner._smoothingLowerBound = 0.1;
|
||||
Runner._smoothingUpperBound = 0.9;
|
||||
|
||||
/**
|
||||
* Creates a new Runner. The options parameter is an object that specifies any properties you wish to override the defaults.
|
||||
* Creates a new Runner.
|
||||
* See the properties section below for detailed information on what you can pass via the `options` object.
|
||||
* @method create
|
||||
* @param {} options
|
||||
*/
|
||||
Runner.create = function(options) {
|
||||
var defaults = {
|
||||
fps: 60,
|
||||
deltaSampleSize: 60,
|
||||
counterTimestamp: 0,
|
||||
frameCounter: 0,
|
||||
deltaHistory: [],
|
||||
timePrev: null,
|
||||
delta: 1000 / 60,
|
||||
frameDelta: null,
|
||||
frameDeltaSmoothing: true,
|
||||
frameDeltaSnapping: true,
|
||||
frameDeltaHistory: [],
|
||||
frameDeltaHistorySize: 100,
|
||||
frameRequestId: null,
|
||||
isFixed: false,
|
||||
timeBuffer: 0,
|
||||
timeLastTick: null,
|
||||
maxUpdates: null,
|
||||
maxFrameTime: 1000 / 30,
|
||||
lastUpdatesDeferred: 0,
|
||||
enabled: true
|
||||
};
|
||||
|
||||
var runner = Common.extend(defaults, options);
|
||||
|
||||
runner.delta = runner.delta || 1000 / runner.fps;
|
||||
runner.deltaMin = runner.deltaMin || 1000 / runner.fps;
|
||||
runner.deltaMax = runner.deltaMax || 1000 / (runner.fps * 0.5);
|
||||
runner.fps = 1000 / runner.delta;
|
||||
// for temporary back compatibility only
|
||||
runner.fps = 0;
|
||||
|
||||
return runner;
|
||||
};
|
||||
|
||||
/**
|
||||
* Continuously ticks a `Matter.Engine` by calling `Runner.tick` on the `requestAnimationFrame` event.
|
||||
* Runs a `Matter.Engine` whilst synchronising engine updates with the browser frame rate.
|
||||
* See module and properties descriptions for more information on this runner.
|
||||
* Alternatively see `Engine.update` to step the engine directly inside your own game loop implementation.
|
||||
* @method run
|
||||
* @param {engine} engine
|
||||
* @param {runner} runner
|
||||
* @param {engine} [engine]
|
||||
* @return {runner} runner
|
||||
*/
|
||||
Runner.run = function(runner, engine) {
|
||||
// create runner if engine is first argument
|
||||
if (typeof runner.positionIterations !== 'undefined') {
|
||||
engine = runner;
|
||||
runner = Runner.create();
|
||||
}
|
||||
// initial time buffer for the first frame
|
||||
runner.timeBuffer = Runner._frameDeltaFallback;
|
||||
|
||||
(function run(time){
|
||||
runner.frameRequestId = _requestAnimationFrame(run);
|
||||
(function onFrame(time){
|
||||
runner.frameRequestId = Runner._onNextFrame(runner, onFrame);
|
||||
|
||||
if (time && runner.enabled) {
|
||||
Runner.tick(runner, engine, time);
|
||||
|
@ -97,84 +83,190 @@ var Common = require('./Common');
|
|||
};
|
||||
|
||||
/**
|
||||
* A game loop utility that updates the engine and renderer by one step (a 'tick').
|
||||
* Features delta smoothing, time correction and fixed or dynamic timing.
|
||||
* Consider just `Engine.update(engine, delta)` if you're using your own loop.
|
||||
* Performs a single runner tick as used inside `Runner.run`.
|
||||
* See module and properties descriptions for more information on this runner.
|
||||
* Alternatively see `Engine.update` to step the engine directly inside your own game loop implementation.
|
||||
* @method tick
|
||||
* @param {runner} runner
|
||||
* @param {engine} engine
|
||||
* @param {number} time
|
||||
*/
|
||||
Runner.tick = function(runner, engine, time) {
|
||||
var timing = engine.timing,
|
||||
delta;
|
||||
var tickStartTime = Common.now(),
|
||||
engineDelta = runner.delta,
|
||||
updateCount = 0;
|
||||
|
||||
if (runner.isFixed) {
|
||||
// fixed timestep
|
||||
delta = runner.delta;
|
||||
} else {
|
||||
// dynamic timestep based on wall clock between calls
|
||||
delta = (time - runner.timePrev) || runner.delta;
|
||||
runner.timePrev = time;
|
||||
// find frame delta time since last call
|
||||
var frameDelta = time - runner.timeLastTick;
|
||||
|
||||
// optimistically filter delta over a few frames, to improve stability
|
||||
runner.deltaHistory.push(delta);
|
||||
runner.deltaHistory = runner.deltaHistory.slice(-runner.deltaSampleSize);
|
||||
delta = Math.min.apply(null, runner.deltaHistory);
|
||||
|
||||
// limit delta
|
||||
delta = delta < runner.deltaMin ? runner.deltaMin : delta;
|
||||
delta = delta > runner.deltaMax ? runner.deltaMax : delta;
|
||||
|
||||
// update engine timing object
|
||||
runner.delta = delta;
|
||||
// fallback for unusable frame delta values (e.g. 0, NaN, on first frame or long pauses)
|
||||
if (!frameDelta || !runner.timeLastTick || frameDelta > Math.max(Runner._maxFrameDelta, runner.maxFrameTime)) {
|
||||
// reuse last accepted frame delta else fallback
|
||||
frameDelta = runner.frameDelta || Runner._frameDeltaFallback;
|
||||
}
|
||||
|
||||
// create an event object
|
||||
if (runner.frameDeltaSmoothing) {
|
||||
// record frame delta over a number of frames
|
||||
runner.frameDeltaHistory.push(frameDelta);
|
||||
runner.frameDeltaHistory = runner.frameDeltaHistory.slice(-runner.frameDeltaHistorySize);
|
||||
|
||||
// sort frame delta history
|
||||
var deltaHistorySorted = runner.frameDeltaHistory.slice(0).sort();
|
||||
|
||||
// sample a central window to limit outliers
|
||||
var deltaHistoryWindow = runner.frameDeltaHistory.slice(
|
||||
deltaHistorySorted.length * Runner._smoothingLowerBound,
|
||||
deltaHistorySorted.length * Runner._smoothingUpperBound
|
||||
);
|
||||
|
||||
// take the mean of the central window
|
||||
var frameDeltaSmoothed = _mean(deltaHistoryWindow);
|
||||
frameDelta = frameDeltaSmoothed || frameDelta;
|
||||
}
|
||||
|
||||
if (runner.frameDeltaSnapping) {
|
||||
// snap frame delta to the nearest 1 Hz
|
||||
frameDelta = 1000 / Math.round(1000 / frameDelta);
|
||||
}
|
||||
|
||||
// update runner values for next call
|
||||
runner.frameDelta = frameDelta;
|
||||
runner.timeLastTick = time;
|
||||
|
||||
// accumulate elapsed time
|
||||
runner.timeBuffer += runner.frameDelta;
|
||||
|
||||
// limit time buffer size to a single frame of updates
|
||||
runner.timeBuffer = Common.clamp(
|
||||
runner.timeBuffer, 0, runner.frameDelta + engineDelta * Runner._timeBufferMargin
|
||||
);
|
||||
|
||||
// reset count of over budget updates
|
||||
runner.lastUpdatesDeferred = 0;
|
||||
|
||||
// get max updates per frame
|
||||
var maxUpdates = runner.maxUpdates || Math.ceil(runner.maxFrameTime / engineDelta);
|
||||
|
||||
// create event object
|
||||
var event = {
|
||||
timestamp: timing.timestamp
|
||||
timestamp: engine.timing.timestamp
|
||||
};
|
||||
|
||||
// tick events before update
|
||||
Events.trigger(runner, 'beforeTick', event);
|
||||
|
||||
// fps counter
|
||||
runner.frameCounter += 1;
|
||||
if (time - runner.counterTimestamp >= 1000) {
|
||||
runner.fps = runner.frameCounter * ((time - runner.counterTimestamp) / 1000);
|
||||
runner.counterTimestamp = time;
|
||||
runner.frameCounter = 0;
|
||||
}
|
||||
|
||||
Events.trigger(runner, 'tick', event);
|
||||
|
||||
// update
|
||||
Events.trigger(runner, 'beforeUpdate', event);
|
||||
var updateStartTime = Common.now();
|
||||
|
||||
Engine.update(engine, delta);
|
||||
// simulate time elapsed between calls
|
||||
while (engineDelta > 0 && runner.timeBuffer >= engineDelta * Runner._timeBufferMargin) {
|
||||
// update the engine
|
||||
Events.trigger(runner, 'beforeUpdate', event);
|
||||
Engine.update(engine, engineDelta);
|
||||
Events.trigger(runner, 'afterUpdate', event);
|
||||
|
||||
Events.trigger(runner, 'afterUpdate', event);
|
||||
// consume time simulated from buffer
|
||||
runner.timeBuffer -= engineDelta;
|
||||
updateCount += 1;
|
||||
|
||||
// find elapsed time during this tick
|
||||
var elapsedTimeTotal = Common.now() - tickStartTime,
|
||||
elapsedTimeUpdates = Common.now() - updateStartTime,
|
||||
elapsedNextEstimate = elapsedTimeTotal + Runner._elapsedNextEstimate * elapsedTimeUpdates / updateCount;
|
||||
|
||||
// defer updates if over performance budgets for this frame
|
||||
if (updateCount >= maxUpdates || elapsedNextEstimate > runner.maxFrameTime) {
|
||||
runner.lastUpdatesDeferred = Math.round(Math.max(0, (runner.timeBuffer / engineDelta) - Runner._timeBufferMargin));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// track timing metrics
|
||||
engine.timing.lastUpdatesPerFrame = updateCount;
|
||||
|
||||
// tick events after update
|
||||
Events.trigger(runner, 'afterTick', event);
|
||||
|
||||
// show useful warnings if needed
|
||||
if (runner.frameDeltaHistory.length >= 100) {
|
||||
if (runner.lastUpdatesDeferred && Math.round(runner.frameDelta / engineDelta) > maxUpdates) {
|
||||
Common.warnOnce('Matter.Runner: runner reached runner.maxUpdates, see docs.');
|
||||
} else if (runner.lastUpdatesDeferred) {
|
||||
Common.warnOnce('Matter.Runner: runner reached runner.maxFrameTime, see docs.');
|
||||
}
|
||||
|
||||
if (typeof runner.isFixed !== 'undefined') {
|
||||
Common.warnOnce('Matter.Runner: runner.isFixed is now redundant, see docs.');
|
||||
}
|
||||
|
||||
if (runner.deltaMin || runner.deltaMax) {
|
||||
Common.warnOnce('Matter.Runner: runner.deltaMin and runner.deltaMax were removed, see docs.');
|
||||
}
|
||||
|
||||
if (runner.fps !== 0) {
|
||||
Common.warnOnce('Matter.Runner: runner.fps was replaced by runner.delta, see docs.');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Ends execution of `Runner.run` on the given `runner`, by canceling the animation frame request event loop.
|
||||
* If you wish to only temporarily pause the runner, see `runner.enabled` instead.
|
||||
* Ends execution of `Runner.run` on the given `runner` by canceling the frame loop.
|
||||
* Alternatively to temporarily pause the runner, see `runner.enabled`.
|
||||
* @method stop
|
||||
* @param {runner} runner
|
||||
*/
|
||||
Runner.stop = function(runner) {
|
||||
_cancelAnimationFrame(runner.frameRequestId);
|
||||
Runner._cancelNextFrame(runner);
|
||||
};
|
||||
|
||||
/**
|
||||
* Alias for `Runner.run`.
|
||||
* @method start
|
||||
* Schedules the `callback` on this `runner` for the next animation frame.
|
||||
* @private
|
||||
* @method _onNextFrame
|
||||
* @param {runner} runner
|
||||
* @param {engine} engine
|
||||
* @param {function} callback
|
||||
* @return {number} frameRequestId
|
||||
*/
|
||||
Runner.start = function(runner, engine) {
|
||||
Runner.run(runner, engine);
|
||||
Runner._onNextFrame = function(runner, callback) {
|
||||
if (typeof window !== 'undefined' && window.requestAnimationFrame) {
|
||||
runner.frameRequestId = window.requestAnimationFrame(callback);
|
||||
} else {
|
||||
throw new Error('Matter.Runner: missing required global window.requestAnimationFrame.');
|
||||
}
|
||||
|
||||
return runner.frameRequestId;
|
||||
};
|
||||
|
||||
/**
|
||||
* Cancels the last callback scheduled by `Runner._onNextFrame` on this `runner`.
|
||||
* @private
|
||||
* @method _cancelNextFrame
|
||||
* @param {runner} runner
|
||||
*/
|
||||
Runner._cancelNextFrame = function(runner) {
|
||||
if (typeof window !== 'undefined' && window.cancelAnimationFrame) {
|
||||
window.cancelAnimationFrame(runner.frameRequestId);
|
||||
} else {
|
||||
throw new Error('Matter.Runner: missing required global window.cancelAnimationFrame.');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the mean of the given numbers.
|
||||
* @method _mean
|
||||
* @private
|
||||
* @param {Number[]} values
|
||||
* @return {Number} the mean of given values.
|
||||
*/
|
||||
var _mean = function(values) {
|
||||
var result = 0,
|
||||
valuesLength = values.length;
|
||||
|
||||
for (var i = 0; i < valuesLength; i += 1) {
|
||||
result += values[i];
|
||||
}
|
||||
|
||||
return (result / valuesLength) || 0;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -184,7 +276,7 @@ var Common = require('./Common');
|
|||
*/
|
||||
|
||||
/**
|
||||
* Fired at the start of a tick, before any updates to the engine or timing
|
||||
* Fired once at the start of the browser frame, before any engine updates.
|
||||
*
|
||||
* @event beforeTick
|
||||
* @param {} event An event object
|
||||
|
@ -194,7 +286,7 @@ var Common = require('./Common');
|
|||
*/
|
||||
|
||||
/**
|
||||
* Fired after engine timing updated, but just before update
|
||||
* Fired once at the start of the browser frame, after `beforeTick`.
|
||||
*
|
||||
* @event tick
|
||||
* @param {} event An event object
|
||||
|
@ -204,7 +296,7 @@ var Common = require('./Common');
|
|||
*/
|
||||
|
||||
/**
|
||||
* Fired at the end of a tick, after engine update and after rendering
|
||||
* Fired once at the end of the browser frame, after `beforeTick`, `tick` and after any engine updates.
|
||||
*
|
||||
* @event afterTick
|
||||
* @param {} event An event object
|
||||
|
@ -214,7 +306,8 @@ var Common = require('./Common');
|
|||
*/
|
||||
|
||||
/**
|
||||
* Fired before update
|
||||
* Fired before each and every engine update in this browser frame (if any).
|
||||
* There may be multiple engine update calls per browser frame (or none) depending on framerate and timestep delta.
|
||||
*
|
||||
* @event beforeUpdate
|
||||
* @param {} event An event object
|
||||
|
@ -224,7 +317,8 @@ var Common = require('./Common');
|
|||
*/
|
||||
|
||||
/**
|
||||
* Fired after update
|
||||
* Fired after each and every engine update in this browser frame (if any).
|
||||
* There may be multiple engine update calls per browser frame (or none) depending on framerate and timestep delta.
|
||||
*
|
||||
* @event afterUpdate
|
||||
* @param {} event An event object
|
||||
|
@ -240,7 +334,31 @@ var Common = require('./Common');
|
|||
*/
|
||||
|
||||
/**
|
||||
* A flag that specifies whether the runner is running or not.
|
||||
* The fixed timestep size used for `Engine.update` calls in milliseconds, known as `delta`.
|
||||
*
|
||||
* This value is recommended to be `1000 / 60` ms or smaller (i.e. equivalent to at least 60hz).
|
||||
*
|
||||
* Smaller `delta` values provide higher quality results at the cost of performance.
|
||||
*
|
||||
* You should usually avoid changing `delta` during running, otherwise quality may be affected.
|
||||
*
|
||||
* For smoother frame pacing choose a `delta` that is an even multiple of each display FPS you target, i.e. `1000 / (n * fps)` as this helps distribute an equal number of updates over each display frame.
|
||||
*
|
||||
* For example with a 60 Hz `delta` i.e. `1000 / 60` the runner will on average perform one update per frame on displays running 60 FPS and one update every two frames on displays running 120 FPS, etc.
|
||||
*
|
||||
* Where as e.g. using a 240 Hz `delta` i.e. `1000 / 240` the runner will on average perform four updates per frame on displays running 60 FPS and two updates per frame on displays running 120 FPS, etc.
|
||||
*
|
||||
* Therefore `Runner.run` will call multiple engine updates (or none) as needed to simulate the time elapsed between browser frames.
|
||||
*
|
||||
* In practice the number of updates in any particular frame may be restricted to respect the runner's performance budgets. These are specified by `runner.maxFrameTime` and `runner.maxUpdates`, see those properties for details.
|
||||
*
|
||||
* @property delta
|
||||
* @type number
|
||||
* @default 1000 / 60
|
||||
*/
|
||||
|
||||
/**
|
||||
* A flag that can be toggled to enable or disable tick calls on this runner, therefore pausing engine updates and events while the runner loop remains running.
|
||||
*
|
||||
* @property enabled
|
||||
* @type boolean
|
||||
|
@ -248,23 +366,88 @@ var Common = require('./Common');
|
|||
*/
|
||||
|
||||
/**
|
||||
* A `Boolean` that specifies if the runner should use a fixed timestep (otherwise it is variable).
|
||||
* If timing is fixed, then the apparent simulation speed will change depending on the frame rate (but behaviour will be deterministic).
|
||||
* If the timing is variable, then the apparent simulation speed will be constant (approximately, but at the cost of determininism).
|
||||
* The accumulated time elapsed that has yet to be simulated in milliseconds.
|
||||
* This value is clamped within certain limits (see `Runner.tick` code).
|
||||
*
|
||||
* @property isFixed
|
||||
* @type boolean
|
||||
* @default false
|
||||
* @private
|
||||
* @property timeBuffer
|
||||
* @type number
|
||||
* @default 0
|
||||
*/
|
||||
|
||||
/**
|
||||
* A `Number` that specifies the time step between updates in milliseconds.
|
||||
* If `engine.timing.isFixed` is set to `true`, then `delta` is fixed.
|
||||
* If it is `false`, then `delta` can dynamically change to maintain the correct apparent simulation speed.
|
||||
* The measured time elapsed between the last two browser frames measured in milliseconds.
|
||||
* This is useful e.g. to estimate the current browser FPS using `1000 / runner.frameDelta`.
|
||||
*
|
||||
* @property delta
|
||||
* @readonly
|
||||
* @property frameDelta
|
||||
* @type number
|
||||
* @default 1000 / 60
|
||||
*/
|
||||
|
||||
/**
|
||||
* Enables averaging to smooth frame rate measurements and therefore stabilise play rate.
|
||||
*
|
||||
* @property frameDeltaSmoothing
|
||||
* @type boolean
|
||||
* @default true
|
||||
*/
|
||||
|
||||
/**
|
||||
* Rounds measured browser frame delta to the nearest 1 Hz.
|
||||
* This option can help smooth frame rate measurements and simplify handling hardware timing differences e.g. 59.94Hz and 60Hz displays.
|
||||
* For best results you should also round your `runner.delta` equivalent to the nearest 1 Hz.
|
||||
*
|
||||
* @property frameDeltaSnapping
|
||||
* @type boolean
|
||||
* @default true
|
||||
*/
|
||||
|
||||
/**
|
||||
* A performance budget that limits execution time allowed for this runner per browser frame in milliseconds.
|
||||
*
|
||||
* To calculate the effective browser FPS at which this throttle is applied use `1000 / runner.maxFrameTime`.
|
||||
*
|
||||
* This performance budget is intended to help maintain browser interactivity and help improve framerate recovery during temporary high CPU usage.
|
||||
*
|
||||
* This budget only covers the measured time elapsed executing the functions called in the scope of the runner tick, including `Engine.update` and its related user event callbacks.
|
||||
*
|
||||
* You may also reduce this budget to allow for any significant additional processing you perform on the same thread outside the scope of this runner tick, e.g. rendering time.
|
||||
*
|
||||
* See also `runner.maxUpdates`.
|
||||
*
|
||||
* @property maxFrameTime
|
||||
* @type number
|
||||
* @default 1000 / 30
|
||||
*/
|
||||
|
||||
/**
|
||||
* An optional limit for maximum engine update count allowed per frame tick in addition to `runner.maxFrameTime`.
|
||||
*
|
||||
* Unless you set a value it is automatically chosen based on `runner.delta` and `runner.maxFrameTime`.
|
||||
*
|
||||
* See also `runner.maxFrameTime`.
|
||||
*
|
||||
* @property maxUpdates
|
||||
* @type number
|
||||
* @default null
|
||||
*/
|
||||
|
||||
/**
|
||||
* The timestamp of the last call to `Runner.tick` used to measure `frameDelta`.
|
||||
*
|
||||
* @private
|
||||
* @property timeLastTick
|
||||
* @type number
|
||||
* @default 0
|
||||
*/
|
||||
|
||||
/**
|
||||
* The id of the last call to `Runner._onNextFrame`.
|
||||
*
|
||||
* @private
|
||||
* @property frameRequestId
|
||||
* @type number
|
||||
* @default null
|
||||
*/
|
||||
|
||||
})();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* The `Matter.Render` module is a simple canvas based renderer for visualising instances of `Matter.Engine`.
|
||||
* The `Matter.Render` module is a lightweight, optional utility which provides a simple canvas based renderer for visualising instances of `Matter.Engine`.
|
||||
* It is intended for development and debugging purposes, but may also be suitable for simple games.
|
||||
* It includes a number of drawing options including wireframe, vector with support for sprites and viewports.
|
||||
*
|
||||
|
@ -61,6 +61,7 @@ var Mouse = require('../core/Mouse');
|
|||
timestampElapsedHistory: [],
|
||||
engineDeltaHistory: [],
|
||||
engineElapsedHistory: [],
|
||||
engineUpdatesHistory: [],
|
||||
elapsedHistory: []
|
||||
},
|
||||
options: {
|
||||
|
@ -552,15 +553,19 @@ var Mouse = require('../core/Mouse');
|
|||
elapsedHistory = timing.elapsedHistory,
|
||||
timestampElapsedHistory = timing.timestampElapsedHistory,
|
||||
engineDeltaHistory = timing.engineDeltaHistory,
|
||||
engineUpdatesHistory = timing.engineUpdatesHistory,
|
||||
engineElapsedHistory = timing.engineElapsedHistory,
|
||||
lastEngineUpdatesPerFrame = engine.timing.lastUpdatesPerFrame,
|
||||
lastEngineDelta = engine.timing.lastDelta;
|
||||
|
||||
var deltaMean = _mean(deltaHistory),
|
||||
elapsedMean = _mean(elapsedHistory),
|
||||
engineDeltaMean = _mean(engineDeltaHistory),
|
||||
engineUpdatesMean = _mean(engineUpdatesHistory),
|
||||
engineElapsedMean = _mean(engineElapsedHistory),
|
||||
timestampElapsedMean = _mean(timestampElapsedHistory),
|
||||
rateMean = (timestampElapsedMean / deltaMean) || 0,
|
||||
neededUpdatesPerFrame = Math.round(deltaMean / lastEngineDelta),
|
||||
fps = (1000 / deltaMean) || 0;
|
||||
|
||||
var graphHeight = 4,
|
||||
|
@ -572,7 +577,7 @@ var Mouse = require('../core/Mouse');
|
|||
|
||||
// background
|
||||
context.fillStyle = '#0e0f19';
|
||||
context.fillRect(0, 50, gap * 4 + width * 5 + 22, height);
|
||||
context.fillRect(0, 50, gap * 5 + width * 6 + 22, height);
|
||||
|
||||
// show FPS
|
||||
Render.status(
|
||||
|
@ -590,17 +595,25 @@ var Mouse = require('../core/Mouse');
|
|||
function(i) { return (engineDeltaHistory[i] / engineDeltaMean) - 1; }
|
||||
);
|
||||
|
||||
// show engine updates per frame
|
||||
Render.status(
|
||||
context, x + (gap + width) * 2, y, width, graphHeight, engineUpdatesHistory.length,
|
||||
lastEngineUpdatesPerFrame + ' upf',
|
||||
Math.pow(Common.clamp((engineUpdatesMean / neededUpdatesPerFrame) || 1, 0, 1), 4),
|
||||
function(i) { return (engineUpdatesHistory[i] / engineUpdatesMean) - 1; }
|
||||
);
|
||||
|
||||
// show engine update time
|
||||
Render.status(
|
||||
context, x + (gap + width) * 2, y, width, graphHeight, engineElapsedHistory.length,
|
||||
context, x + (gap + width) * 3, y, width, graphHeight, engineElapsedHistory.length,
|
||||
engineElapsedMean.toFixed(2) + ' ut',
|
||||
1 - (engineElapsedMean / Render._goodFps),
|
||||
1 - (lastEngineUpdatesPerFrame * engineElapsedMean / Render._goodFps),
|
||||
function(i) { return (engineElapsedHistory[i] / engineElapsedMean) - 1; }
|
||||
);
|
||||
|
||||
// show render time
|
||||
Render.status(
|
||||
context, x + (gap + width) * 3, y, width, graphHeight, elapsedHistory.length,
|
||||
context, x + (gap + width) * 4, y, width, graphHeight, elapsedHistory.length,
|
||||
elapsedMean.toFixed(2) + ' rt',
|
||||
1 - (elapsedMean / Render._goodFps),
|
||||
function(i) { return (elapsedHistory[i] / elapsedMean) - 1; }
|
||||
|
@ -608,7 +621,7 @@ var Mouse = require('../core/Mouse');
|
|||
|
||||
// show effective speed
|
||||
Render.status(
|
||||
context, x + (gap + width) * 4, y, width, graphHeight, timestampElapsedHistory.length,
|
||||
context, x + (gap + width) * 5, y, width, graphHeight, timestampElapsedHistory.length,
|
||||
rateMean.toFixed(2) + ' x',
|
||||
rateMean * rateMean * rateMean,
|
||||
function(i) { return (((timestampElapsedHistory[i] / deltaHistory[i]) / rateMean) || 0) - 1; }
|
||||
|
@ -1433,6 +1446,9 @@ var Mouse = require('../core/Mouse');
|
|||
timing.timestampElapsedHistory.unshift(timing.timestampElapsed);
|
||||
timing.timestampElapsedHistory.length = Math.min(timing.timestampElapsedHistory.length, historySize);
|
||||
|
||||
timing.engineUpdatesHistory.unshift(engine.timing.lastUpdatesPerFrame);
|
||||
timing.engineUpdatesHistory.length = Math.min(timing.engineUpdatesHistory.length, historySize);
|
||||
|
||||
timing.engineElapsedHistory.unshift(engine.timing.lastElapsed);
|
||||
timing.engineElapsedHistory.length = Math.min(timing.engineElapsedHistory.length, historySize);
|
||||
|
||||
|
@ -1722,6 +1738,7 @@ var Mouse = require('../core/Mouse');
|
|||
*
|
||||
* - average render frequency (e.g. 60 fps)
|
||||
* - exact engine delta time used for last update (e.g. 16.66ms)
|
||||
* - average updates per frame (e.g. 1)
|
||||
* - average engine execution duration (e.g. 5.00ms)
|
||||
* - average render execution duration (e.g. 0.40ms)
|
||||
* - average effective play speed (e.g. '1.00x' is 'real-time')
|
||||
|
|
Loading…
Reference in a new issue