UNPKG

spark-matter-js

Version:

A fork of the Matter.js 2D physics engine for Meta Spark

510 lines (432 loc) 16.1 kB
/** * The `Matter.Engine` module contains methods for creating and manipulating engines. * An engine is a controller that manages updating the simulation of the world. * * See the included usage [examples](https://github.com/liabru/matter-js/tree/master/examples). * * @class Engine */ var Engine = {}; module.exports = Engine; var Sleeping = require('./Sleeping'); var Resolver = require('../collision/Resolver'); var Detector = require('../collision/Detector'); var Pairs = require('../collision/Pairs'); var Events = require('./Events'); var Composite = require('../body/Composite'); var Constraint = require('../constraint/Constraint'); var Common = require('./Common'); var Body = require('../body/Body'); (function() { /** * 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. * See the properties section below for detailed information on what you can pass via the `options` object. * @method create * @param {object} [options] * @return {engine} engine */ Engine.create = function(options) { options = options || {}; var defaults = { positionIterations: 6, velocityIterations: 4, constraintIterations: 2, enableSleeping: false, events: [], plugin: {}, gravity: { x: 0, y: 1, scale: 0.001 }, timing: { timestamp: 0, timeScale: 1, lastDelta: 0, lastElapsed: 0 } }; var engine = Common.extend(defaults, options); engine.world = options.world || Composite.create({ label: 'World' }); engine.pairs = options.pairs || Pairs.create(); engine.detector = options.detector || Detector.create(); // for temporary back compatibility only engine.grid = { buckets: [] }; engine.world.gravity = engine.gravity; engine.broadphase = engine.grid; engine.metrics = {}; return engine; }; /** * Moves the simulation forward in time by `delta` ms. * The `correction` argument is an optional `Number` that specifies the time correction factor to apply to the update. * This can help improve the accuracy of the simulation in cases where `delta` is changing between updates. * The value of `correction` is defined as `delta / lastDelta`, i.e. the percentage change of `delta` over the last step. * Therefore the value is always `1` (no correction) when `delta` constant (or when no correction is desired, which is the default). * See the paper on <a href="http://lonesock.net/article/verlet.html">Time Corrected Verlet</a> for more information. * * Triggers `beforeUpdate` and `afterUpdate` events. * Triggers `collisionStart`, `collisionActive` and `collisionEnd` events. * @method update * @param {engine} engine * @param {number} [delta=16.666] * @param {number} [correction=1] */ Engine.update = function(engine, delta, correction) { var startTime = Common.now(); delta = delta || 1000 / 60; correction = correction || 1; var world = engine.world, detector = engine.detector, pairs = engine.pairs, timing = engine.timing, timestamp = timing.timestamp, i; // increment timestamp timing.timestamp += delta * timing.timeScale; timing.lastDelta = delta * timing.timeScale; // create an event object var event = { timestamp: timing.timestamp }; Events.trigger(engine, 'beforeUpdate', event); // get all bodies and all constraints in the world var allBodies = Composite.allBodies(world), allConstraints = Composite.allConstraints(world); // update the detector bodies if they have changed if (world.isModified) { Detector.setBodies(detector, allBodies); } // reset all composite modified flags if (world.isModified) { Composite.setModified(world, false, false, true); } // update sleeping if enabled if (engine.enableSleeping) Sleeping.update(allBodies, timing.timeScale); // apply gravity to all bodies Engine._bodiesApplyGravity(allBodies, engine.gravity); // update all body position and rotation by integration Engine._bodiesUpdate(allBodies, delta, timing.timeScale, correction, world.bounds); // update all constraints (first pass) Constraint.preSolveAll(allBodies); for (i = 0; i < engine.constraintIterations; i++) { Constraint.solveAll(allConstraints, timing.timeScale); } Constraint.postSolveAll(allBodies); // find all collisions detector.pairs = engine.pairs; var collisions = Detector.collisions(detector); // update collision pairs Pairs.update(pairs, collisions, timestamp); // wake up bodies involved in collisions if (engine.enableSleeping) Sleeping.afterCollisions(pairs.list, timing.timeScale); // trigger collision events if (pairs.collisionStart.length > 0) Events.trigger(engine, 'collisionStart', { pairs: pairs.collisionStart }); // iteratively resolve position between collisions Resolver.preSolvePosition(pairs.list); for (i = 0; i < engine.positionIterations; i++) { Resolver.solvePosition(pairs.list, timing.timeScale); } Resolver.postSolvePosition(allBodies); // update all constraints (second pass) Constraint.preSolveAll(allBodies); for (i = 0; i < engine.constraintIterations; i++) { Constraint.solveAll(allConstraints, timing.timeScale); } Constraint.postSolveAll(allBodies); // iteratively resolve velocity between collisions Resolver.preSolveVelocity(pairs.list); for (i = 0; i < engine.velocityIterations; i++) { Resolver.solveVelocity(pairs.list, timing.timeScale); } // trigger collision events if (pairs.collisionActive.length > 0) Events.trigger(engine, 'collisionActive', { pairs: pairs.collisionActive }); if (pairs.collisionEnd.length > 0) Events.trigger(engine, 'collisionEnd', { pairs: pairs.collisionEnd }); // clear force buffers Engine._bodiesClearForces(allBodies); Events.trigger(engine, 'afterUpdate', event); // log the time elapsed computing this update engine.timing.lastElapsed = Common.now() - startTime; return engine; }; /** * Merges two engines by keeping the configuration of `engineA` but replacing the world with the one from `engineB`. * @method merge * @param {engine} engineA * @param {engine} engineB */ Engine.merge = function(engineA, engineB) { Common.extend(engineA, engineB); if (engineB.world) { engineA.world = engineB.world; Engine.clear(engineA); var bodies = Composite.allBodies(engineA.world); for (var i = 0; i < bodies.length; i++) { var body = bodies[i]; Sleeping.set(body, false); body.id = Common.nextId(); } } }; /** * Clears the engine pairs and detector. * @method clear * @param {engine} engine */ Engine.clear = function(engine) { Pairs.clear(engine.pairs); Detector.clear(engine.detector); }; /** * Zeroes the `body.force` and `body.torque` force buffers. * @method _bodiesClearForces * @private * @param {body[]} bodies */ Engine._bodiesClearForces = function(bodies) { for (var i = 0; i < bodies.length; i++) { var body = bodies[i]; // reset force buffers body.force.x = 0; body.force.y = 0; body.torque = 0; } }; /** * Applys a mass dependant force to all given bodies. * @method _bodiesApplyGravity * @private * @param {body[]} bodies * @param {vector} gravity */ Engine._bodiesApplyGravity = function(bodies, gravity) { var gravityScale = typeof gravity.scale !== 'undefined' ? gravity.scale : 0.001; if ((gravity.x === 0 && gravity.y === 0) || gravityScale === 0) { return; } for (var i = 0; i < bodies.length; i++) { var body = bodies[i]; if (body.isStatic || body.isSleeping) continue; // apply gravity body.force.y += body.mass * gravity.y * gravityScale; body.force.x += body.mass * gravity.x * gravityScale; } }; /** * Applys `Body.update` to all given `bodies`. * @method _bodiesUpdate * @private * @param {body[]} bodies * @param {number} deltaTime * The amount of time elapsed between updates * @param {number} timeScale * @param {number} correction * The Verlet correction factor (deltaTime / lastDeltaTime) * @param {bounds} worldBounds */ Engine._bodiesUpdate = function(bodies, deltaTime, timeScale, correction, worldBounds) { for (var i = 0; i < bodies.length; i++) { var body = bodies[i]; if (body.isStatic || body.isSleeping) continue; Body.update(body, deltaTime, timeScale, correction); } }; /** * Fired just before an update * * @event beforeUpdate * @param {object} event An event object * @param {number} event.timestamp The engine.timing.timestamp of the event * @param {engine} event.source The source object of the event * @param {string} event.name The name of the event */ /** * Fired after engine update and all collision events * * @event afterUpdate * @param {object} event An event object * @param {number} event.timestamp The engine.timing.timestamp of the event * @param {engine} event.source The source object of the event * @param {string} event.name The name of the event */ /** * Fired after engine update, provides a list of all pairs that have started to collide in the current tick (if any) * * @event collisionStart * @param {object} event An event object * @param {pair[]} event.pairs List of affected pairs * @param {number} event.timestamp The engine.timing.timestamp of the event * @param {engine} event.source The source object of the event * @param {string} event.name The name of the event */ /** * Fired after engine update, provides a list of all pairs that are colliding in the current tick (if any) * * @event collisionActive * @param {object} event An event object * @param {pair[]} event.pairs List of affected pairs * @param {number} event.timestamp The engine.timing.timestamp of the event * @param {engine} event.source The source object of the event * @param {string} event.name The name of the event */ /** * Fired after engine update, provides a list of all pairs that have ended collision in the current tick (if any) * * @event collisionEnd * @param {object} event An event object * @param {pair[]} event.pairs List of affected pairs * @param {number} event.timestamp The engine.timing.timestamp of the event * @param {engine} event.source The source object of the event * @param {string} event.name The name of the event */ /* * * Properties Documentation * */ /** * An integer `Number` that specifies the number of position iterations to perform each update. * The higher the value, the higher quality the simulation will be at the expense of performance. * * @property positionIterations * @type number * @default 6 */ /** * An integer `Number` that specifies the number of velocity iterations to perform each update. * The higher the value, the higher quality the simulation will be at the expense of performance. * * @property velocityIterations * @type number * @default 4 */ /** * An integer `Number` that specifies the number of constraint iterations to perform each update. * The higher the value, the higher quality the simulation will be at the expense of performance. * The default value of `2` is usually very adequate. * * @property constraintIterations * @type number * @default 2 */ /** * A flag that specifies whether the engine should allow sleeping via the `Matter.Sleeping` module. * Sleeping can improve stability and performance, but often at the expense of accuracy. * * @property enableSleeping * @type boolean * @default false */ /** * An `Object` containing properties regarding the timing systems of the engine. * * @property timing * @type object */ /** * A `Number` that specifies the global scaling factor of time for all bodies. * A value of `0` freezes the simulation. * A value of `0.1` gives a slow-motion effect. * A value of `1.2` gives a speed-up effect. * * @property timing.timeScale * @type number * @default 1 */ /** * A `Number` that specifies the current simulation-time in milliseconds starting from `0`. * It is incremented on every `Engine.update` by the given `delta` argument. * * @property timing.timestamp * @type number * @default 0 */ /** * A `Number` that represents the total execution time elapsed during the last `Engine.update` in milliseconds. * It is updated by timing from the start of the last `Engine.update` call until it ends. * * This value will also include the total execution time of all event handlers directly or indirectly triggered by the engine update. * * @property timing.lastElapsed * @type number * @default 0 */ /** * A `Number` that represents the `delta` value used in the last engine update. * * @property timing.lastDelta * @type number * @default 0 */ /** * A `Matter.Detector` instance. * * @property detector * @type detector * @default a Matter.Detector instance */ /** * A `Matter.Grid` instance. * * @deprecated replaced by `engine.detector` * @property grid * @type grid * @default a Matter.Grid instance */ /** * Replaced by and now alias for `engine.grid`. * * @deprecated replaced by `engine.detector` * @property broadphase * @type grid * @default a Matter.Grid instance */ /** * The root `Matter.Composite` instance that will contain all bodies, constraints and other composites to be simulated by this engine. * * @property world * @type composite * @default a Matter.Composite instance */ /** * An object reserved for storing plugin-specific properties. * * @property plugin * @type {} */ /** * The gravity to apply on all bodies in `engine.world`. * * @property gravity * @type object */ /** * The gravity x component. * * @property gravity.x * @type object * @default 0 */ /** * The gravity y component. * * @property gravity.y * @type object * @default 1 */ /** * The gravity scale factor. * * @property gravity.scale * @type object * @default 0.001 */ })();