0
0
Fork 0
mirror of https://github.com/liabru/matter-js.git synced 2024-11-30 10:20:52 -05:00

Added readonly body.deltaTime

Added delta property to engine update event
Added delta argument to various internal functions
Changed timeScale argument to use delta instead on various internal functions
Fixed issues when using an engine update delta of 0
Improved time independence for friction, air friction, restitution, sleeping, collisions, constraints
Removed optional correction argument from Engine.update
Removed correction and timeScale from Body.update and Matter.Runner
This commit is contained in:
liabru 2019-09-01 12:21:16 +01:00
parent 2ec247b7af
commit 0784a5b5df
9 changed files with 114 additions and 107 deletions

View file

@ -15,13 +15,13 @@ module.exports = Body;
var Vertices = require('../geometry/Vertices'); var Vertices = require('../geometry/Vertices');
var Vector = require('../geometry/Vector'); var Vector = require('../geometry/Vector');
var Sleeping = require('../core/Sleeping'); var Sleeping = require('../core/Sleeping');
var Render = require('../render/Render');
var Common = require('../core/Common'); var Common = require('../core/Common');
var Bounds = require('../geometry/Bounds'); var Bounds = require('../geometry/Bounds');
var Axes = require('../geometry/Axes'); var Axes = require('../geometry/Axes');
(function() { (function() {
Body._timeCorrection = true;
Body._inertiaScale = 4; Body._inertiaScale = 4;
Body._nextCollidingGroupId = 1; Body._nextCollidingGroupId = 1;
Body._nextNonCollidingGroupId = -1; Body._nextNonCollidingGroupId = -1;
@ -95,6 +95,7 @@ var Axes = require('../geometry/Axes');
area: 0, area: 0,
mass: 0, mass: 0,
inertia: 0, inertia: 0,
deltaTime: null,
_original: null _original: null
}; };
@ -462,8 +463,8 @@ var Axes = require('../geometry/Axes');
*/ */
Body.setPosition = function(body, position) { Body.setPosition = function(body, position) {
var delta = Vector.sub(position, body.position); var delta = Vector.sub(position, body.position);
body.positionPrev.x += delta.x; body.positionPrev.x += delta.x;
body.positionPrev.y += delta.y; body.positionPrev.y += delta.y;
for (var i = 0; i < body.parts.length; i++) { for (var i = 0; i < body.parts.length; i++) {
var part = body.parts[i]; var part = body.parts[i];
@ -482,7 +483,7 @@ var Axes = require('../geometry/Axes');
*/ */
Body.setAngle = function(body, angle) { Body.setAngle = function(body, angle) {
var delta = angle - body.angle; var delta = angle - body.angle;
body.anglePrev += delta; body.anglePrev += delta;
for (var i = 0; i < body.parts.length; i++) { for (var i = 0; i < body.parts.length; i++) {
var part = body.parts[i]; var part = body.parts[i];
@ -625,26 +626,28 @@ var Axes = require('../geometry/Axes');
* Performs a simulation step for the given `body`, including updating position and angle using Verlet integration. * Performs a simulation step for the given `body`, including updating position and angle using Verlet integration.
* @method update * @method update
* @param {body} body * @param {body} body
* @param {number} deltaTime * @param {number} [deltaTime=16.666]
* @param {number} timeScale
* @param {number} correction
*/ */
Body.update = function(body, deltaTime, timeScale, correction) { Body.update = function(body, deltaTime) {
var deltaTimeSquared = Math.pow(deltaTime * timeScale * body.timeScale, 2); deltaTime = (typeof deltaTime !== 'undefined' ? deltaTime : Common._timeUnit) * body.timeScale;
var deltaTimeSquared = deltaTime * deltaTime,
correction = Body._timeCorrection ? deltaTime / (body.deltaTime || deltaTime) : 1;
// from the previous step // from the previous step
var frictionAir = 1 - body.frictionAir * timeScale * body.timeScale, var frictionAir = 1 - body.frictionAir * (deltaTime / Common._timeUnit),
velocityPrevX = body.position.x - body.positionPrev.x, velocityPrevX = (body.position.x - body.positionPrev.x) * correction,
velocityPrevY = body.position.y - body.positionPrev.y; velocityPrevY = (body.position.y - body.positionPrev.y) * correction;
// update velocity with Verlet integration // update velocity with Verlet integration
body.velocity.x = (velocityPrevX * frictionAir * correction) + (body.force.x / body.mass) * deltaTimeSquared; body.velocity.x = (velocityPrevX * frictionAir) + (body.force.x / body.mass) * deltaTimeSquared;
body.velocity.y = (velocityPrevY * frictionAir * correction) + (body.force.y / body.mass) * deltaTimeSquared; body.velocity.y = (velocityPrevY * frictionAir) + (body.force.y / body.mass) * deltaTimeSquared;
body.positionPrev.x = body.position.x; body.positionPrev.x = body.position.x;
body.positionPrev.y = body.position.y; body.positionPrev.y = body.position.y;
body.position.x += body.velocity.x; body.position.x += body.velocity.x;
body.position.y += body.velocity.y; body.position.y += body.velocity.y;
body.deltaTime = deltaTime;
// update angular velocity with Verlet integration // update angular velocity with Verlet integration
body.angularVelocity = ((body.angle - body.anglePrev) * frictionAir * correction) + (body.torque / body.inertia) * deltaTimeSquared; body.angularVelocity = ((body.angle - body.anglePrev) * frictionAir * correction) + (body.torque / body.inertia) * deltaTimeSquared;
@ -880,7 +883,7 @@ var Axes = require('../geometry/Axes');
/** /**
* A `Vector` that _measures_ the current velocity of the body after the last `Body.update`. It is read-only. * A `Vector` that _measures_ the current velocity of the body after the last `Body.update`. It is read-only.
* If you need to modify a body's velocity directly, you should either apply a force or simply change the body's `position` (as the engine uses position-Verlet integration). * If you need to modify a body's velocity directly, you should either apply a force or simply change the body's `position` (as the engine uses position-Verlet integration).
* *
* @readOnly * @readOnly
* @property velocity * @property velocity
* @type vector * @type vector
@ -1109,6 +1112,16 @@ var Axes = require('../geometry/Axes');
* @default 1 * @default 1
*/ */
/**
* A `Number` that records the last delta time value used to update this body.
* This is automatically updated by the engine inside of `Body.update`.
*
* @readOnly
* @property deltaTime
* @type number
* @default null
*/
/** /**
* An `Object` that defines the rendering properties to be consumed by the module `Matter.Render`. * An `Object` that defines the rendering properties to be consumed by the module `Matter.Render`.
* *

View file

@ -21,9 +21,10 @@ var Bounds = require('../geometry/Bounds');
* @method collisions * @method collisions
* @param {pair[]} broadphasePairs * @param {pair[]} broadphasePairs
* @param {engine} engine * @param {engine} engine
* @param {number} delta
* @return {array} collisions * @return {array} collisions
*/ */
Detector.collisions = function(broadphasePairs, engine) { Detector.collisions = function(broadphasePairs, engine, delta) {
var collisions = [], var collisions = [],
pairsTable = engine.pairs.table; pairsTable = engine.pairs.table;
@ -66,7 +67,7 @@ var Bounds = require('../geometry/Bounds');
} }
// narrow phase // narrow phase
var collision = SAT.collides(partA, partB, previousCollision); var collision = SAT.collides(partA, partB, previousCollision, delta);
// @if DEBUG // @if DEBUG
metrics.narrowphaseTests += 1; metrics.narrowphaseTests += 1;

View file

@ -49,9 +49,9 @@ var Bounds = require('../geometry/Bounds');
* @method solvePosition * @method solvePosition
* @param {pair[]} pairs * @param {pair[]} pairs
* @param {body[]} bodies * @param {body[]} bodies
* @param {number} timeScale * @param {number} delta
*/ */
Resolver.solvePosition = function(pairs, bodies, timeScale) { Resolver.solvePosition = function(pairs, bodies, delta) {
var i, var i,
normalX, normalX,
normalY, normalY,
@ -68,7 +68,8 @@ var Bounds = require('../geometry/Bounds');
bodyBtoAX, bodyBtoAX,
bodyBtoAY, bodyBtoAY,
positionImpulse, positionImpulse,
impulseCoefficient = timeScale * Resolver._positionDampen; timeScale = delta / Common._timeUnit,
impulseCoefficient = Resolver._positionDampen * timeScale;
for (i = 0; i < bodies.length; i++) { for (i = 0; i < bodies.length; i++) {
var body = bodies[i]; var body = bodies[i];
@ -231,10 +232,12 @@ var Bounds = require('../geometry/Bounds');
* Find a solution for pair velocities. * Find a solution for pair velocities.
* @method solveVelocity * @method solveVelocity
* @param {pair[]} pairs * @param {pair[]} pairs
* @param {number} timeScale * @param {number} delta
*/ */
Resolver.solveVelocity = function(pairs, timeScale) { Resolver.solveVelocity = function(pairs, delta) {
var timeScaleSquared = timeScale * timeScale, var timeScale = delta / Common._timeUnit,
timeScale2 = timeScale * timeScale,
timeScale3 = timeScale2 * timeScale,
impulse = Vector._temp[0], impulse = Vector._temp[0],
tempA = Vector._temp[1], tempA = Vector._temp[1],
tempB = Vector._temp[2], tempB = Vector._temp[2],
@ -287,10 +290,10 @@ var Bounds = require('../geometry/Bounds');
var tangentImpulse = tangentVelocity, var tangentImpulse = tangentVelocity,
maxFriction = Infinity; maxFriction = Infinity;
if (tangentSpeed > pair.friction * pair.frictionStatic * normalForce * timeScaleSquared) { if (tangentSpeed > pair.friction * pair.frictionStatic * normalForce * timeScale3) {
maxFriction = tangentSpeed; maxFriction = tangentSpeed * timeScale;
tangentImpulse = Common.clamp( tangentImpulse = Common.clamp(
pair.friction * tangentVelocityDirection * timeScaleSquared, pair.friction * tangentVelocityDirection * timeScale3,
-maxFriction, maxFriction -maxFriction, maxFriction
); );
} }
@ -304,7 +307,7 @@ var Bounds = require('../geometry/Bounds');
tangentImpulse *= share; tangentImpulse *= share;
// handle high velocity and resting collisions separately // handle high velocity and resting collisions separately
if (normalVelocity < 0 && normalVelocity * normalVelocity > Resolver._restingThresh * timeScaleSquared) { if (normalVelocity < 0 && normalVelocity * normalVelocity > Resolver._restingThresh * timeScale2) {
// high normal velocity so clear cached contact normal impulse // high normal velocity so clear cached contact normal impulse
contact.normalImpulse = 0; contact.normalImpulse = 0;
} else { } else {
@ -316,7 +319,7 @@ var Bounds = require('../geometry/Bounds');
} }
// handle high velocity and resting collisions separately // handle high velocity and resting collisions separately
if (tangentVelocity * tangentVelocity > Resolver._restingThreshTangent * timeScaleSquared) { if (tangentVelocity * tangentVelocity > Resolver._restingThreshTangent * timeScale2) {
// high tangent velocity so clear cached contact tangent impulse // high tangent velocity so clear cached contact tangent impulse
contact.tangentImpulse = 0; contact.tangentImpulse = 0;
} else { } else {

View file

@ -12,23 +12,30 @@ module.exports = SAT;
var Vertices = require('../geometry/Vertices'); var Vertices = require('../geometry/Vertices');
var Vector = require('../geometry/Vector'); var Vector = require('../geometry/Vector');
var Common = require('../core/Common');
(function() { (function() {
SAT._reuseMotionThresh = 0.2;
/** /**
* Detect collision between two bodies using the Separating Axis Theorem. * Detect collision between two bodies using the Separating Axis Theorem.
* @method collides * @method collides
* @param {body} bodyA * @param {body} bodyA
* @param {body} bodyB * @param {body} bodyB
* @param {collision} previousCollision * @param {collision} previousCollision
* @param {number} [delta=0]
* @return {collision} collision * @return {collision} collision
*/ */
SAT.collides = function(bodyA, bodyB, previousCollision) { SAT.collides = function(bodyA, bodyB, previousCollision, delta) {
var overlapAB, var overlapAB,
overlapBA, overlapBA,
minOverlap, minOverlap,
collision, collision,
canReusePrevCol = false; canReusePrevCol = false,
timeScale = delta / Common._timeUnit;
delta = typeof delta !== 'undefined' ? delta : 0;
if (previousCollision) { if (previousCollision) {
// estimate total motion // estimate total motion
@ -39,7 +46,7 @@ var Vector = require('../geometry/Vector');
// we may be able to (partially) reuse collision result // we may be able to (partially) reuse collision result
// but only safe if collision was resting // but only safe if collision was resting
canReusePrevCol = previousCollision && previousCollision.collided && motion < 0.2; canReusePrevCol = previousCollision && previousCollision.collided && motion < SAT._reuseMotionThresh * timeScale * timeScale;
// reuse collision object // reuse collision object
collision = previousCollision; collision = previousCollision;

View file

@ -110,9 +110,11 @@ var Common = require('../core/Common');
* @private * @private
* @method solveAll * @method solveAll
* @param {constraint[]} constraints * @param {constraint[]} constraints
* @param {number} timeScale * @param {number} delta
*/ */
Constraint.solveAll = function(constraints, timeScale) { Constraint.solveAll = function(constraints, delta) {
var timeScale = Common.clamp(delta / Common._timeUnit, 0, 1);
// Solve fixed constraints first. // Solve fixed constraints first.
for (var i = 0; i < constraints.length; i += 1) { for (var i = 0; i < constraints.length; i += 1) {
var constraint = constraints[i], var constraint = constraints[i],
@ -183,7 +185,9 @@ var Common = require('../core/Common');
// solve distance constraint with Gauss-Siedel method // solve distance constraint with Gauss-Siedel method
var difference = (currentLength - constraint.length) / currentLength, var difference = (currentLength - constraint.length) / currentLength,
stiffness = constraint.stiffness < 1 ? constraint.stiffness * timeScale : constraint.stiffness, isRigid = constraint.stiffness >= 1 || constraint.length === 0,
stiffness = isRigid ? constraint.stiffness : constraint.stiffness * timeScale * timeScale,
damping = constraint.damping * timeScale,
force = Vector.mult(delta, difference * stiffness), force = Vector.mult(delta, difference * stiffness),
massTotal = (bodyA ? bodyA.inverseMass : 0) + (bodyB ? bodyB.inverseMass : 0), massTotal = (bodyA ? bodyA.inverseMass : 0) + (bodyB ? bodyB.inverseMass : 0),
inertiaTotal = (bodyA ? bodyA.inverseInertia : 0) + (bodyB ? bodyB.inverseInertia : 0), inertiaTotal = (bodyA ? bodyA.inverseInertia : 0) + (bodyB ? bodyB.inverseInertia : 0),
@ -193,8 +197,8 @@ var Common = require('../core/Common');
normal, normal,
normalVelocity, normalVelocity,
relativeVelocity; relativeVelocity;
if (constraint.damping) { if (damping > 0) {
var zero = Vector.create(); var zero = Vector.create();
normal = Vector.div(delta, currentLength); normal = Vector.div(delta, currentLength);
@ -218,9 +222,9 @@ var Common = require('../core/Common');
bodyA.position.y -= force.y * share; bodyA.position.y -= force.y * share;
// apply damping // apply damping
if (constraint.damping) { if (damping > 0) {
bodyA.positionPrev.x -= constraint.damping * normal.x * normalVelocity * share; bodyA.positionPrev.x -= damping * normal.x * normalVelocity * share;
bodyA.positionPrev.y -= constraint.damping * normal.y * normalVelocity * share; bodyA.positionPrev.y -= damping * normal.y * normalVelocity * share;
} }
// apply torque // apply torque
@ -241,9 +245,9 @@ var Common = require('../core/Common');
bodyB.position.y += force.y * share; bodyB.position.y += force.y * share;
// apply damping // apply damping
if (constraint.damping) { if (damping > 0) {
bodyB.positionPrev.x += constraint.damping * normal.x * normalVelocity * share; bodyB.positionPrev.x += damping * normal.x * normalVelocity * share;
bodyB.positionPrev.y += constraint.damping * normal.y * normalVelocity * share; bodyB.positionPrev.y += damping * normal.y * normalVelocity * share;
} }
// apply torque // apply torque

View file

@ -10,6 +10,7 @@ module.exports = Common;
(function() { (function() {
Common._timeUnit = 1000 / 60;
Common._nextId = 0; Common._nextId = 0;
Common._seed = 0; Common._seed = 0;
Common._nowStartTime = +(new Date()); Common._nowStartTime = +(new Date());

View file

@ -97,35 +97,29 @@ var Body = require('../body/Body');
/** /**
* Moves the simulation forward in time by `delta` ms. * 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 `beforeUpdate` and `afterUpdate` events.
* Triggers `collisionStart`, `collisionActive` and `collisionEnd` events. * Triggers `collisionStart`, `collisionActive` and `collisionEnd` events.
* @method update * @method update
* @param {engine} engine * @param {engine} engine
* @param {number} [delta=16.666] * @param {number} [delta=16.666]
* @param {number} [correction=1]
*/ */
Engine.update = function(engine, delta, correction) { Engine.update = function(engine, delta) {
delta = delta || 1000 / 60;
correction = correction || 1;
var world = engine.world, var world = engine.world,
timing = engine.timing, timing = engine.timing,
broadphase = engine.broadphase, broadphase = engine.broadphase,
broadphasePairs = [], broadphasePairs,
i; i;
delta = typeof delta !== 'undefined' ? delta : Common._timeUnit;
delta *= timing.timeScale;
// increment timestamp // increment timestamp
timing.timestamp += delta * timing.timeScale; timing.timestamp += delta;
// create an event object // create an event object
var event = { var event = {
timestamp: timing.timestamp timestamp: timing.timestamp,
delta: delta
}; };
Events.trigger(engine, 'beforeUpdate', event); Events.trigger(engine, 'beforeUpdate', event);
@ -141,18 +135,20 @@ var Body = require('../body/Body');
// if sleeping enabled, call the sleeping controller // if sleeping enabled, call the sleeping controller
if (engine.enableSleeping) if (engine.enableSleeping)
Sleeping.update(allBodies, timing.timeScale); Sleeping.update(allBodies, delta);
// applies gravity to all bodies // applies gravity to all bodies
Engine._bodiesApplyGravity(allBodies, world.gravity); Engine._bodiesApplyGravity(allBodies, world.gravity);
// update all body position and rotation by integration // update all body position and rotation by integration
Engine._bodiesUpdate(allBodies, delta, timing.timeScale, correction, world.bounds); if (delta > 0) {
Engine._bodiesUpdate(allBodies, delta);
}
// update all constraints (first pass) // update all constraints (first pass)
Constraint.preSolveAll(allBodies); Constraint.preSolveAll(allBodies);
for (i = 0; i < engine.constraintIterations; i++) { for (i = 0; i < engine.constraintIterations; i++) {
Constraint.solveAll(allConstraints, timing.timeScale); Constraint.solveAll(allConstraints, delta);
} }
Constraint.postSolveAll(allBodies); Constraint.postSolveAll(allBodies);
@ -176,7 +172,7 @@ var Body = require('../body/Body');
} }
// narrowphase pass: find actual collisions, then create or update collision pairs // narrowphase pass: find actual collisions, then create or update collision pairs
var collisions = broadphase.detector(broadphasePairs, engine); var collisions = broadphase.detector(broadphasePairs, engine, delta);
// update collision pairs // update collision pairs
var pairs = engine.pairs, var pairs = engine.pairs,
@ -186,7 +182,7 @@ var Body = require('../body/Body');
// wake up bodies involved in collisions // wake up bodies involved in collisions
if (engine.enableSleeping) if (engine.enableSleeping)
Sleeping.afterCollisions(pairs.list, timing.timeScale); Sleeping.afterCollisions(pairs.list, delta);
// trigger collision events // trigger collision events
if (pairs.collisionStart.length > 0) if (pairs.collisionStart.length > 0)
@ -195,21 +191,21 @@ var Body = require('../body/Body');
// iteratively resolve position between collisions // iteratively resolve position between collisions
Resolver.preSolvePosition(pairs.list); Resolver.preSolvePosition(pairs.list);
for (i = 0; i < engine.positionIterations; i++) { for (i = 0; i < engine.positionIterations; i++) {
Resolver.solvePosition(pairs.list, allBodies, timing.timeScale); Resolver.solvePosition(pairs.list, allBodies, delta);
} }
Resolver.postSolvePosition(allBodies); Resolver.postSolvePosition(allBodies);
// update all constraints (second pass) // update all constraints (second pass)
Constraint.preSolveAll(allBodies); Constraint.preSolveAll(allBodies);
for (i = 0; i < engine.constraintIterations; i++) { for (i = 0; i < engine.constraintIterations; i++) {
Constraint.solveAll(allConstraints, timing.timeScale); Constraint.solveAll(allConstraints, delta);
} }
Constraint.postSolveAll(allBodies); Constraint.postSolveAll(allBodies);
// iteratively resolve velocity between collisions // iteratively resolve velocity between collisions
Resolver.preSolveVelocity(pairs.list); Resolver.preSolveVelocity(pairs.list);
for (i = 0; i < engine.velocityIterations; i++) { for (i = 0; i < engine.velocityIterations; i++) {
Resolver.solveVelocity(pairs.list, timing.timeScale); Resolver.solveVelocity(pairs.list, delta);
} }
// trigger collision events // trigger collision events
@ -322,21 +318,16 @@ var Body = require('../body/Body');
* @method _bodiesUpdate * @method _bodiesUpdate
* @private * @private
* @param {body[]} bodies * @param {body[]} bodies
* @param {number} deltaTime * @param {number} delta The amount of time elapsed between updates
* 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) { Engine._bodiesUpdate = function(bodies, delta) {
for (var i = 0; i < bodies.length; i++) { for (var i = 0; i < bodies.length; i++) {
var body = bodies[i]; var body = bodies[i];
if (body.isStatic || body.isSleeping) if (body.isStatic || body.isSleeping)
continue; continue;
Body.update(body, deltaTime, timeScale, correction); Body.update(body, delta);
} }
}; };
@ -373,6 +364,7 @@ var Body = require('../body/Body');
* @param {} event An event object * @param {} event An event object
* @param {} event.pairs List of affected pairs * @param {} event.pairs List of affected pairs
* @param {number} event.timestamp The engine.timing.timestamp of the event * @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {number} event.delta The delta time in milliseconds value used in the update
* @param {} event.source The source object of the event * @param {} event.source The source object of the event
* @param {} event.name The name of the event * @param {} event.name The name of the event
*/ */
@ -384,6 +376,7 @@ var Body = require('../body/Body');
* @param {} event An event object * @param {} event An event object
* @param {} event.pairs List of affected pairs * @param {} event.pairs List of affected pairs
* @param {number} event.timestamp The engine.timing.timestamp of the event * @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {number} event.delta The delta time in milliseconds value used in the update
* @param {} event.source The source object of the event * @param {} event.source The source object of the event
* @param {} event.name The name of the event * @param {} event.name The name of the event
*/ */

View file

@ -53,13 +53,11 @@ var Common = require('./Common');
Runner.create = function(options) { Runner.create = function(options) {
var defaults = { var defaults = {
fps: 60, fps: 60,
correction: 1,
deltaSampleSize: 60, deltaSampleSize: 60,
counterTimestamp: 0, counterTimestamp: 0,
frameCounter: 0, frameCounter: 0,
deltaHistory: [], deltaHistory: [],
timePrev: null, timePrev: null,
timeScalePrev: 1,
frameRequestId: null, frameRequestId: null,
isFixed: false, isFixed: false,
enabled: true enabled: true
@ -100,7 +98,7 @@ var Common = require('./Common');
/** /**
* A game loop utility that updates the engine and renderer by one step (a 'tick'). * 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. * Features delta smoothing and fixed or dynamic timing.
* Triggers `beforeTick`, `tick` and `afterTick` events on the engine. * Triggers `beforeTick`, `tick` and `afterTick` events on the engine.
* Consider just `Engine.update(engine, delta)` if you're using your own loop. * Consider just `Engine.update(engine, delta)` if you're using your own loop.
* @method tick * @method tick
@ -110,17 +108,8 @@ var Common = require('./Common');
*/ */
Runner.tick = function(runner, engine, time) { Runner.tick = function(runner, engine, time) {
var timing = engine.timing, var timing = engine.timing,
correction = 1,
delta; delta;
// create an event object
var event = {
timestamp: timing.timestamp
};
Events.trigger(runner, 'beforeTick', event);
Events.trigger(engine, 'beforeTick', event); // @deprecated
if (runner.isFixed) { if (runner.isFixed) {
// fixed timestep // fixed timestep
delta = runner.delta; delta = runner.delta;
@ -133,27 +122,22 @@ var Common = require('./Common');
runner.deltaHistory.push(delta); runner.deltaHistory.push(delta);
runner.deltaHistory = runner.deltaHistory.slice(-runner.deltaSampleSize); runner.deltaHistory = runner.deltaHistory.slice(-runner.deltaSampleSize);
delta = Math.min.apply(null, runner.deltaHistory); delta = Math.min.apply(null, runner.deltaHistory);
// limit delta // limit delta
delta = delta < runner.deltaMin ? runner.deltaMin : delta; delta = delta < runner.deltaMin ? runner.deltaMin : delta;
delta = delta > runner.deltaMax ? runner.deltaMax : delta; delta = delta > runner.deltaMax ? runner.deltaMax : delta;
// correction for delta
correction = delta / runner.delta;
// update engine timing object // update engine timing object
runner.delta = delta; runner.delta = delta;
} }
// time correction for time scaling // create an event object
if (runner.timeScalePrev !== 0) var event = {
correction *= timing.timeScale / runner.timeScalePrev; timestamp: timing.timestamp
};
if (timing.timeScale === 0) Events.trigger(runner, 'beforeTick', event);
correction = 0; Events.trigger(engine, 'beforeTick', event); // @deprecated
runner.timeScalePrev = timing.timeScale;
runner.correction = correction;
// fps counter // fps counter
runner.frameCounter += 1; runner.frameCounter += 1;

View file

@ -9,6 +9,7 @@ var Sleeping = {};
module.exports = Sleeping; module.exports = Sleeping;
var Events = require('./Events'); var Events = require('./Events');
var Common = require('./Common');
(function() { (function() {
@ -20,11 +21,11 @@ var Events = require('./Events');
* Puts bodies to sleep or wakes them up depending on their motion. * Puts bodies to sleep or wakes them up depending on their motion.
* @method update * @method update
* @param {body[]} bodies * @param {body[]} bodies
* @param {number} timeScale * @param {number} delta
*/ */
Sleeping.update = function(bodies, timeScale) { Sleeping.update = function(bodies, delta) {
var timeFactor = timeScale * timeScale * timeScale; var timeScale = delta / Common._timeUnit;
// update bodies sleeping status // update bodies sleeping status
for (var i = 0; i < bodies.length; i++) { for (var i = 0; i < bodies.length; i++) {
var body = bodies[i], var body = bodies[i],
@ -41,11 +42,11 @@ var Events = require('./Events');
// biased average motion estimation between frames // biased average motion estimation between frames
body.motion = Sleeping._minBias * minMotion + (1 - Sleeping._minBias) * maxMotion; body.motion = Sleeping._minBias * minMotion + (1 - Sleeping._minBias) * maxMotion;
if (body.sleepThreshold > 0 && body.motion < Sleeping._motionSleepThreshold * timeFactor) { if (body.sleepThreshold > 0 && body.motion < Sleeping._motionSleepThreshold * timeScale * timeScale) {
body.sleepCounter += 1; body.sleepCounter += 1;
if (body.sleepCounter >= body.sleepThreshold) if (body.sleepCounter >= body.sleepThreshold / timeScale)
Sleeping.set(body, true); Sleeping.set(body, true);
} else if (body.sleepCounter > 0) { } else if (body.sleepCounter > 0) {
body.sleepCounter -= 1; body.sleepCounter -= 1;
@ -57,10 +58,10 @@ var Events = require('./Events');
* Given a set of colliding pairs, wakes the sleeping bodies involved. * Given a set of colliding pairs, wakes the sleeping bodies involved.
* @method afterCollisions * @method afterCollisions
* @param {pair[]} pairs * @param {pair[]} pairs
* @param {number} timeScale * @param {number} delta
*/ */
Sleeping.afterCollisions = function(pairs, timeScale) { Sleeping.afterCollisions = function(pairs, delta) {
var timeFactor = timeScale * timeScale * timeScale; var timeScale = delta / Common._timeUnit;
// wake up bodies involved in collisions // wake up bodies involved in collisions
for (var i = 0; i < pairs.length; i++) { for (var i = 0; i < pairs.length; i++) {
@ -82,7 +83,7 @@ var Events = require('./Events');
var sleepingBody = (bodyA.isSleeping && !bodyA.isStatic) ? bodyA : bodyB, var sleepingBody = (bodyA.isSleeping && !bodyA.isStatic) ? bodyA : bodyB,
movingBody = sleepingBody === bodyA ? bodyB : bodyA; movingBody = sleepingBody === bodyA ? bodyB : bodyA;
if (!sleepingBody.isStatic && movingBody.motion > Sleeping._motionWakeThreshold * timeFactor) { if (!sleepingBody.isStatic && movingBody.motion > Sleeping._motionWakeThreshold * timeScale * timeScale) {
Sleeping.set(sleepingBody, false); Sleeping.set(sleepingBody, false);
} }
} }