diff --git a/demo/dev.html b/demo/dev.html
index de17387..63bcb67 100644
--- a/demo/dev.html
+++ b/demo/dev.html
@@ -49,6 +49,7 @@
+
@@ -61,7 +62,6 @@
-
diff --git a/demo/js/Demo.js b/demo/js/Demo.js
index bd3bf94..fc0304d 100644
--- a/demo/js/Demo.js
+++ b/demo/js/Demo.js
@@ -448,12 +448,12 @@
Demo.chains = function() {
var _world = _engine.world,
- groupId = Body.nextNonCollidingGroupId();
+ group = Body.nextGroup(true);
Demo.reset();
var ropeA = Composites.stack(200, 100, 5, 2, 10, 10, function(x, y, column, row) {
- return Bodies.rectangle(x, y, 50, 20, { collisionFilter: {group: groupId} });
+ return Bodies.rectangle(x, y, 50, 20, { collisionFilter: { group: group } });
});
Composites.chain(ropeA, 0.5, 0, -0.5, 0, { stiffness: 0.8, length: 2 });
@@ -466,10 +466,10 @@
World.add(_world, ropeA);
- groupId = Body.nextNonCollidingGroupId();
+ group = Body.nextGroup(true);
var ropeB = Composites.stack(500, 100, 5, 2, 10, 10, function(x, y, column, row) {
- return Bodies.circle(x, y, 20, { collisionFilter: {group: groupId} });
+ return Bodies.circle(x, y, 20, { collisionFilter: { group: group } });
});
Composites.chain(ropeB, 0.5, 0, -0.5, 0, { stiffness: 0.8, length: 2 });
@@ -485,12 +485,12 @@
Demo.bridge = function() {
var _world = _engine.world,
- groupId = Body.nextNonCollidingGroupId();
+ group = Body.nextGroup(true);
Demo.reset();
var bridge = Composites.stack(150, 300, 9, 1, 10, 10, function(x, y, column, row) {
- return Bodies.rectangle(x, y, 50, 20, { collisionFilter: {group: groupId} });
+ return Bodies.rectangle(x, y, 50, 20, { collisionFilter: { group: group } });
});
Composites.chain(bridge, 0.5, 0, -0.5, 0, { stiffness: 0.9 });
@@ -937,8 +937,8 @@
Demo.reset();
- var groupId = Body.nextNonCollidingGroupId(),
- particleOptions = { friction: 0.00001, collisionFilter: { group: groupId }, render: { visible: false }},
+ var group = Body.nextGroup(true),
+ particleOptions = { friction: 0.00001, collisionFilter: { group: group }, render: { visible: false }},
cloth = Composites.softBody(200, 200, 20, 12, 5, 5, false, 8, particleOptions);
for (var i = 0; i < 20; i++) {
@@ -1234,39 +1234,92 @@
Demo.collisionFiltering = function() {
var _world = _engine.world;
+ // define our categories (as bit fields, there are up to 32 available)
+
+ var defaultCategory = 0x0001,
+ redCategory = 0x0002,
+ greenCategory = 0x0004,
+ blueCategory = 0x0008;
+
+ var redColor = '#C44D58',
+ blueColor = '#4ECDC4',
+ greenColor = '#C7F464';
+
Demo.reset();
- var i;
+ // create a stack with varying body categories (but these bodies can all collide with each other)
World.add(_world,
Composites.stack(275, 150, 5, 10, 10, 10, function(x, y, column, row) {
+ var category = redCategory,
+ color = redColor;
+
+ if (row > 5) {
+ category = blueCategory;
+ color = blueColor;
+ } else if (row > 2) {
+ category = greenCategory;
+ color = greenColor;
+ }
+
return Bodies.circle(x, y, 20, {
collisionFilter: {
- category: row < 7 ? 2 : 4
+ category: category
},
render: {
- strokeStyle: row < 7 ? 'red' : 'green',
+ strokeStyle: color,
fillStyle: 'transparent'
}
});
})
);
+ // this body will only collide with the walls and the green bodies
+
+ World.add(_world,
+ Bodies.circle(310, 40, 30, {
+ collisionFilter: {
+ mask: defaultCategory | greenCategory
+ },
+ render: {
+ strokeStyle: Common.shadeColor(greenColor, -20),
+ fillStyle: greenColor
+ }
+ })
+ );
+
+ // this body will only collide with the walls and the red bodies
+
World.add(_world,
Bodies.circle(400, 40, 30, {
collisionFilter: {
- mask: 5
+ mask: defaultCategory | redCategory
},
render: {
- fillStyle: 'blue'
+ strokeStyle: Common.shadeColor(redColor, -20),
+ fillStyle: redColor
+ }
+ })
+ );
+
+ // this body will only collide with the walls and the blue bodies
+
+ World.add(_world,
+ Bodies.circle(480, 40, 30, {
+ collisionFilter: {
+ mask: defaultCategory | blueCategory
+ },
+ render: {
+ strokeStyle: Common.shadeColor(blueColor, -20),
+ fillStyle: blueColor
}
})
);
var renderOptions = _engine.render.options;
renderOptions.wireframes = false;
- renderOptions.background = '#111';
- renderOptions.showCollisions = true;
+ renderOptions.background = '#222';
+ renderOptions.showAngleIndicator = false;
};
// the functions for the demo interface and controls below
diff --git a/src/body/Body.js b/src/body/Body.js
index c7831d9..4cbe2ff 100644
--- a/src/body/Body.js
+++ b/src/body/Body.js
@@ -51,7 +51,7 @@ var Body = {};
friction: 0.1,
frictionAir: 0.01,
collisionFilter: {
- category: 1,
+ category: 0x0001,
mask: 0xFFFFFFFF,
group: 0
},
@@ -75,21 +75,18 @@ var Body = {};
};
/**
- * Returns the next unique groupID number for which bodies will collide.
- * @method nextCollidingGroupId
- * @return {Number} Unique groupID
+ * Returns the next unique group index for which bodies will collide.
+ * If `isNonColliding` is `true`, returns the next unique group index for which bodies will _not_ collide.
+ * See `body.collisionFilter` for more information.
+ * @method nextGroup
+ * @param {bool} [isNonColliding=false]
+ * @return {Number} Unique group index
*/
- Body.nextCollidingGroupId = function() {
- return _nextCollidingGroupId++;
- };
+ Body.nextGroup = function(isNonColliding) {
+ if (isNonColliding)
+ return _nextNonCollidingGroupId--;
- /**
- * Returns the next collisionFilter.group value for which bodies will not collide.
- * @method nextNonCollidingGroupId
- * @return {Number} Unique groupID
- */
- Body.nextNonCollidingGroupId = function() {
- return _nextNonCollidingGroupId--;
+ return _nextCollidingGroupId++;
};
/**
@@ -699,6 +696,7 @@ var Body = {};
* value is used as a bit field and the category should have only one bit set, meaning that
* the value of this property is a power of two in the range [1, 2^31]. Thus, there are 32
* different collision categories available.
+ *
* Each body also defines a collision bitmask, given by `collisionFilter.mask` which specifies
* the categories it collides with (the value is the bitwise AND value of all these categories).
*
@@ -711,7 +709,7 @@ var Body = {};
*/
/**
- * An Integer `Number`, see `collisionFilter`
+ * An Integer `Number`, see `body.collisionFilter` for more information.
*
* @property collisionFilter.group
* @type object
@@ -719,7 +717,10 @@ var Body = {};
*/
/**
- * An Integer `Number`, see `collisionFilter`
+ * A bit field that specifies the collision category this body belongs to.
+ * The category value should have only one bit set, for example `0x0001`.
+ * This means there are up to 32 unique collision categories available.
+ * See `body.collisionFilter` for more information.
*
* @property collisionFilter.category
* @type object
@@ -727,7 +728,7 @@ var Body = {};
*/
/**
- * An Integer `Number`, see `collisionFilter`
+ * An Integer `Number`, see `body.collisionFilter` for more information.
*
* @property collisionFilter.mask
* @type object
diff --git a/src/collision/Detector.js b/src/collision/Detector.js
index 879308e..aa46820 100644
--- a/src/collision/Detector.js
+++ b/src/collision/Detector.js
@@ -26,13 +26,10 @@ var Detector = {};
var bodyA = broadphasePairs[i][0],
bodyB = broadphasePairs[i][1];
- var collisionFilterA = bodyA.collisionFilter,
- collisionFilterB = bodyB.collisionFilter;
-
if ((bodyA.isStatic || bodyA.isSleeping) && (bodyB.isStatic || bodyB.isSleeping))
continue;
- if (!_pairCollides(collisionFilterA, collisionFilterB))
+ if (!Detector.canCollide(bodyA.collisionFilter, bodyB.collisionFilter))
continue;
metrics.midphaseTests += 1;
@@ -88,11 +85,11 @@ var Detector = {};
// NOTE: could share a function for the below, but may drop performance?
- if (bodyA.groupId && bodyB.groupId && bodyA.groupId === bodyB.groupId)
- continue;
-
if ((bodyA.isStatic || bodyA.isSleeping) && (bodyB.isStatic || bodyB.isSleeping))
continue;
+
+ if (!Detector.canCollide(bodyA.collisionFilter, bodyB.collisionFilter))
+ continue;
metrics.midphaseTests += 1;
@@ -128,12 +125,20 @@ var Detector = {};
return collisions;
};
-
- var _pairCollides = function(filterA, filterB) {
+
+ /**
+ * Returns `true` if both supplied collision filters will allow a collision to occur.
+ * See `body.collisionFilter` for more information.
+ * @method canCollide
+ * @param {} filterA
+ * @param {} filterB
+ * @return {bool} `true` if collision can occur
+ */
+ Detector.canCollide = function(filterA, filterB) {
if (filterA.group === filterB.group && filterA.group !== 0)
return filterA.group > 0;
- return ((filterA.mask & filterB.category) !== 0 && (filterB.mask & filterA.category) !== 0);
+ return (filterA.mask & filterB.category) !== 0 && (filterB.mask & filterA.category) !== 0;
};
})();
diff --git a/src/factory/Composites.js b/src/factory/Composites.js
index 688d835..fea5eb7 100644
--- a/src/factory/Composites.js
+++ b/src/factory/Composites.js
@@ -226,7 +226,7 @@ var Composites = {};
* @return {composite} A new composite car body
*/
Composites.car = function(xx, yy, width, height, wheelSize) {
- var collisionFilterGroup = Body.nextNonCollidingGroupId(),
+ var group = Body.nextGroup(true),
wheelBase = -20,
wheelAOffset = -width * 0.5 + wheelBase,
wheelBOffset = width * 0.5 - wheelBase,
@@ -235,7 +235,7 @@ var Composites = {};
var car = Composite.create({ label: 'Car' }),
body = Bodies.trapezoid(xx, yy, width, height, 0.3, {
collisionFilter: {
- group: collisionFilterGroup
+ group: group
},
friction: 0.01,
chamfer: {
@@ -245,7 +245,7 @@ var Composites = {};
var wheelA = Bodies.circle(xx + wheelAOffset, yy + wheelYOffset, wheelSize, {
collisionFilter: {
- group: collisionFilterGroup
+ group: group
},
restitution: 0.5,
friction: 0.9,
@@ -254,7 +254,7 @@ var Composites = {};
var wheelB = Bodies.circle(xx + wheelBOffset, yy + wheelYOffset, wheelSize, {
collisionFilter: {
- group: collisionFilterGroup
+ group: group
},
restitution: 0.5,
friction: 0.9,