mirror of
https://github.com/liabru/matter-js.git
synced 2025-01-15 16:38:43 -05:00
330 lines
10 KiB
JavaScript
330 lines
10 KiB
JavaScript
/**
|
|
* 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.
|
|
*
|
|
* See the included usage [examples](https://github.com/liabru/matter-js/tree/master/examples).
|
|
*
|
|
* @class Runner
|
|
*/
|
|
|
|
var Runner = {};
|
|
|
|
module.exports = Runner;
|
|
|
|
var Events = require('./Events');
|
|
var Engine = require('./Engine');
|
|
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);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 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 = {
|
|
fps: 60,
|
|
correction: 1,
|
|
deltaSampleSize: 60,
|
|
counterTimestamp: 0,
|
|
frameCounter: 0,
|
|
deltaHistory: [],
|
|
timePrev: null,
|
|
timeScalePrev: 1,
|
|
frameRequestId: null,
|
|
isFixed: false,
|
|
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;
|
|
|
|
return runner;
|
|
};
|
|
|
|
/**
|
|
* Continuously ticks a `Matter.Engine` by calling `Runner.tick` on the `requestAnimationFrame` event.
|
|
* @method run
|
|
* @param {engine} engine
|
|
*/
|
|
Runner.run = function(runner, engine) {
|
|
// create runner if engine is first argument
|
|
if (typeof runner.positionIterations !== 'undefined') {
|
|
engine = runner;
|
|
runner = Runner.create();
|
|
}
|
|
|
|
(function render(time){
|
|
runner.frameRequestId = _requestAnimationFrame(render);
|
|
|
|
if (time && runner.enabled) {
|
|
Runner.tick(runner, engine, time);
|
|
}
|
|
})();
|
|
|
|
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: timing.timestamp
|
|
};
|
|
|
|
Events.trigger(runner, 'beforeTick', event);
|
|
Events.trigger(engine, 'beforeTick', event); // @deprecated
|
|
|
|
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;
|
|
|
|
// 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;
|
|
|
|
// correction for delta
|
|
correction = delta / runner.delta;
|
|
|
|
// update engine timing object
|
|
runner.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;
|
|
runner.correction = correction;
|
|
|
|
// 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);
|
|
Events.trigger(engine, 'tick', event); // @deprecated
|
|
|
|
// 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); // @deprecated
|
|
}
|
|
|
|
// update
|
|
Events.trigger(runner, 'beforeUpdate', event);
|
|
Engine.update(engine, delta, correction);
|
|
Events.trigger(runner, 'afterUpdate', event);
|
|
|
|
// render
|
|
// @deprecated
|
|
if (engine.render && engine.render.controller) {
|
|
Events.trigger(runner, 'beforeRender', event);
|
|
Events.trigger(engine, 'beforeRender', event); // @deprecated
|
|
|
|
engine.render.controller.world(engine.render);
|
|
|
|
Events.trigger(runner, 'afterRender', event);
|
|
Events.trigger(engine, 'afterRender', event); // @deprecated
|
|
}
|
|
|
|
Events.trigger(runner, 'afterTick', event);
|
|
Events.trigger(engine, 'afterTick', event); // @deprecated
|
|
};
|
|
|
|
/**
|
|
* 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 {runner} runner
|
|
*/
|
|
Runner.stop = function(runner) {
|
|
_cancelAnimationFrame(runner.frameRequestId);
|
|
};
|
|
|
|
/**
|
|
* Alias for `Runner.run`.
|
|
* @method start
|
|
* @param {runner} runner
|
|
* @param {engine} engine
|
|
*/
|
|
Runner.start = function(runner, engine) {
|
|
Runner.run(runner, 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 {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
|
|
* @deprecated
|
|
*/
|
|
|
|
/**
|
|
* 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
|
|
* @deprecated
|
|
*/
|
|
|
|
/*
|
|
*
|
|
* 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 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).
|
|
*
|
|
* @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
|
|
*/
|
|
|
|
})();
|