mirror of
https://github.com/liabru/matter-js.git
synced 2025-01-13 16:18:50 -05:00
refactored pair manager, added new events to engine
This commit is contained in:
parent
21732978ce
commit
88bd7bc29f
6 changed files with 156 additions and 77 deletions
|
@ -8,77 +8,112 @@ var Manager = {};
|
|||
|
||||
(function() {
|
||||
|
||||
var _pairMaxIdleLife = 500;
|
||||
var _pairMaxIdleLife = 1000;
|
||||
|
||||
/**
|
||||
* Description
|
||||
* @method updatePairs
|
||||
* @param {object} pairs
|
||||
* @param {pair[]} pairsList
|
||||
* @param {pair[]} candidatePairs
|
||||
* @param {metrics} metrics
|
||||
* @param {detector} detector
|
||||
* @return {bool} pairsUpdated flag
|
||||
* @param {collision[]} collisions
|
||||
*/
|
||||
Manager.updatePairs = function(pairs, pairsList, candidatePairs, metrics, detector) {
|
||||
var i;
|
||||
Manager.updatePairs = function(pairs, collisions) {
|
||||
var pairsList = pairs.list,
|
||||
pairsTable = pairs.table,
|
||||
collisionStart = pairs.collisionStart,
|
||||
collisionEnd = pairs.collisionEnd,
|
||||
collisionActive = pairs.collisionActive,
|
||||
activePairIds = [],
|
||||
collision,
|
||||
pairId,
|
||||
pair,
|
||||
i;
|
||||
|
||||
// first set all pairs inactive
|
||||
for (i = 0; i < pairsList.length; i++) {
|
||||
var pair = pairsList[i];
|
||||
Pair.setActive(pair, false);
|
||||
}
|
||||
|
||||
// detect collisions in current step
|
||||
var pairsUpdated = false,
|
||||
collisions = detector(candidatePairs, metrics);
|
||||
// clear collision state arrays, but maintain old reference
|
||||
collisionStart.length = 0;
|
||||
collisionEnd.length = 0;
|
||||
collisionActive.length = 0;
|
||||
|
||||
// set collision pairs to active, or create if pair is new
|
||||
for (i = 0; i < collisions.length; i++) {
|
||||
var collision = collisions[i],
|
||||
collision = collisions[i];
|
||||
|
||||
if (collision.collided) {
|
||||
pairId = Pair.id(collision.bodyA, collision.bodyB);
|
||||
|
||||
if (pairId in pairs) {
|
||||
Pair.update(pairs[pairId], collision);
|
||||
} else {
|
||||
pairs[pairId] = Pair.create(collision);
|
||||
pairsUpdated = true;
|
||||
activePairIds.push(pairId);
|
||||
|
||||
if (pairId in pairsTable) {
|
||||
// pair already exists (but may or may not be active)
|
||||
pair = pairsTable[pairId];
|
||||
|
||||
if (pair.isActive) {
|
||||
// pair exists and is active
|
||||
collisionActive.push(pair);
|
||||
} else {
|
||||
// pair exists but was inactive, so a collision has just started again
|
||||
collisionStart.push(pair);
|
||||
}
|
||||
|
||||
// update the pair
|
||||
Pair.update(pair, collision);
|
||||
} else {
|
||||
// pair did not exist, create a new pair
|
||||
pair = Pair.create(collision);
|
||||
pairsTable[pairId] = pair;
|
||||
|
||||
// push the new pair
|
||||
collisionStart.push(pair);
|
||||
pairsList.push(pair);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// deactivate previously active pairs that are now inactive
|
||||
for (i = 0; i < pairsList.length; i++) {
|
||||
pair = pairsList[i];
|
||||
if (pair.isActive && activePairIds.indexOf(pair.id) === -1) {
|
||||
Pair.setActive(pair, false);
|
||||
collisionEnd.push(pair);
|
||||
}
|
||||
}
|
||||
|
||||
return pairsUpdated;
|
||||
};
|
||||
|
||||
/**
|
||||
* Description
|
||||
* @method removeOldPairs
|
||||
* @param {object} pairs
|
||||
* @param {pair[]} pairsList
|
||||
* @return {bool} pairsRemoved flag
|
||||
*/
|
||||
Manager.removeOldPairs = function(pairs, pairsList) {
|
||||
var timeNow = Common.now(),
|
||||
pairsRemoved = false,
|
||||
Manager.removeOldPairs = function(pairs) {
|
||||
var pairsList = pairs.list,
|
||||
pairsTable = pairs.table,
|
||||
timeNow = Common.now(),
|
||||
indexesToRemove = [],
|
||||
pair,
|
||||
collision,
|
||||
pairIndex,
|
||||
i;
|
||||
|
||||
|
||||
for (i = 0; i < pairsList.length; i++) {
|
||||
var pair = pairsList[i],
|
||||
collision = pair.collision;
|
||||
pair = pairsList[i];
|
||||
collision = pair.collision;
|
||||
|
||||
// never remove sleeping pairs
|
||||
if (collision.bodyA.isSleeping || collision.bodyB.isSleeping) {
|
||||
pair.timestamp = timeNow;
|
||||
pair.timeUpdated = timeNow;
|
||||
continue;
|
||||
}
|
||||
|
||||
// if pair is inactive for too long, remove it
|
||||
if (timeNow - pair.timestamp > _pairMaxIdleLife) {
|
||||
delete pairs[pair.id];
|
||||
pairsRemoved = true;
|
||||
// if pair is inactive for too long, mark it to be removed
|
||||
if (timeNow - pair.timeUpdated > _pairMaxIdleLife) {
|
||||
indexesToRemove.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
return pairsRemoved;
|
||||
|
||||
// remove marked pairs
|
||||
for (i = 0; i < indexesToRemove.length; i++) {
|
||||
pairIndex = indexesToRemove[i];
|
||||
pair = pairsList[pairIndex];
|
||||
delete pairsTable[pair.id];
|
||||
pairsList.splice(pairIndex, 1);
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
|
@ -16,15 +16,19 @@ var Pair = {};
|
|||
*/
|
||||
Pair.create = function(collision) {
|
||||
var bodyA = collision.bodyA,
|
||||
bodyB = collision.bodyB;
|
||||
bodyB = collision.bodyB,
|
||||
timestamp = Common.now();
|
||||
|
||||
var pair = {
|
||||
id: Pair.id(bodyA, bodyB),
|
||||
bodyA: bodyA,
|
||||
bodyB: bodyB,
|
||||
contacts: {},
|
||||
activeContacts: [],
|
||||
separation: 0,
|
||||
isActive: true,
|
||||
timestamp: Common.now(),
|
||||
timeCreated: timestamp,
|
||||
timeUpdated: timestamp,
|
||||
inverseMass: bodyA.inverseMass + bodyB.inverseMass,
|
||||
friction: Math.min(bodyA.friction, bodyB.friction),
|
||||
restitution: Math.max(bodyA.restitution, bodyB.restitution),
|
||||
|
@ -78,7 +82,7 @@ var Pair = {};
|
|||
Pair.setActive = function(pair, isActive) {
|
||||
if (isActive) {
|
||||
pair.isActive = true;
|
||||
pair.timestamp = Common.now();
|
||||
pair.timeUpdated = Common.now();
|
||||
} else {
|
||||
pair.isActive = false;
|
||||
pair.activeContacts = [];
|
||||
|
|
|
@ -34,11 +34,17 @@ var Engine = {};
|
|||
positionIterations: 6,
|
||||
velocityIterations: 4,
|
||||
constraintIterations: 1,
|
||||
pairs: {},
|
||||
pairsList: [],
|
||||
pairs: {
|
||||
table: {},
|
||||
list: [],
|
||||
collisionStart: [],
|
||||
collisionActive: [],
|
||||
collisionEnd: []
|
||||
},
|
||||
enableSleeping: false,
|
||||
timeScale: 1,
|
||||
input: {},
|
||||
events: [],
|
||||
timing: {
|
||||
fps: _fps,
|
||||
timestamp: 0,
|
||||
|
@ -78,16 +84,6 @@ var Engine = {};
|
|||
}
|
||||
};
|
||||
|
||||
engine.events = {
|
||||
tick: function(engine) {
|
||||
Engine.update(engine, engine.timing.delta, engine.timing.correction);
|
||||
},
|
||||
render: function(engine) {
|
||||
if (engine.render.options.enabled)
|
||||
engine.render.controller.world(engine);
|
||||
}
|
||||
};
|
||||
|
||||
return engine;
|
||||
};
|
||||
|
||||
|
@ -99,6 +95,7 @@ var Engine = {};
|
|||
Engine.run = function(engine) {
|
||||
var timing = engine.timing,
|
||||
delta,
|
||||
correction,
|
||||
counterTimestamp = 0,
|
||||
frameCounter = 0,
|
||||
deltaHistory = [];
|
||||
|
@ -109,6 +106,13 @@ var Engine = {};
|
|||
if (!engine.enabled)
|
||||
return;
|
||||
|
||||
// create an event object
|
||||
var event = {
|
||||
timestamp: timestamp
|
||||
};
|
||||
|
||||
Events.trigger(engine, 'beforeTick', event);
|
||||
|
||||
delta = (timestamp - timing.timestamp) || _delta;
|
||||
|
||||
// optimistically filter delta over a few frames, to improve stability
|
||||
|
@ -120,20 +124,32 @@ var Engine = {};
|
|||
delta = delta < engine.timing.deltaMin ? engine.timing.deltaMin : delta;
|
||||
delta = delta > engine.timing.deltaMax ? engine.timing.deltaMax : delta;
|
||||
|
||||
// verlet time correction
|
||||
correction = delta / timing.delta;
|
||||
|
||||
// update engine timing object
|
||||
timing.timestamp = timestamp;
|
||||
timing.correction = delta / timing.delta;
|
||||
timing.correction = correction;
|
||||
timing.delta = delta;
|
||||
|
||||
// fps counter
|
||||
frameCounter += 1;
|
||||
|
||||
if (timestamp - counterTimestamp >= 1000) {
|
||||
timing.fps = frameCounter * ((timestamp - counterTimestamp) / 1000);
|
||||
counterTimestamp = timestamp;
|
||||
frameCounter = 0;
|
||||
}
|
||||
|
||||
engine.events.tick(engine);
|
||||
engine.events.render(engine);
|
||||
Events.trigger(engine, 'tick beforeUpdate', event);
|
||||
|
||||
Engine.update(engine, delta, correction);
|
||||
|
||||
Events.trigger(engine, 'afterUpdate beforeRender', event);
|
||||
|
||||
if (engine.render.options.enabled)
|
||||
engine.render.controller.world(engine);
|
||||
|
||||
Events.trigger(engine, 'afterTick afterRender', event);
|
||||
})();
|
||||
};
|
||||
|
||||
|
@ -151,16 +167,17 @@ var Engine = {};
|
|||
broadphasePairs = [],
|
||||
i;
|
||||
|
||||
Body.resetForcesAll(world.bodies, world.gravity);
|
||||
Metrics.reset(engine.metrics);
|
||||
|
||||
MouseConstraint.update(engine.mouseConstraint, world.bodies, engine.input);
|
||||
Body.updateAll(world.bodies, delta * engine.timeScale, correction, world.bounds);
|
||||
|
||||
// update all constraints
|
||||
for (i = 0; i < engine.constraintIterations; i++) {
|
||||
Constraint.updateAll(world.constraints);
|
||||
}
|
||||
|
||||
// broadphase pass: find potential collision pairs
|
||||
if (broadphase.controller) {
|
||||
broadphase.controller.update(broadphase.instance, world.bodies, engine);
|
||||
broadphasePairs = broadphase.instance.pairsList;
|
||||
|
@ -168,25 +185,44 @@ var Engine = {};
|
|||
broadphasePairs = world.bodies;
|
||||
}
|
||||
|
||||
var pairsUpdated = Manager.updatePairs(engine.pairs, engine.pairsList, broadphasePairs, engine.metrics, broadphase.detector),
|
||||
pairsRemoved = Manager.removeOldPairs(engine.pairs, engine.pairsList);
|
||||
|
||||
if (pairsUpdated || pairsRemoved)
|
||||
engine.pairsList = Common.values(engine.pairs);
|
||||
// narrowphase pass: find actual collisions, then create or update collision pairs
|
||||
var collisions = broadphase.detector(broadphasePairs, engine.metrics);
|
||||
|
||||
// update pairs
|
||||
var pairs = engine.pairs;
|
||||
Manager.updatePairs(pairs, collisions);
|
||||
Manager.removeOldPairs(pairs);
|
||||
|
||||
// trigger collision events
|
||||
if (pairs.collisionStart.length > 0) {
|
||||
Events.trigger(engine, 'collisionStart', {
|
||||
pairs: pairs.collisionStart
|
||||
});
|
||||
}
|
||||
if (pairs.collisionActive.length > 0) {
|
||||
Events.trigger(engine, 'collisionActive', {
|
||||
pairs: pairs.collisionActive
|
||||
});
|
||||
}
|
||||
if (pairs.collisionEnd.length > 0) {
|
||||
Events.trigger(engine, 'collisionEnd', {
|
||||
pairs: pairs.collisionEnd
|
||||
});
|
||||
}
|
||||
|
||||
// wake up bodies involved in collisions
|
||||
if (engine.enableSleeping)
|
||||
Sleeping.afterCollisions(engine.pairsList);
|
||||
Sleeping.afterCollisions(pairs.list);
|
||||
|
||||
// iteratively resolve velocity between collisions
|
||||
Resolver.preSolveVelocity(engine.pairsList);
|
||||
Resolver.preSolveVelocity(pairs.list);
|
||||
for (i = 0; i < engine.velocityIterations; i++) {
|
||||
Resolver.solveVelocity(engine.pairsList);
|
||||
Resolver.solveVelocity(pairs.list);
|
||||
}
|
||||
|
||||
// iteratively resolve position between collisions
|
||||
for (i = 0; i < engine.positionIterations; i++) {
|
||||
Resolver.solvePosition(engine.pairsList);
|
||||
Resolver.solvePosition(pairs.list);
|
||||
}
|
||||
Resolver.postSolvePosition(world.bodies);
|
||||
|
||||
|
@ -195,6 +231,9 @@ var Engine = {};
|
|||
|
||||
Metrics.update(engine.metrics, engine);
|
||||
|
||||
// clear force buffers
|
||||
Body.resetForcesAll(world.bodies, world.gravity);
|
||||
|
||||
return engine;
|
||||
};
|
||||
|
||||
|
@ -238,8 +277,8 @@ var Engine = {};
|
|||
Engine.clear = function(engine) {
|
||||
var world = engine.world;
|
||||
|
||||
engine.pairs = {};
|
||||
engine.pairsList = [];
|
||||
engine.pairs.table = {};
|
||||
engine.pairs.list = [];
|
||||
|
||||
World.addComposite(engine.world, engine.mouseConstraint);
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ var Metrics = {};
|
|||
broadphase = engine.broadphase[engine.broadphase.current];
|
||||
|
||||
metrics.collisions = metrics.narrowDetections;
|
||||
metrics.pairs = engine.pairsList.length;
|
||||
metrics.pairs = engine.pairs.list.length;
|
||||
metrics.bodies = world.bodies.length;
|
||||
metrics.midEff = (metrics.narrowDetections / (metrics.midphaseTests || 1)).toFixed(2);
|
||||
metrics.narrowEff = (metrics.narrowDetections / (metrics.narrowphaseTests || 1)).toFixed(2);
|
||||
|
|
|
@ -31,6 +31,7 @@ Matter.Vector = Vector;
|
|||
Matter.Vertices = Vertices;
|
||||
Matter.Gui = Gui;
|
||||
Matter.Render = Render;
|
||||
Matter.Events = Events;
|
||||
|
||||
// CommonJS module
|
||||
if (typeof exports !== 'undefined') {
|
||||
|
|
|
@ -82,8 +82,8 @@ var Render = {};
|
|||
Render.constraint(world.constraints[i], context);
|
||||
|
||||
if (options.showCollisions)
|
||||
for (i = 0; i < engine.pairsList.length; i++)
|
||||
Render.collision(engine, engine.pairsList[i], context);
|
||||
for (i = 0; i < engine.pairs.list.length; i++)
|
||||
Render.collision(engine, engine.pairs.list[i], context);
|
||||
|
||||
if (options.showBroadphase && engine.broadphase.current === 'grid')
|
||||
Render.grid(engine, engine.broadphase[engine.broadphase.current].instance, context);
|
||||
|
@ -118,7 +118,7 @@ var Render = {};
|
|||
text += "\n";
|
||||
|
||||
text += "collisions: " + engine.metrics.collisions + space;
|
||||
text += "pairs: " + engine.pairs.length + space;
|
||||
text += "pairs: " + engine.pairs.list.length + space;
|
||||
text += "broad: " + engine.metrics.broadEff + space;
|
||||
text += "mid: " + engine.metrics.midEff + space;
|
||||
text += "narrow: " + engine.metrics.narrowEff + space;
|
||||
|
|
Loading…
Reference in a new issue