mirror of
https://github.com/photonstorm/phaser
synced 2024-11-10 07:04:31 +00:00
MatterJS v0.20 integration
This commit is contained in:
parent
91f21c95e9
commit
c45e2f4dba
12 changed files with 278 additions and 212 deletions
|
@ -193,7 +193,11 @@ var Body = require('./Body');
|
|||
Composite.removeComposite = function(compositeA, compositeB, deep) {
|
||||
var position = Common.indexOf(compositeA.composites, compositeB);
|
||||
if (position !== -1) {
|
||||
var bodies = Composite.allBodies(compositeB);
|
||||
Composite.removeCompositeAt(compositeA, position);
|
||||
for (var i = 0; i < bodies.length; i++) {
|
||||
bodies[i].sleepCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (deep) {
|
||||
|
@ -246,6 +250,7 @@ var Body = require('./Body');
|
|||
var position = Common.indexOf(composite.bodies, body);
|
||||
if (position !== -1) {
|
||||
Composite.removeBodyAt(composite, position);
|
||||
body.sleepCounter = 0;
|
||||
}
|
||||
|
||||
if (deep) {
|
||||
|
|
|
@ -47,7 +47,8 @@ var Pair = require('./Pair');
|
|||
normal: { x: 0, y: 0 },
|
||||
tangent: { x: 0, y: 0 },
|
||||
penetration: { x: 0, y: 0 },
|
||||
supports: []
|
||||
supports: [null, null],
|
||||
supportCount: 0
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -99,27 +100,31 @@ var Pair = require('./Pair');
|
|||
}
|
||||
|
||||
var normal = collision.normal,
|
||||
tangent = collision.tangent,
|
||||
penetration = collision.penetration,
|
||||
supports = collision.supports,
|
||||
depth = minOverlap.overlap,
|
||||
minAxis = minOverlap.axis,
|
||||
minAxisX = minAxis.x,
|
||||
minAxisY = minAxis.y;
|
||||
normalX = minAxis.x,
|
||||
normalY = minAxis.y,
|
||||
deltaX = bodyB.position.x - bodyA.position.x,
|
||||
deltaY = bodyB.position.y - bodyA.position.y;
|
||||
|
||||
// ensure normal is facing away from bodyA
|
||||
if (minAxisX * (bodyB.position.x - bodyA.position.x) + minAxisY * (bodyB.position.y - bodyA.position.y) < 0) {
|
||||
normal.x = minAxisX;
|
||||
normal.y = minAxisY;
|
||||
} else {
|
||||
normal.x = -minAxisX;
|
||||
normal.y = -minAxisY;
|
||||
if (normalX * deltaX + normalY * deltaY >= 0) {
|
||||
normalX = -normalX;
|
||||
normalY = -normalY;
|
||||
}
|
||||
normal.x = normalX;
|
||||
normal.y = normalY;
|
||||
|
||||
collision.tangent.x = -normal.y;
|
||||
collision.tangent.y = normal.x;
|
||||
tangent.x = -normalY;
|
||||
tangent.y = normalX;
|
||||
|
||||
collision.depth = minOverlap.overlap;
|
||||
|
||||
collision.penetration.x = normal.x * collision.depth;
|
||||
collision.penetration.y = normal.y * collision.depth;
|
||||
penetration.x = normalX * depth;
|
||||
penetration.y = normalY * depth;
|
||||
collision.depth = depth;
|
||||
|
||||
// find support points, there is always either exactly one or two
|
||||
var supportsB = Collision._findSupports(bodyA, bodyB, normal, 1),
|
||||
|
@ -153,7 +158,7 @@ var Pair = require('./Pair');
|
|||
}
|
||||
|
||||
// update supports array size
|
||||
supports.length = supportCount;
|
||||
collision.supportCount = supportCount;
|
||||
|
||||
return collision;
|
||||
};
|
||||
|
@ -232,32 +237,6 @@ var Pair = require('./Pair');
|
|||
result.overlap = overlapMin;
|
||||
};
|
||||
|
||||
/**
|
||||
* Projects vertices on an axis and returns an interval.
|
||||
* @method _projectToAxis
|
||||
* @private
|
||||
* @param {} projection
|
||||
* @param {} vertices
|
||||
* @param {} axis
|
||||
*/
|
||||
Collision._projectToAxis = function(projection, vertices, axis) {
|
||||
var min = vertices[0].x * axis.x + vertices[0].y * axis.y,
|
||||
max = min;
|
||||
|
||||
for (var i = 1; i < vertices.length; i += 1) {
|
||||
var dot = vertices[i].x * axis.x + vertices[i].y * axis.y;
|
||||
|
||||
if (dot > max) {
|
||||
max = dot;
|
||||
} else if (dot < min) {
|
||||
min = dot;
|
||||
}
|
||||
}
|
||||
|
||||
projection.min = min;
|
||||
projection.max = max;
|
||||
};
|
||||
|
||||
/**
|
||||
* Finds supporting vertices given two bodies along a given direction using hill-climbing.
|
||||
* @method _findSupports
|
||||
|
@ -275,15 +254,15 @@ var Pair = require('./Pair');
|
|||
bodyAPositionY = bodyA.position.y,
|
||||
normalX = normal.x * direction,
|
||||
normalY = normal.y * direction,
|
||||
nearestDistance = Number.MAX_VALUE,
|
||||
vertexA,
|
||||
vertexB,
|
||||
vertexA = vertices[0],
|
||||
vertexB = vertexA,
|
||||
nearestDistance = normalX * (bodyAPositionX - vertexB.x) + normalY * (bodyAPositionY - vertexB.y),
|
||||
vertexC,
|
||||
distance,
|
||||
j;
|
||||
|
||||
// find deepest vertex relative to the axis
|
||||
for (j = 0; j < verticesLength; j += 1) {
|
||||
for (j = 1; j < verticesLength; j += 1) {
|
||||
vertexB = vertices[j];
|
||||
distance = normalX * (bodyAPositionX - vertexB.x) + normalY * (bodyAPositionY - vertexB.y);
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ var Collision = require('./Collision');
|
|||
Detector.create = function(options) {
|
||||
var defaults = {
|
||||
bodies: [],
|
||||
collisions: [],
|
||||
pairs: null
|
||||
};
|
||||
|
||||
|
@ -45,6 +46,7 @@ var Collision = require('./Collision');
|
|||
*/
|
||||
Detector.clear = function(detector) {
|
||||
detector.bodies = [];
|
||||
detector.collisions = [];
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -57,12 +59,13 @@ var Collision = require('./Collision');
|
|||
* @return {collision[]} collisions
|
||||
*/
|
||||
Detector.collisions = function(detector) {
|
||||
var collisions = [],
|
||||
pairs = detector.pairs,
|
||||
var pairs = detector.pairs,
|
||||
bodies = detector.bodies,
|
||||
bodiesLength = bodies.length,
|
||||
canCollide = Detector.canCollide,
|
||||
collides = Collision.collides,
|
||||
collisions = detector.collisions,
|
||||
collisionIndex = 0,
|
||||
i,
|
||||
j;
|
||||
|
||||
|
@ -104,7 +107,7 @@ var Collision = require('./Collision');
|
|||
var collision = collides(bodyA, bodyB, pairs);
|
||||
|
||||
if (collision) {
|
||||
collisions.push(collision);
|
||||
collisions[collisionIndex++] = collision;
|
||||
}
|
||||
} else {
|
||||
var partsAStart = partsALength > 1 ? 1 : 0,
|
||||
|
@ -126,13 +129,16 @@ var Collision = require('./Collision');
|
|||
var collision = collides(partA, partB, pairs);
|
||||
|
||||
if (collision) {
|
||||
collisions.push(collision);
|
||||
collisions[collisionIndex++] = collision;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (collisions.length !== collisionIndex) {
|
||||
collisions.length = collisionIndex;
|
||||
}
|
||||
|
||||
return collisions;
|
||||
};
|
||||
|
@ -180,6 +186,13 @@ var Collision = require('./Collision');
|
|||
* @default []
|
||||
*/
|
||||
|
||||
/**
|
||||
* The array of `Matter.Collision` found in the last call to `Detector.collisions` on this detector.
|
||||
* @property collisions
|
||||
* @type collision[]
|
||||
* @default []
|
||||
*/
|
||||
|
||||
/**
|
||||
* Optional. A `Matter.Pairs` object from which previous collision objects may be reused. Intended for internal `Matter.Engine` usage.
|
||||
* @property pairs
|
||||
|
|
|
@ -28,11 +28,10 @@ var Contact = require('./Contact');
|
|||
bodyA: bodyA,
|
||||
bodyB: bodyB,
|
||||
collision: collision,
|
||||
contacts: [],
|
||||
activeContacts: [],
|
||||
contacts: [Contact.create(), Contact.create()],
|
||||
contactCount: 0,
|
||||
separation: 0,
|
||||
isActive: true,
|
||||
confirmedActive: true,
|
||||
isSensor: bodyA.isSensor || bodyB.isSensor,
|
||||
timeCreated: timestamp,
|
||||
timeUpdated: timestamp,
|
||||
|
@ -56,12 +55,11 @@ var Contact = require('./Contact');
|
|||
* @param {number} timestamp
|
||||
*/
|
||||
Pair.update = function(pair, collision, timestamp) {
|
||||
var contacts = pair.contacts,
|
||||
supports = collision.supports,
|
||||
activeContacts = pair.activeContacts,
|
||||
var supports = collision.supports,
|
||||
supportCount = collision.supportCount,
|
||||
contacts = pair.contacts,
|
||||
parentA = collision.parentA,
|
||||
parentB = collision.parentB,
|
||||
parentAVerticesLength = parentA.vertices.length;
|
||||
parentB = collision.parentB;
|
||||
|
||||
pair.isActive = true;
|
||||
pair.timeUpdated = timestamp;
|
||||
|
@ -73,20 +71,22 @@ var Contact = require('./Contact');
|
|||
pair.restitution = parentA.restitution > parentB.restitution ? parentA.restitution : parentB.restitution;
|
||||
pair.slop = parentA.slop > parentB.slop ? parentA.slop : parentB.slop;
|
||||
|
||||
pair.contactCount = supportCount;
|
||||
collision.pair = pair;
|
||||
activeContacts.length = 0;
|
||||
var supportA = supports[0],
|
||||
contactA = contacts[0],
|
||||
|
||||
for (var i = 0; i < supports.length; i++) {
|
||||
var support = supports[i],
|
||||
contactId = support.body === parentA ? support.index : parentAVerticesLength + support.index,
|
||||
contact = contacts[contactId];
|
||||
supportB = supports[1],
|
||||
contactB = contacts[1];
|
||||
if (contactB.vertex === supportA || contactA.vertex === supportB) {
|
||||
contacts[1] = contactA;
|
||||
contacts[0] = contactA = contactB;
|
||||
|
||||
if (contact) {
|
||||
activeContacts.push(contact);
|
||||
} else {
|
||||
activeContacts.push(contacts[contactId] = Contact.create(support));
|
||||
contactB = contacts[1];
|
||||
}
|
||||
}
|
||||
|
||||
contactA.vertex = supportA;
|
||||
contactB.vertex = supportB;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -102,7 +102,7 @@ var Contact = require('./Contact');
|
|||
pair.timeUpdated = timestamp;
|
||||
} else {
|
||||
pair.isActive = false;
|
||||
pair.activeContacts.length = 0;
|
||||
pair.contactCount = 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -114,11 +114,8 @@ var Contact = require('./Contact');
|
|||
* @return {string} Unique pairId
|
||||
*/
|
||||
Pair.id = function(bodyA, bodyB) {
|
||||
if (bodyA.id < bodyB.id) {
|
||||
return 'A' + bodyA.id + 'B' + bodyB.id;
|
||||
} else {
|
||||
return 'A' + bodyB.id + 'B' + bodyA.id;
|
||||
}
|
||||
return bodyA.id < bodyB.id ? bodyA.id.toString(36) + ':' + bodyB.id.toString(36)
|
||||
: bodyB.id.toString(36) + ':' + bodyA.id.toString(36);
|
||||
};
|
||||
|
||||
})();
|
||||
|
|
|
@ -37,26 +37,26 @@ var Common = require('../core/Common');
|
|||
* @param {number} timestamp
|
||||
*/
|
||||
Pairs.update = function(pairs, collisions, timestamp) {
|
||||
var pairsList = pairs.list,
|
||||
pairsListLength = pairsList.length,
|
||||
var pairUpdate = Pair.update,
|
||||
pairCreate = Pair.create,
|
||||
pairSetActive = Pair.setActive,
|
||||
pairsTable = pairs.table,
|
||||
collisionsLength = collisions.length,
|
||||
pairsList = pairs.list,
|
||||
pairsListLength = pairsList.length,
|
||||
pairsListIndex = pairsListLength,
|
||||
collisionStart = pairs.collisionStart,
|
||||
collisionEnd = pairs.collisionEnd,
|
||||
collisionActive = pairs.collisionActive,
|
||||
collisionsLength = collisions.length,
|
||||
collisionStartIndex = 0,
|
||||
collisionEndIndex = 0,
|
||||
collisionActiveIndex = 0,
|
||||
collision,
|
||||
pairIndex,
|
||||
pair,
|
||||
i;
|
||||
|
||||
// clear collision state arrays, but maintain old reference
|
||||
collisionStart.length = 0;
|
||||
collisionEnd.length = 0;
|
||||
collisionActive.length = 0;
|
||||
|
||||
for (i = 0; i < pairsListLength; i++) {
|
||||
pairsList[i].confirmedActive = false;
|
||||
}
|
||||
|
||||
for (i = 0; i < collisionsLength; i++) {
|
||||
collision = collisions[i];
|
||||
|
@ -66,49 +66,56 @@ var Common = require('../core/Common');
|
|||
// pair already exists (but may or may not be active)
|
||||
if (pair.isActive) {
|
||||
// pair exists and is active
|
||||
collisionActive.push(pair);
|
||||
} else {
|
||||
collisionActive[collisionActiveIndex++] = pair;
|
||||
// pair exists but was inactive, so a collision has just started again
|
||||
collisionStart.push(pair);
|
||||
}
|
||||
|
||||
// update the pair
|
||||
Pair.update(pair, collision, timestamp);
|
||||
pair.confirmedActive = true;
|
||||
pairUpdate(pair, collision, timestamp);
|
||||
} else {
|
||||
// pair did not exist, create a new pair
|
||||
pair = Pair.create(collision, timestamp);
|
||||
pair = pairCreate(collision, timestamp);
|
||||
pairsTable[pair.id] = pair;
|
||||
|
||||
// push the new pair
|
||||
collisionStart.push(pair);
|
||||
pairsList.push(pair);
|
||||
collisionStart[collisionStartIndex++] = pair;
|
||||
pairsList[pairsListIndex++] = pair;
|
||||
}
|
||||
}
|
||||
|
||||
// find pairs that are no longer active
|
||||
var removePairIndex = [];
|
||||
pairsListIndex = 0;
|
||||
pairsListLength = pairsList.length;
|
||||
|
||||
for (i = 0; i < pairsListLength; i++) {
|
||||
pair = pairsList[i];
|
||||
|
||||
if (!pair.confirmedActive) {
|
||||
Pair.setActive(pair, false, timestamp);
|
||||
collisionEnd.push(pair);
|
||||
if (pair.timeUpdated >= timestamp) {
|
||||
pairsList[pairsListIndex++] = pair;
|
||||
} else {
|
||||
pairSetActive(pair, false, timestamp);
|
||||
|
||||
if (!pair.collision.bodyA.isSleeping && !pair.collision.bodyB.isSleeping) {
|
||||
removePairIndex.push(i);
|
||||
if (pair.collision.bodyA.sleepCounter > 0 && pair.collision.bodyB.sleepCounter > 0) {
|
||||
pairsList[pairsListIndex++] = pair;
|
||||
} else {
|
||||
collisionEnd[collisionEndIndex++] = pair;
|
||||
delete pairsTable[pair.id];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove inactive pairs
|
||||
for (i = 0; i < removePairIndex.length; i++) {
|
||||
pairIndex = removePairIndex[i] - i;
|
||||
pair = pairsList[pairIndex];
|
||||
pairsList.splice(pairIndex, 1);
|
||||
delete pairsTable[pair.id];
|
||||
if (pairsList.length !== pairsListIndex) {
|
||||
pairsList.length = pairsListIndex;
|
||||
}
|
||||
if (collisionStart.length !== collisionStartIndex) {
|
||||
collisionStart.length = collisionStartIndex;
|
||||
}
|
||||
if (collisionEnd.length !== collisionEndIndex) {
|
||||
collisionEnd.length = collisionEndIndex;
|
||||
}
|
||||
if (collisionActive.length !== collisionActiveIndex) {
|
||||
collisionActive.length = collisionActiveIndex;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ var Bounds = require('../geometry/Bounds');
|
|||
Resolver.preSolvePosition = function(pairs) {
|
||||
var i,
|
||||
pair,
|
||||
activeCount,
|
||||
contactCount,
|
||||
pairsLength = pairs.length;
|
||||
|
||||
// find total contacts on each body
|
||||
|
@ -39,9 +39,9 @@ var Bounds = require('../geometry/Bounds');
|
|||
if (!pair.isActive)
|
||||
continue;
|
||||
|
||||
activeCount = pair.activeContacts.length;
|
||||
pair.collision.parentA.totalContacts += activeCount;
|
||||
pair.collision.parentB.totalContacts += activeCount;
|
||||
contactCount = pair.contactCount;
|
||||
pair.collision.parentA.totalContacts += contactCount;
|
||||
pair.collision.parentB.totalContacts += contactCount;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -79,8 +79,8 @@ var Bounds = require('../geometry/Bounds');
|
|||
|
||||
// get current separation between body edges involved in collision
|
||||
pair.separation =
|
||||
normal.x * (bodyB.positionImpulse.x + collision.penetration.x - bodyA.positionImpulse.x)
|
||||
+ normal.y * (bodyB.positionImpulse.y + collision.penetration.y - bodyA.positionImpulse.y);
|
||||
collision.depth + normal.x * (bodyB.positionImpulse.x - bodyA.positionImpulse.x)
|
||||
+ normal.y * (bodyB.positionImpulse.y - bodyA.positionImpulse.y);
|
||||
}
|
||||
|
||||
for (i = 0; i < pairsLength; i++) {
|
||||
|
@ -176,8 +176,8 @@ var Bounds = require('../geometry/Bounds');
|
|||
if (!pair.isActive || pair.isSensor)
|
||||
continue;
|
||||
|
||||
var contacts = pair.activeContacts,
|
||||
contactsLength = contacts.length,
|
||||
var contacts = pair.contacts,
|
||||
contactCount = pair.contactCount,
|
||||
collision = pair.collision,
|
||||
bodyA = collision.parentA,
|
||||
bodyB = collision.parentB,
|
||||
|
@ -185,7 +185,7 @@ var Bounds = require('../geometry/Bounds');
|
|||
tangent = collision.tangent;
|
||||
|
||||
// resolve each contact
|
||||
for (j = 0; j < contactsLength; j++) {
|
||||
for (j = 0; j < contactCount; j++) {
|
||||
var contact = contacts[j],
|
||||
contactVertex = contact.vertex,
|
||||
normalImpulse = contact.normalImpulse,
|
||||
|
@ -248,28 +248,26 @@ var Bounds = require('../geometry/Bounds');
|
|||
var collision = pair.collision,
|
||||
bodyA = collision.parentA,
|
||||
bodyB = collision.parentB,
|
||||
bodyAVelocity = bodyA.velocity,
|
||||
bodyBVelocity = bodyB.velocity,
|
||||
normalX = collision.normal.x,
|
||||
normalY = collision.normal.y,
|
||||
tangentX = collision.tangent.x,
|
||||
tangentY = collision.tangent.y,
|
||||
contacts = pair.activeContacts,
|
||||
contactsLength = contacts.length,
|
||||
contactShare = 1 / contactsLength,
|
||||
inverseMassTotal = bodyA.inverseMass + bodyB.inverseMass,
|
||||
friction = pair.friction * pair.frictionStatic * frictionNormalMultiplier;
|
||||
inverseMassTotal = pair.inverseMass,
|
||||
friction = pair.friction * pair.frictionStatic * frictionNormalMultiplier,
|
||||
contacts = pair.contacts,
|
||||
contactCount = pair.contactCount,
|
||||
contactShare = 1 / contactCount;
|
||||
|
||||
// update body velocities
|
||||
bodyAVelocity.x = bodyA.position.x - bodyA.positionPrev.x;
|
||||
bodyAVelocity.y = bodyA.position.y - bodyA.positionPrev.y;
|
||||
bodyBVelocity.x = bodyB.position.x - bodyB.positionPrev.x;
|
||||
bodyBVelocity.y = bodyB.position.y - bodyB.positionPrev.y;
|
||||
bodyA.angularVelocity = bodyA.angle - bodyA.anglePrev;
|
||||
bodyB.angularVelocity = bodyB.angle - bodyB.anglePrev;
|
||||
var bodyAVelocityX = bodyA.position.x - bodyA.positionPrev.x,
|
||||
bodyAVelocityY = bodyA.position.y - bodyA.positionPrev.y,
|
||||
bodyAAngularVelocity = bodyA.angle - bodyA.anglePrev,
|
||||
bodyBVelocityX = bodyB.position.x - bodyB.positionPrev.x,
|
||||
bodyBVelocityY = bodyB.position.y - bodyB.positionPrev.y,
|
||||
bodyBAngularVelocity = bodyB.angle - bodyB.anglePrev;
|
||||
|
||||
// resolve each contact
|
||||
for (j = 0; j < contactsLength; j++) {
|
||||
for (j = 0; j < contactCount; j++) {
|
||||
var contact = contacts[j],
|
||||
contactVertex = contact.vertex;
|
||||
|
||||
|
@ -278,10 +276,10 @@ var Bounds = require('../geometry/Bounds');
|
|||
offsetBX = contactVertex.x - bodyB.position.x,
|
||||
offsetBY = contactVertex.y - bodyB.position.y;
|
||||
|
||||
var velocityPointAX = bodyAVelocity.x - offsetAY * bodyA.angularVelocity,
|
||||
velocityPointAY = bodyAVelocity.y + offsetAX * bodyA.angularVelocity,
|
||||
velocityPointBX = bodyBVelocity.x - offsetBY * bodyB.angularVelocity,
|
||||
velocityPointBY = bodyBVelocity.y + offsetBX * bodyB.angularVelocity;
|
||||
var velocityPointAX = bodyAVelocityX - offsetAY * bodyAAngularVelocity,
|
||||
velocityPointAY = bodyAVelocityY + offsetAX * bodyAAngularVelocity,
|
||||
velocityPointBX = bodyBVelocityX - offsetBY * bodyBAngularVelocity,
|
||||
velocityPointBY = bodyBVelocityY + offsetBX * bodyBAngularVelocity;
|
||||
|
||||
var relativeVelocityX = velocityPointAX - velocityPointBX,
|
||||
relativeVelocityY = velocityPointAY - velocityPointBY;
|
||||
|
|
|
@ -24,6 +24,7 @@ var Body = require('../body/Body');
|
|||
|
||||
(function() {
|
||||
|
||||
Engine._deltaMax = 1000 / 60;
|
||||
/**
|
||||
* 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.
|
||||
|
@ -51,7 +52,8 @@ var Body = require('../body/Body');
|
|||
timestamp: 0,
|
||||
timeScale: 1,
|
||||
lastDelta: 0,
|
||||
lastElapsed: 0
|
||||
lastElapsed: 0,
|
||||
lastUpdatesPerFrame: 0
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -60,6 +62,7 @@ var Body = require('../body/Body');
|
|||
engine.world = options.world || Composite.create({ label: 'World' });
|
||||
engine.pairs = options.pairs || Pairs.create();
|
||||
engine.detector = options.detector || Detector.create();
|
||||
engine.detector.pairs = engine.pairs;
|
||||
|
||||
// for temporary back compatibility only
|
||||
engine.grid = { buckets: [] };
|
||||
|
@ -87,6 +90,11 @@ var Body = require('../body/Body');
|
|||
timing = engine.timing,
|
||||
timestamp = timing.timestamp,
|
||||
i;
|
||||
if (delta > Engine._deltaMax) {
|
||||
Common.warnOnce(
|
||||
'Matter.Engine.update: delta argument is recommended to be less than or equal to', Engine._deltaMax.toFixed(3), 'ms.'
|
||||
);
|
||||
}
|
||||
|
||||
delta = typeof delta !== 'undefined' ? delta : Common._baseDelta;
|
||||
delta *= timing.timeScale;
|
||||
|
@ -138,7 +146,6 @@ var Body = require('../body/Body');
|
|||
Constraint.postSolveAll(allBodies);
|
||||
|
||||
// find all collisions
|
||||
detector.pairs = engine.pairs;
|
||||
var collisions = Detector.collisions(detector);
|
||||
|
||||
// update collision pairs
|
||||
|
|
|
@ -27,7 +27,7 @@ var Common = require('./Common');
|
|||
* @readOnly
|
||||
* @type {String}
|
||||
*/
|
||||
Matter.version = '0.19.0';
|
||||
Matter.version = '0.20.0';
|
||||
|
||||
/**
|
||||
* A list of plugin dependencies to be installed. These are normally set and installed through `Matter.use`.
|
||||
|
|
|
@ -20,30 +20,17 @@ var Common = require('./Common');
|
|||
|
||||
(function() {
|
||||
|
||||
var _requestAnimationFrame,
|
||||
_cancelAnimationFrame;
|
||||
Runner._maxFrameDelta = 1000 / 15;
|
||||
|
||||
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);
|
||||
};
|
||||
Runner._frameDeltaFallback = 1000 / 60;
|
||||
Runner._timeBufferMargin = 1.5;
|
||||
Runner._elapsedNextEstimate = 1;
|
||||
Runner._smoothingLowerBound = 0.1;
|
||||
Runner._smoothingUpperBound = 0.9;
|
||||
|
||||
_cancelAnimationFrame = function() {
|
||||
clearTimeout(_frameTimeout);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Runner. The options parameter is an object that specifies any properties you wish to override the defaults.
|
||||
|
@ -52,23 +39,25 @@ var Common = require('./Common');
|
|||
*/
|
||||
Runner.create = function(options) {
|
||||
var defaults = {
|
||||
fps: 60,
|
||||
deltaSampleSize: 60,
|
||||
counterTimestamp: 0,
|
||||
frameCounter: 0,
|
||||
deltaHistory: [],
|
||||
timePrev: null,
|
||||
delta: 1000 / 60,
|
||||
frameDelta: null,
|
||||
frameDeltaSmoothing: true,
|
||||
frameDeltaSnapping: true,
|
||||
frameDeltaHistory: [],
|
||||
frameDeltaHistorySize: 100,
|
||||
frameRequestId: null,
|
||||
isFixed: false,
|
||||
timeBuffer: 0,
|
||||
timeLastTick: null,
|
||||
maxUpdates: null,
|
||||
maxFrameTime: 1000 / 30,
|
||||
lastUpdatesDeferred: 0,
|
||||
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;
|
||||
// for temporary back compatibility only
|
||||
runner.fps = 0;
|
||||
|
||||
return runner;
|
||||
};
|
||||
|
@ -80,13 +69,10 @@ var Common = require('./Common');
|
|||
*/
|
||||
Runner.run = function(runner, engine) {
|
||||
// create runner if engine is first argument
|
||||
if (typeof runner.positionIterations !== 'undefined') {
|
||||
engine = runner;
|
||||
runner = Runner.create();
|
||||
}
|
||||
runner.timeBuffer = Runner._frameDeltaFallback;
|
||||
|
||||
(function run(time){
|
||||
runner.frameRequestId = _requestAnimationFrame(run);
|
||||
(function onFrame(time){
|
||||
runner.frameRequestId = Runner._onNextFrame(runner, onFrame);
|
||||
|
||||
if (time && runner.enabled) {
|
||||
Runner.tick(runner, engine, time);
|
||||
|
@ -106,54 +92,93 @@ var Common = require('./Common');
|
|||
* @param {number} time
|
||||
*/
|
||||
Runner.tick = function(runner, engine, time) {
|
||||
var timing = engine.timing,
|
||||
delta;
|
||||
var tickStartTime = Common.now(),
|
||||
engineDelta = runner.delta,
|
||||
updateCount = 0;
|
||||
|
||||
if (runner.isFixed) {
|
||||
// fixed timestep
|
||||
delta = runner.delta;
|
||||
} else {
|
||||
var frameDelta = time - runner.timeLastTick;
|
||||
|
||||
if (!frameDelta || !runner.timeLastTick || frameDelta > Math.max(Runner._maxFrameDelta, runner.maxFrameTime)) {
|
||||
// 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;
|
||||
|
||||
// update engine timing object
|
||||
runner.delta = delta;
|
||||
frameDelta = runner.frameDelta || Runner._frameDeltaFallback;
|
||||
}
|
||||
|
||||
if (runner.frameDeltaSmoothing) {
|
||||
// optimistically filter delta over a few frames, to improve stability
|
||||
runner.frameDeltaHistory.push(frameDelta);
|
||||
runner.frameDeltaHistory = runner.frameDeltaHistory.slice(-runner.frameDeltaHistorySize);
|
||||
var deltaHistorySorted = runner.frameDeltaHistory.slice(0).sort();
|
||||
|
||||
// limit delta
|
||||
var deltaHistoryWindow = runner.frameDeltaHistory.slice(
|
||||
deltaHistorySorted.length * Runner._smoothingLowerBound,
|
||||
deltaHistorySorted.length * Runner._smoothingUpperBound
|
||||
);
|
||||
|
||||
// update engine timing object
|
||||
var frameDeltaSmoothed = _mean(deltaHistoryWindow);
|
||||
frameDelta = frameDeltaSmoothed || frameDelta;
|
||||
}
|
||||
|
||||
if (runner.frameDeltaSnapping) {
|
||||
frameDelta = 1000 / Math.round(1000 / frameDelta);
|
||||
}
|
||||
runner.frameDelta = frameDelta;
|
||||
runner.timeLastTick = time;
|
||||
runner.timeBuffer += runner.frameDelta;
|
||||
runner.timeBuffer = Common.clamp(
|
||||
runner.timeBuffer, 0, runner.frameDelta + engineDelta * Runner._timeBufferMargin
|
||||
);
|
||||
runner.lastUpdatesDeferred = 0;
|
||||
var maxUpdates = runner.maxUpdates || Math.ceil(runner.maxFrameTime / engineDelta);
|
||||
// create an event object
|
||||
var event = {
|
||||
timestamp: timing.timestamp
|
||||
timestamp: engine.timing.timestamp
|
||||
};
|
||||
|
||||
Events.trigger(runner, 'beforeTick', event);
|
||||
|
||||
// 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);
|
||||
|
||||
var updateStartTime = Common.now();
|
||||
while (engineDelta > 0 && runner.timeBuffer >= engineDelta * Runner._timeBufferMargin) {
|
||||
// update
|
||||
Events.trigger(runner, 'beforeUpdate', event);
|
||||
|
||||
Engine.update(engine, delta);
|
||||
Engine.update(engine, engineDelta);
|
||||
Events.trigger(runner, 'afterUpdate', event);
|
||||
|
||||
runner.timeBuffer -= engineDelta;
|
||||
updateCount += 1;
|
||||
var elapsedTimeTotal = Common.now() - tickStartTime,
|
||||
elapsedTimeUpdates = Common.now() - updateStartTime,
|
||||
elapsedNextEstimate = elapsedTimeTotal + Runner._elapsedNextEstimate * elapsedTimeUpdates / updateCount;
|
||||
if (updateCount >= maxUpdates || elapsedNextEstimate > runner.maxFrameTime) {
|
||||
runner.lastUpdatesDeferred = Math.round(Math.max(0, (runner.timeBuffer / engineDelta) - Runner._timeBufferMargin));
|
||||
break;
|
||||
}
|
||||
}
|
||||
engine.timing.lastUpdatesPerFrame = updateCount;
|
||||
Events.trigger(runner, 'afterTick', event);
|
||||
if (runner.frameDeltaHistory.length >= 100) {
|
||||
if (runner.lastUpdatesDeferred && Math.round(runner.frameDelta / engineDelta) > maxUpdates) {
|
||||
Common.warnOnce('Matter.Runner: runner reached runner.maxUpdates, see docs.');
|
||||
} else if (runner.lastUpdatesDeferred) {
|
||||
Common.warnOnce('Matter.Runner: runner reached runner.maxFrameTime, see docs.');
|
||||
}
|
||||
if (typeof runner.isFixed !== 'undefined') {
|
||||
Common.warnOnce('Matter.Runner: runner.isFixed is now redundant, see docs.');
|
||||
}
|
||||
if (runner.deltaMin || runner.deltaMax) {
|
||||
Common.warnOnce('Matter.Runner: runner.deltaMin and runner.deltaMax were removed, see docs.');
|
||||
}
|
||||
if (runner.fps !== 0) {
|
||||
Common.warnOnce('Matter.Runner: runner.fps was replaced by runner.delta, see docs.');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -163,7 +188,7 @@ var Common = require('./Common');
|
|||
* @param {runner} runner
|
||||
*/
|
||||
Runner.stop = function(runner) {
|
||||
_cancelAnimationFrame(runner.frameRequestId);
|
||||
Runner._cancelNextFrame(runner);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -172,10 +197,30 @@ var Common = require('./Common');
|
|||
* @param {runner} runner
|
||||
* @param {engine} engine
|
||||
*/
|
||||
Runner.start = function(runner, engine) {
|
||||
Runner.run(runner, engine);
|
||||
Runner._onNextFrame = function(runner, callback) {
|
||||
if (typeof window !== 'undefined' && window.requestAnimationFrame) {
|
||||
runner.frameRequestId = window.requestAnimationFrame(callback);
|
||||
} else {
|
||||
throw new Error('Matter.Runner: missing required global window.requestAnimationFrame.');
|
||||
}
|
||||
return runner.frameRequestId;
|
||||
};
|
||||
Runner._cancelNextFrame = function(runner) {
|
||||
if (typeof window !== 'undefined' && window.cancelAnimationFrame) {
|
||||
window.cancelAnimationFrame(runner.frameRequestId);
|
||||
} else {
|
||||
throw new Error('Matter.Runner: missing required global window.cancelAnimationFrame.');
|
||||
}
|
||||
};
|
||||
|
||||
var _mean = function(values) {
|
||||
var result = 0,
|
||||
valuesLength = values.length;
|
||||
for (var i = 0; i < valuesLength; i += 1) {
|
||||
result += values[i];
|
||||
}
|
||||
return (result / valuesLength) || 0;
|
||||
};
|
||||
/*
|
||||
*
|
||||
* Events Documentation
|
||||
|
|
|
@ -69,6 +69,9 @@ var Vector = require('../geometry/Vector');
|
|||
Bodies.trapezoid = function(x, y, width, height, slope, options) {
|
||||
options = options || {};
|
||||
|
||||
if (slope >= 1) {
|
||||
Common.warn('Bodies.trapezoid: slope parameter must be < 1.');
|
||||
}
|
||||
slope *= 0.5;
|
||||
var roof = (1 - (slope * 2)) * width;
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ var MatterCollisionEvents = {
|
|||
|
||||
name: 'matter-collision-events',
|
||||
version: '0.1.6',
|
||||
for: 'matter-js@^0.19.0',
|
||||
for: 'matter-js@^0.20.0',
|
||||
silent: true,
|
||||
|
||||
install: function (matter)
|
||||
|
|
|
@ -548,15 +548,19 @@ var Vector = require('../geometry/Vector');
|
|||
elapsedHistory = timing.elapsedHistory,
|
||||
timestampElapsedHistory = timing.timestampElapsedHistory,
|
||||
engineDeltaHistory = timing.engineDeltaHistory,
|
||||
engineUpdatesHistory = timing.engineUpdatesHistory,
|
||||
engineElapsedHistory = timing.engineElapsedHistory,
|
||||
lastEngineUpdatesPerFrame = engine.timing.lastUpdatesPerFrame,
|
||||
lastEngineDelta = engine.timing.lastDelta;
|
||||
|
||||
var deltaMean = _mean(deltaHistory),
|
||||
elapsedMean = _mean(elapsedHistory),
|
||||
engineDeltaMean = _mean(engineDeltaHistory),
|
||||
engineUpdatesMean = _mean(engineUpdatesHistory),
|
||||
engineElapsedMean = _mean(engineElapsedHistory),
|
||||
timestampElapsedMean = _mean(timestampElapsedHistory),
|
||||
rateMean = (timestampElapsedMean / deltaMean) || 0,
|
||||
neededUpdatesPerFrame = Math.round(deltaMean / lastEngineDelta),
|
||||
fps = (1000 / deltaMean) || 0;
|
||||
|
||||
var graphHeight = 4,
|
||||
|
@ -568,7 +572,7 @@ var Vector = require('../geometry/Vector');
|
|||
|
||||
// background
|
||||
context.fillStyle = '#0e0f19';
|
||||
context.fillRect(0, 50, gap * 4 + width * 5 + 22, height);
|
||||
context.fillRect(0, 50, gap * 5 + width * 6 + 22, height);
|
||||
|
||||
// show FPS
|
||||
Render.status(
|
||||
|
@ -586,17 +590,23 @@ var Vector = require('../geometry/Vector');
|
|||
function(i) { return (engineDeltaHistory[i] / engineDeltaMean) - 1; }
|
||||
);
|
||||
|
||||
Render.status(
|
||||
context, x + (gap + width) * 2, y, width, graphHeight, engineUpdatesHistory.length,
|
||||
lastEngineUpdatesPerFrame + ' upf',
|
||||
Math.pow(Common.clamp((engineUpdatesMean / neededUpdatesPerFrame) || 1, 0, 1), 4),
|
||||
function(i) { return (engineUpdatesHistory[i] / engineUpdatesMean) - 1; }
|
||||
);
|
||||
// show engine update time
|
||||
Render.status(
|
||||
context, x + (gap + width) * 2, y, width, graphHeight, engineElapsedHistory.length,
|
||||
context, x + (gap + width) * 3, y, width, graphHeight, engineElapsedHistory.length,
|
||||
engineElapsedMean.toFixed(2) + ' ut',
|
||||
1 - (engineElapsedMean / Render._goodFps),
|
||||
1 - (lastEngineUpdatesPerFrame * engineElapsedMean / Render._goodFps),
|
||||
function(i) { return (engineElapsedHistory[i] / engineElapsedMean) - 1; }
|
||||
);
|
||||
|
||||
// show render time
|
||||
Render.status(
|
||||
context, x + (gap + width) * 3, y, width, graphHeight, elapsedHistory.length,
|
||||
context, x + (gap + width) * 4, y, width, graphHeight, elapsedHistory.length,
|
||||
elapsedMean.toFixed(2) + ' rt',
|
||||
1 - (elapsedMean / Render._goodFps),
|
||||
function(i) { return (elapsedHistory[i] / elapsedMean) - 1; }
|
||||
|
@ -604,7 +614,7 @@ var Vector = require('../geometry/Vector');
|
|||
|
||||
// show effective speed
|
||||
Render.status(
|
||||
context, x + (gap + width) * 4, y, width, graphHeight, timestampElapsedHistory.length,
|
||||
context, x + (gap + width) * 5, y, width, graphHeight, timestampElapsedHistory.length,
|
||||
rateMean.toFixed(2) + ' x',
|
||||
rateMean * rateMean * rateMean,
|
||||
function(i) { return (((timestampElapsedHistory[i] / deltaHistory[i]) / rateMean) || 0) - 1; }
|
||||
|
@ -1205,8 +1215,8 @@ var Vector = require('../geometry/Vector');
|
|||
continue;
|
||||
|
||||
collision = pair.collision;
|
||||
for (j = 0; j < pair.activeContacts.length; j++) {
|
||||
var contact = pair.activeContacts[j],
|
||||
for (j = 0; j < pair.contactCount; j++) {
|
||||
var contact = pair.contacts[j],
|
||||
vertex = contact.vertex;
|
||||
c.rect(vertex.x - 1.5, vertex.y - 1.5, 3.5, 3.5);
|
||||
}
|
||||
|
@ -1230,13 +1240,13 @@ var Vector = require('../geometry/Vector');
|
|||
|
||||
collision = pair.collision;
|
||||
|
||||
if (pair.activeContacts.length > 0) {
|
||||
var normalPosX = pair.activeContacts[0].vertex.x,
|
||||
normalPosY = pair.activeContacts[0].vertex.y;
|
||||
if (pair.contactCount > 0) {
|
||||
var normalPosX = pair.contacts[0].vertex.x,
|
||||
normalPosY = pair.contacts[0].vertex.y;
|
||||
|
||||
if (pair.activeContacts.length === 2) {
|
||||
normalPosX = (pair.activeContacts[0].vertex.x + pair.activeContacts[1].vertex.x) / 2;
|
||||
normalPosY = (pair.activeContacts[0].vertex.y + pair.activeContacts[1].vertex.y) / 2;
|
||||
if (pair.contactCount === 2) {
|
||||
normalPosX = (pair.contacts[0].vertex.x + pair.contacts[1].vertex.x) / 2;
|
||||
normalPosY = (pair.contacts[0].vertex.y + pair.contacts[1].vertex.y) / 2;
|
||||
}
|
||||
|
||||
if (collision.bodyB === collision.supports[0].body || collision.bodyA.isStatic === true) {
|
||||
|
@ -1429,6 +1439,8 @@ var Vector = require('../geometry/Vector');
|
|||
timing.timestampElapsedHistory.unshift(timing.timestampElapsed);
|
||||
timing.timestampElapsedHistory.length = Math.min(timing.timestampElapsedHistory.length, historySize);
|
||||
|
||||
timing.engineUpdatesHistory.unshift(engine.timing.lastUpdatesPerFrame);
|
||||
timing.engineUpdatesHistory.length = Math.min(timing.engineUpdatesHistory.length, historySize);
|
||||
timing.engineElapsedHistory.unshift(engine.timing.lastElapsed);
|
||||
timing.engineElapsedHistory.length = Math.min(timing.engineElapsedHistory.length, historySize);
|
||||
|
||||
|
|
Loading…
Reference in a new issue