UNPKG

phaser

Version:

A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers.

331 lines (278 loc) 10.9 kB
/** * 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 */ })();