Having a
lot of bodies in the level at the same time can significantly impact
framerates. Especially collision processing is a big burden to process. Add to
that the fact that it’s usually necessary for gameplay to not have all bodies
collide (for instance, bricks flying from a building should not collide with
the player but should collide with the terrain): a way to filter these
collisions is a necessity. Luckily, Box2D has a way to do this.
Every Box2D fixture has two bitfields (a
bitfield consisting of 16 bits) for this task: a mask bit and a category bit. To
determine whether fixtures collide, the following statement is checked:
uint16 catA = fixtureA.filter.categoryBits; uint16 maskA = fixtureA.filter.maskBits; uint16 catB = fixtureB.filter.categoryBits; uint16 maskB = fixtureB.filter.maskBits; if ((catA & maskB) != 0 && (catB & maskA) != 0) { // fixtures can collide }
The if
statement checks if both the category bitfield of fixture A and the mask
bitfield of fixture B have more than one 1 in common, and the same for the
category bitfield of fixture B and mask bitfield of fixture A. If both are
true, the fixtures collide.
Let’s clarify this with an example:
Fixture A
Category
bitfield:
0x0001
Mask
bitfield:
CatB|CatC
Fixture B
Category bitfield:
0x0002
Mask bitfield:
CatA
CatA
Fixture C
Category bitfield:
0x0004
Mask bitfield:
CatA
CatA
Notice the
way the bitfields are written. Each bitfield consist of 4x4 bits. Every digit
represents a power of 2 in one of the four blocks of bits. Written fully, the category
bitfield of fixture A would be:
0000 0000
0000 0001
Fixture C’s
category would be:
0000 0000
0000 0100
This way,
the category bitfields can range from 0x0001 to 0x8000, thus allowing for 16
categories.
Now, lets see how the fixtures will collide.
The mask bit of A is: CatB|CatC. This means that both bitfields are combined;
Category B = 0000 0000 0000 0010
Category C = 0000 0000 0000 0100
Mask A
= 0000 0000 0000 0110
This
results in A colliding with B and C, and B and C not colliding with each other. A mask of -1 means the fixture will collide
with all objects, a mask of 0x000 means the fixture collides with no objects.
For extra control or in order to set
up collision filtering without bitfields, collision group indexes can be used.
A combination of both systems can also be used, for instance when fixtures of a
tank should all collide with the same fixtures, but not with each other.
Group indexes work as follows (taken
from the Box2d manual):
Collision
groups let you specify an integral group index. You can have all fixtures with
the same group index always collide (positive index) or never collide (negative
index). Group indices are usually used for things that are somehow related,
like the parts of a bicycle. In the following example, fixture1 and fixture2
always collide, but fixture3 and fixture4 never collide.
fixture1Def.filter.groupIndex = 2; fixture2Def.filter.groupIndex = 2; fixture3Def.filter.groupIndex = -8; fixture4Def.filter.groupIndex = -8;
Collisions
between fixtures of different group indices are filtered according the category
and mask bits. In other words, group filtering has higher precedence than
category filtering.
Note that group indexes are noted in integers, not
bitfields. This is
easier to learn, but less powerful than using categories and masks as is
doesn’t allow for collision filtering between groups.
As you can see, setting up collision filtering
in code is fairly straightforward, once you understand the principle. One
difficulty we experienced was that SpriteHelper (the spritesheet & physics
editor used for Tank Rampage) notes
mask and category bits as integers. This results in a tedious process of
determining collision masks on paper and converting them back to bitfields
instead of being able to do this programmagiacally.
Please let me know if you want any more info on this subject, or perhaps a tutorial?
Please let me know if you want any more info on this subject, or perhaps a tutorial?
No comments:
Post a Comment