From 5c69f2efd1a1c189b2e790dbf9486917b9839145 Mon Sep 17 00:00:00 2001 From: liabru Date: Tue, 7 Jul 2015 23:02:17 +0100 Subject: [PATCH] added Runner.create and Runner.tick --- src/core/Runner.js | 207 ++++++++++++++++++++++++++------------------- 1 file changed, 118 insertions(+), 89 deletions(-) diff --git a/src/core/Runner.js b/src/core/Runner.js index ab3bdfd..68465d6 100644 --- a/src/core/Runner.js +++ b/src/core/Runner.js @@ -1,6 +1,9 @@ /** * 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. +* It is intended for demo and testing purposes, but may be adequate 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. * 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) @@ -18,110 +21,136 @@ var Runner = {}; return; } - 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); }; + || function(callback){ window.setTimeout(function() { callback(Common.now()); }, 1000 / 60); }; 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. + * Creates a new Runner. The options parameter is an object that specifies any properties you wish to override the defaults. + * @method create + * @param {} options + */ + Runner.create = function(options) { + var defaults = { + deltaSampleSize: 60, + counterTimestamp: 0, + frameCounter: 0, + deltaHistory: [], + timePrev: null, + timeScalePrev: 1 + }; + + return Common.extend(defaults, options); + }; + + /** + * Continuously ticks a `Matter.Engine` by calling `Runner.tick` on the `requestAnimationFrame` event. * @method run * @param {engine} engine */ - Runner.run = function(engine) { - var counterTimestamp = 0, - frameCounter = 0, - deltaHistory = [], - timePrev, - timeScalePrev = 1; + Runner.run = function(runner, engine) { + // create runner if engine is first argument + if (typeof runner.positionIterations !== 'undefined') { + engine = runner; + runner = Runner.create(engine); + } (function render(time){ - var timing = engine.timing, - delta, - correction = 1; + engine.timing.frameRequestId = _requestAnimationFrame(render); - 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; + if (time && engine.enabled) { + Runner.tick(runner, engine, time); } - - // 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 - && engine.render.controller - && engine.render.controller.clear) { - engine.render.controller.clear(engine.render); - } - - // update - Engine.update(engine, delta, correction); - - // render - if (engine.render) { - Engine.render(engine); - } - - Events.trigger(engine, 'afterTick', event); })(); + + return runner; + }; + + /** + * 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. + * Triggers `beforeTick`, `tick` and `afterTick` events on the engine. + * Consider just `Engine.update(engine, delta)` if you're using your own loop. + * @method tick + * @param {runner} runner + * @param {engine} engine + * @param {number} time + */ + Runner.tick = function(runner, engine, time) { + var timing = engine.timing, + correction = 1, + delta; + + // 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 - runner.timePrev) || timing.delta; + runner.timePrev = time; + + // 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 < timing.deltaMin ? timing.deltaMin : delta; + delta = delta > timing.deltaMax ? timing.deltaMax : delta; + + // time runner.correction for delta + correction = delta / timing.delta; + + // update engine timing object + timing.delta = delta; + } + + // time correction for time scaling + if (runner.timeScalePrev !== 0) + correction *= timing.timeScale / runner.timeScalePrev; + + if (timing.timeScale === 0) + correction = 0; + + runner.timeScalePrev = timing.timeScale; + + // fps counter + runner.frameCounter += 1; + if (time - runner.counterTimestamp >= 1000) { + timing.fps = runner.frameCounter * ((time - runner.counterTimestamp) / 1000); + runner.counterTimestamp = time; + runner.frameCounter = 0; + } + + Events.trigger(engine, 'tick', event); + + // if world has been modified, clear the render scene graph + if (engine.world.isModified + && engine.render + && engine.render.controller + && engine.render.controller.clear) { + engine.render.controller.clear(engine.render); + } + + // update + Engine.update(engine, delta, correction); + + // render + if (engine.render) { + Engine.render(engine); + } + + Events.trigger(engine, 'afterTick', event); }; /**