diff --git a/src/core/Engine.js b/src/core/Engine.js index 1fe46717..2c2c1a28 100644 --- a/src/core/Engine.js +++ b/src/core/Engine.js @@ -1,6 +1,7 @@ /** * 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. +* See `Matter.Runner` for an optional game loop utility. * * See [Demo.js](https://github.com/liabru/matter-js/blob/master/demo/js/Demo.js) * and [DemoMobile.js](https://github.com/liabru/matter-js/blob/master/demo/js/DemoMobile.js) for usage examples. @@ -13,13 +14,8 @@ var Engine = {}; (function() { var _fps = 60, - _deltaSampleSize = _fps, _delta = 1000 / _fps; - - var _requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame - || window.mozRequestAnimationFrame || window.msRequestAnimationFrame - || function(callback){ window.setTimeout(function() { callback(Common.now()); }, _delta); }; - + /** * 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. @@ -50,7 +46,8 @@ var Engine = {}; deltaMin: 1000 / _fps, deltaMax: 1000 / (_fps * 0.5), timeScale: 1, - isFixed: false + isFixed: false, + frameRequestId: 0 }, render: { element: element, @@ -72,95 +69,6 @@ var Engine = {}; return engine; }; - /** - * An optional utility function that provides a game loop, that handles updating the engine for you. - * Calls `Engine.update` and `Engine.render` on the `requestAnimationFrame` event automatically. - * Handles time correction and non-fixed dynamic timing (if enabled). - * Triggers `beforeTick`, `tick` and `afterTick` events. - * @method run - * @param {engine} engine - */ - Engine.run = function(engine) { - var counterTimestamp = 0, - frameCounter = 0, - deltaHistory = [], - timePrev, - timeScalePrev = 1; - - (function render(time){ - _requestAnimationFrame(render); - - if (!engine.enabled) - return; - - var timing = engine.timing, - delta, - correction; - - // create an event object - var event = { - timestamp: time - }; - - Events.trigger(engine, 'beforeTick', event); - - if (timing.isFixed) { - // fixed timestep - delta = timing.delta; - } else { - // dynamic timestep based on wall clock between calls - delta = (time - timePrev) || timing.delta; - timePrev = time; - - // optimistically filter delta over a few frames, to improve stability - deltaHistory.push(delta); - deltaHistory = deltaHistory.slice(-_deltaSampleSize); - delta = Math.min.apply(null, deltaHistory); - - // limit delta - delta = delta < timing.deltaMin ? timing.deltaMin : delta; - delta = delta > timing.deltaMax ? timing.deltaMax : delta; - - // time correction for delta - correction = delta / timing.delta; - - // update engine timing object - timing.delta = delta; - } - - // time correction for time scaling - if (timeScalePrev !== 0) - correction *= timing.timeScale / timeScalePrev; - - if (timing.timeScale === 0) - correction = 0; - - timeScalePrev = timing.timeScale; - - // fps counter - frameCounter += 1; - if (time - counterTimestamp >= 1000) { - timing.fps = frameCounter * ((time - counterTimestamp) / 1000); - counterTimestamp = time; - frameCounter = 0; - } - - Events.trigger(engine, 'tick', event); - - // if world has been modified, clear the render scene graph - if (engine.world.isModified) - engine.render.controller.clear(engine.render); - - // update - Engine.update(engine, delta, correction); - - // render - Engine.render(engine); - - Events.trigger(engine, 'afterTick', event); - })(); - }; - /** * Moves the simulation forward in time by `delta` ms. * Triggers `beforeUpdate` and `afterUpdate` events. @@ -339,6 +247,12 @@ var Engine = {}; } }; + /** + * An alias for Runner.run, see `Matter.Runner` for more information. + * @method run + * @param {engine} engine + */ + /* * * Events Documentation diff --git a/src/core/Runner.js b/src/core/Runner.js new file mode 100644 index 00000000..84306736 --- /dev/null +++ b/src/core/Runner.js @@ -0,0 +1,126 @@ +/** +* The `Matter.Runner` module is an optional utility which provides a game loop, +* that handles updating and rendering a `Matter.Engine` for you within a browser. +* Note that the method `Engine.run` is an alias for `Runner.run`. +* +* See [Demo.js](https://github.com/liabru/matter-js/blob/master/demo/js/Demo.js) +* and [DemoMobile.js](https://github.com/liabru/matter-js/blob/master/demo/js/DemoMobile.js) for usage examples. +* +* @class Runner +*/ + +var Runner = {}; + +(function() { + + var _fps = 60, + _deltaSampleSize = _fps, + _delta = 1000 / _fps; + + var _requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame + || window.mozRequestAnimationFrame || window.msRequestAnimationFrame + || function(callback){ window.setTimeout(function() { callback(Common.now()); }, _delta); }; + + var _cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame + || window.webkitCancelAnimationFrame || window.msCancelAnimationFrame; + + /** + * Provides a basic game loop that handles updating the engine for you. + * Calls `Engine.update` and `Engine.render` on the `requestAnimationFrame` event automatically. + * Handles time correction and non-fixed dynamic timing (if enabled). + * Triggers `beforeTick`, `tick` and `afterTick` events. + * @method run + * @param {engine} engine + */ + Runner.run = function(engine) { + var counterTimestamp = 0, + frameCounter = 0, + deltaHistory = [], + timePrev, + timeScalePrev = 1; + + (function render(time){ + var timing = engine.timing, + delta, + correction; + + timing.frameRequestId = _requestAnimationFrame(render); + + if (!engine.enabled) + return; + + // create an event object + var event = { + timestamp: time + }; + + Events.trigger(engine, 'beforeTick', event); + + if (timing.isFixed) { + // fixed timestep + delta = timing.delta; + } else { + // dynamic timestep based on wall clock between calls + delta = (time - timePrev) || timing.delta; + timePrev = time; + + // optimistically filter delta over a few frames, to improve stability + deltaHistory.push(delta); + deltaHistory = deltaHistory.slice(-_deltaSampleSize); + delta = Math.min.apply(null, deltaHistory); + + // limit delta + delta = delta < timing.deltaMin ? timing.deltaMin : delta; + delta = delta > timing.deltaMax ? timing.deltaMax : delta; + + // time correction for delta + correction = delta / timing.delta; + + // update engine timing object + timing.delta = delta; + } + + // time correction for time scaling + if (timeScalePrev !== 0) + correction *= timing.timeScale / timeScalePrev; + + if (timing.timeScale === 0) + correction = 0; + + timeScalePrev = timing.timeScale; + + // fps counter + frameCounter += 1; + if (time - counterTimestamp >= 1000) { + timing.fps = frameCounter * ((time - counterTimestamp) / 1000); + counterTimestamp = time; + frameCounter = 0; + } + + Events.trigger(engine, 'tick', event); + + // if world has been modified, clear the render scene graph + if (engine.world.isModified) + engine.render.controller.clear(engine.render); + + // update + Engine.update(engine, delta, correction); + + // render + Engine.render(engine); + + Events.trigger(engine, 'afterTick', event); + })(); + }; + + /** + * Ends execution of `Runner.run` on the given `engine`, 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 + */ + Runner.stop = function(engine) { + _cancelAnimationFrame(engine.timing.frameRequestId); + }; + +})(); \ No newline at end of file diff --git a/src/module/Outro.js b/src/module/Outro.js index c695f600..86466848 100644 --- a/src/module/Outro.js +++ b/src/module/Outro.js @@ -7,6 +7,8 @@ World.addBody = Composite.addBody; World.addConstraint = Composite.addConstraint; World.clear = Composite.clear; +Engine.run = Runner.run; + // exports Matter.Body = Body; @@ -36,6 +38,7 @@ Matter.Render = Render; Matter.RenderPixi = RenderPixi; Matter.Events = Events; Matter.Query = Query; +Matter.Runner = Runner; // CommonJS module if (typeof exports !== 'undefined') {