Skip to content

Commit

Permalink
added Runner.create and Runner.tick
Browse files Browse the repository at this point in the history
  • Loading branch information
liabru committed Jul 7, 2015
1 parent 1b7e27d commit 5c69f2e
Showing 1 changed file with 112 additions and 83 deletions.
195 changes: 112 additions & 83 deletions src/core/Runner.js
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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;

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;
}
engine.timing.frameRequestId = _requestAnimationFrame(render);

// time correction for time scaling
if (timeScalePrev !== 0)
correction *= timing.timeScale / timeScalePrev;
if (time && engine.enabled) {
Runner.tick(runner, engine, time);
}
})();

if (timing.timeScale === 0)
correction = 0;
return runner;
};

timeScalePrev = timing.timeScale;
/**
* 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);

// fps counter
frameCounter += 1;
if (time - counterTimestamp >= 1000) {
timing.fps = frameCounter * ((time - counterTimestamp) / 1000);
counterTimestamp = time;
frameCounter = 0;
}
// limit delta
delta = delta < timing.deltaMin ? timing.deltaMin : delta;
delta = delta > timing.deltaMax ? timing.deltaMax : delta;

Events.trigger(engine, 'tick', event);
// time runner.correction for delta
correction = delta / timing.delta;

// 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 timing object
timing.delta = delta;
}

// update
Engine.update(engine, delta, correction);
// time correction for time scaling
if (runner.timeScalePrev !== 0)
correction *= timing.timeScale / runner.timeScalePrev;

// render
if (engine.render) {
Engine.render(engine);
}
if (timing.timeScale === 0)
correction = 0;

Events.trigger(engine, 'afterTick', event);
})();
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);
};

/**
Expand Down

0 comments on commit 5c69f2e

Please sign in to comment.