diff --git a/src/core/Engine.js b/src/core/Engine.js index a6a3014..856e2a5 100644 --- a/src/core/Engine.js +++ b/src/core/Engine.js @@ -1,6 +1,6 @@ /** * The `Matter.Engine` module contains methods for creating and manipulating engines. -* An engine is a controller that manages updating and rendering the simulation of the world. +* An engine is a controller that manages updating the simulation of the world. * See `Matter.Runner` for an optional game loop utility. * * See [Demo.js](https://github.com/liabru/matter-js/blob/master/demo/js/Demo.js) @@ -13,9 +13,6 @@ var Engine = {}; (function() { - var _fps = 60, - _delta = 1000 / _fps; - /** * 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. @@ -32,22 +29,14 @@ var Engine = {}; element = Common.isElement(element) ? element : null; var defaults = { - enabled: true, positionIterations: 6, velocityIterations: 4, constraintIterations: 2, enableSleeping: false, events: [], timing: { - fps: _fps, timestamp: 0, - delta: _delta, - correction: 1, - deltaMin: 1000 / _fps, - deltaMax: 1000 / (_fps * 0.5), - timeScale: 1, - isFixed: false, - frameRequestId: 0 + timeScale: 1 }, broadphase: { controller: Grid @@ -82,7 +71,13 @@ var Engine = {}; }; /** - * Moves the simulation forward in time by `delta` ms. + * Moves the simulation forward in time by `delta` ms. + * The `correction` argument is an optional `Number` that specifies the time correction factor to apply to the update. + * This can help improve the accuracy of the simulation in cases where `delta` is changing between updates. + * The value of `correction` is defined as `delta / lastDelta`, i.e. the percentage change of `delta` over the last step. + * Therefore the value is always `1` (no correction) when `delta` constant (or when no correction is desired, which is the default). + * See the paper on Time Corrected Verlet for more information. + * * Triggers `beforeUpdate` and `afterUpdate` events. * Triggers `collisionStart`, `collisionActive` and `collisionEnd` events. * @method update @@ -101,11 +96,10 @@ var Engine = {}; // increment timestamp timing.timestamp += delta * timing.timeScale; - timing.correction = correction; // create an event object var event = { - timestamp: engine.timing.timestamp + timestamp: timing.timestamp }; Events.trigger(engine, 'beforeUpdate', event); @@ -204,22 +198,6 @@ var Engine = {}; return engine; }; - - /** - * Renders the world by calling its defined renderer `engine.render.controller`. Triggers `beforeRender` and `afterRender` events. - * @method render - * @param {engine} engine - */ - Engine.render = function(engine) { - // create an event object - var event = { - timestamp: engine.timing.timestamp - }; - - Events.trigger(engine, 'beforeRender', event); - engine.render.controller.world(engine); - Events.trigger(engine, 'afterRender', event); - }; /** * Merges two engines by keeping the configuration of `engineA` but replacing the world with the one from `engineB`. @@ -329,38 +307,12 @@ var Engine = {}; * @param {engine} engine */ - /* - * - * Events Documentation - * - */ - - /** - * Fired at the start of a tick, before any updates to the engine or timing - * - * @event beforeTick - * @param {} event An event object - * @param {DOMHighResTimeStamp} event.timestamp The timestamp of the current tick - * @param {} event.source The source object of the event - * @param {} event.name The name of the event - */ - - /** - * Fired after engine timing updated, but just before engine state updated - * - * @event tick - * @param {} event An event object - * @param {DOMHighResTimeStamp} event.timestamp The timestamp of the current tick - * @param {} event.source The source object of the event - * @param {} event.name The name of the event - */ - /** * Fired just before an update * * @event beforeUpdate * @param {} event An event object - * @param {DOMHighResTimeStamp} event.timestamp The timestamp of the current tick + * @param {number} event.timestamp The engine.timing.timestamp of the event * @param {} event.source The source object of the event * @param {} event.name The name of the event */ @@ -370,37 +322,7 @@ var Engine = {}; * * @event afterUpdate * @param {} event An event object - * @param {DOMHighResTimeStamp} event.timestamp The timestamp of the current tick - * @param {} event.source The source object of the event - * @param {} event.name The name of the event - */ - - /** - * Fired just before rendering - * - * @event beforeRender - * @param {} event An event object - * @param {DOMHighResTimeStamp} event.timestamp The timestamp of the current tick - * @param {} event.source The source object of the event - * @param {} event.name The name of the event - */ - - /** - * Fired after rendering - * - * @event afterRender - * @param {} event An event object - * @param {DOMHighResTimeStamp} event.timestamp The timestamp of the current tick - * @param {} event.source The source object of the event - * @param {} event.name The name of the event - */ - - /** - * Fired after engine update and after rendering - * - * @event afterTick - * @param {} event An event object - * @param {DOMHighResTimeStamp} event.timestamp The timestamp of the current tick + * @param {number} event.timestamp The engine.timing.timestamp of the event * @param {} event.source The source object of the event * @param {} event.name The name of the event */ @@ -411,7 +333,7 @@ var Engine = {}; * @event collisionStart * @param {} event An event object * @param {} event.pairs List of affected pairs - * @param {DOMHighResTimeStamp} event.timestamp The timestamp of the current tick + * @param {number} event.timestamp The engine.timing.timestamp of the event * @param {} event.source The source object of the event * @param {} event.name The name of the event */ @@ -422,7 +344,7 @@ var Engine = {}; * @event collisionActive * @param {} event An event object * @param {} event.pairs List of affected pairs - * @param {DOMHighResTimeStamp} event.timestamp The timestamp of the current tick + * @param {number} event.timestamp The engine.timing.timestamp of the event * @param {} event.source The source object of the event * @param {} event.name The name of the event */ @@ -433,7 +355,7 @@ var Engine = {}; * @event collisionEnd * @param {} event An event object * @param {} event.pairs List of affected pairs - * @param {DOMHighResTimeStamp} event.timestamp The timestamp of the current tick + * @param {number} event.timestamp The engine.timing.timestamp of the event * @param {} event.source The source object of the event * @param {} event.name The name of the event */ @@ -444,14 +366,6 @@ var Engine = {}; * */ - /** - * A flag that specifies whether the engine is running or not. - * - * @property enabled - * @type boolean - * @default true - */ - /** * An integer `Number` that specifies the number of position iterations to perform each update. * The higher the value, the higher quality the simulation will be at the expense of performance. @@ -509,46 +423,13 @@ var Engine = {}; /** * A `Number` that specifies the current simulation-time in milliseconds starting from `0`. - * It is incremented on every `Engine.update` by the `timing.delta`. + * It is incremented on every `Engine.update` by the given `delta` argument. * * @property timing.timestamp * @type number * @default 0 */ - /** - * A `Boolean` that specifies if the `Engine.run` game loop 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). - * - * @property timing.isFixed - * @type boolean - * @default false - */ - - /** - * 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. - * - * @property timing.delta - * @type number - * @default 1000 / 60 - */ - - /** - * A `Number` that specifies the time correction factor to apply to the current timestep. - * It is automatically handled when using `Engine.run`, but is also only optional even if you use your own game loop. - * The value is defined as `delta / lastDelta`, i.e. the percentage change of `delta` between steps. - * This value is always `1` (no correction) when frame rate is constant or `engine.timing.isFixed` is `true`. - * If the framerate and hence `delta` are changing, then correction should be applied to the current update to account for the change. - * See the paper on Time Corrected Verlet for more information. - * - * @property timing.correction - * @type number - * @default 1 - */ - /** * An instance of a `Render` controller. The default value is a `Matter.Render` instance created by `Engine.create`. * One may also develop a custom renderer module based on `Matter.Render` and pass an instance of it to `Engine.create` via `options.render`. diff --git a/src/core/Runner.js b/src/core/Runner.js index 68465d6..ad4091c 100644 --- a/src/core/Runner.js +++ b/src/core/Runner.js @@ -35,15 +35,26 @@ var Runner = {}; */ Runner.create = function(options) { var defaults = { + fps: 60, + correction: 1, deltaSampleSize: 60, counterTimestamp: 0, frameCounter: 0, deltaHistory: [], timePrev: null, - timeScalePrev: 1 + timeScalePrev: 1, + frameRequestId: null, + isFixed: false, + enabled: true }; - return Common.extend(defaults, options); + var runner = Common.extend(defaults, options); + + runner.delta = 1000 / runner.fps; + runner.deltaMin = 1000 / runner.fps; + runner.deltaMax = 1000 / (runner.fps * 0.5); + + return runner; }; /** @@ -55,13 +66,13 @@ var Runner = {}; // create runner if engine is first argument if (typeof runner.positionIterations !== 'undefined') { engine = runner; - runner = Runner.create(engine); + runner = Runner.create(); } (function render(time){ - engine.timing.frameRequestId = _requestAnimationFrame(render); + runner.frameRequestId = _requestAnimationFrame(render); - if (time && engine.enabled) { + if (time && runner.enabled) { Runner.tick(runner, engine, time); } })(); @@ -86,17 +97,18 @@ var Runner = {}; // create an event object var event = { - timestamp: time + timestamp: timing.timestamp }; - Events.trigger(engine, 'beforeTick', event); + Events.trigger(runner, 'beforeTick', event); + Events.trigger(engine, 'beforeTick', event); // @deprecated - if (timing.isFixed) { + if (runner.isFixed) { // fixed timestep - delta = timing.delta; + delta = runner.delta; } else { // dynamic timestep based on wall clock between calls - delta = (time - runner.timePrev) || timing.delta; + delta = (time - runner.timePrev) || runner.delta; runner.timePrev = time; // optimistically filter delta over a few frames, to improve stability @@ -105,14 +117,14 @@ var Runner = {}; delta = Math.min.apply(null, runner.deltaHistory); // limit delta - delta = delta < timing.deltaMin ? timing.deltaMin : delta; - delta = delta > timing.deltaMax ? timing.deltaMax : delta; + delta = delta < runner.deltaMin ? runner.deltaMin : delta; + delta = delta > runner.deltaMax ? runner.deltaMax : delta; - // time runner.correction for delta - correction = delta / timing.delta; + // correction for delta + correction = delta / runner.delta; // update engine timing object - timing.delta = delta; + runner.delta = delta; } // time correction for time scaling @@ -127,12 +139,13 @@ var Runner = {}; // fps counter runner.frameCounter += 1; if (time - runner.counterTimestamp >= 1000) { - timing.fps = runner.frameCounter * ((time - runner.counterTimestamp) / 1000); + runner.fps = runner.frameCounter * ((time - runner.counterTimestamp) / 1000); runner.counterTimestamp = time; runner.frameCounter = 0; } - Events.trigger(engine, 'tick', event); + Events.trigger(runner, 'tick', event); + Events.trigger(engine, 'tick', event); // @deprecated // if world has been modified, clear the render scene graph if (engine.world.isModified @@ -143,24 +156,143 @@ var Runner = {}; } // update + Events.trigger(runner, 'beforeUpdate', event); Engine.update(engine, delta, correction); + Events.trigger(runner, 'afterUpdate', event); // render if (engine.render) { - Engine.render(engine); + Events.trigger(runner, 'beforeRender', event); + Events.trigger(engine, 'beforeRender', event); // @deprecated + + engine.render.controller.world(engine); + + Events.trigger(runner, 'afterRender', event); + Events.trigger(engine, 'afterRender', event); // @deprecated } - Events.trigger(engine, 'afterTick', event); + Events.trigger(runner, 'afterTick', event); + Events.trigger(engine, 'afterTick', event); // @deprecated }; /** - * Ends execution of `Runner.run` on the given `engine`, by canceling the animation frame request event loop. + * 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 engine, see `engine.enabled` instead. * @method stop - * @param {engine} engine + * @param {runner} runner */ - Runner.stop = function(engine) { - _cancelAnimationFrame(engine.timing.frameRequestId); + Runner.stop = function(runner) { + _cancelAnimationFrame(runner.frameRequestId); }; + /* + * + * Events Documentation + * + */ + + /** + * Fired at the start of a tick, before any updates to the engine or timing + * + * @event beforeTick + * @param {} event An event object + * @param {number} event.timestamp The engine.timing.timestamp of the event + * @param {} event.source The source object of the event + * @param {} event.name The name of the event + */ + + /** + * Fired after engine timing updated, but just before update + * + * @event tick + * @param {} event An event object + * @param {number} event.timestamp The engine.timing.timestamp of the event + * @param {} event.source The source object of the event + * @param {} event.name The name of the event + */ + + /** + * Fired at the end of a tick, after engine update and after rendering + * + * @event afterTick + * @param {} event An event object + * @param {number} event.timestamp The engine.timing.timestamp of the event + * @param {} event.source The source object of the event + * @param {} event.name The name of the event + */ + + /** + * Fired before update + * + * @event beforeUpdate + * @param {} event An event object + * @param {number} event.timestamp The engine.timing.timestamp of the event + * @param {} event.source The source object of the event + * @param {} event.name The name of the event + */ + + /** + * Fired after update + * + * @event afterUpdate + * @param {} event An event object + * @param {number} event.timestamp The engine.timing.timestamp of the event + * @param {} event.source The source object of the event + * @param {} event.name The name of the event + */ + + /** + * Fired before rendering + * + * @event beforeRender + * @param {} event An event object + * @param {number} event.timestamp The engine.timing.timestamp of the event + * @param {} event.source The source object of the event + * @param {} event.name The name of the event + */ + + /** + * Fired after rendering + * + * @event afterRender + * @param {} event An event object + * @param {number} event.timestamp The engine.timing.timestamp of the event + * @param {} event.source The source object of the event + * @param {} event.name The name of the event + */ + + /* + * + * Properties Documentation + * + */ + + /** + * A flag that specifies whether the runner is running or not. + * + * @property enabled + * @type boolean + * @default true + */ + + /** + * A `Boolean` that specifies if the `Engine.run` game loop 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). + * + * @property isFixed + * @type boolean + * @default false + */ + + /** + * 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. + * + * @property delta + * @type number + * @default 1000 / 60 + */ + })();