Introducing Box2D filtering
In Box2D, normally every object collides with other objects in the stage.
But sometimes we may need a way to make an object collide with only certain objects, ignoring the rest. I am not talking about sensors, but real objects that won’t collide with certain other objects.
This feature can be done with filtering.
Collision filtering is a system for preventing collision between shapes. For example, say you make a character that rides a bicycle. You want the bicycle to collide with the terrain and the character to collide with the terrain, but you don’t want the character to collide with the bicycle (because they must overlap). Box2D supports such collision filtering using categories and groups.
Box2D supports 16 collision categories. For each shape you can specify which category it belongs to. You also specify what other categories this shape can collide with. For example, you could specify in a multiplayer game that all players don’t collide with each other and monsters don’t collide with each other, but players and monsters should collide. This is done with masking bits.
The following script is the same we saw a Understanding how Box2D manages boundaries but I am using a random filtering to make the ball ignore the ramp, the wall, or none of them.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | package { import flash.display.Sprite; import flash.events.Event; import Box2D.Dynamics.*; import Box2D.Collision.*; import Box2D.Collision.Shapes.*; import Box2D.Common.Math.*; public class bound extends Sprite { var body:b2Body; public var m_world:b2World; public var m_iterations:int=10; public var m_timeStep:Number=1.0/30.0; var m_boundaryListener=new b2BoundaryListener(); var bodyDef:b2BodyDef; var boxDef:b2PolygonDef; var circleDef:b2CircleDef; public function bound() { addEventListener(Event.ENTER_FRAME, Update, false, 0, true); var worldAABB:b2AABB = new b2AABB(); worldAABB.lowerBound.Set(-100.0, -100.0); worldAABB.upperBound.Set(100.0, 100.0); var gravity:b2Vec2=new b2Vec2(0.0,10.0); var doSleep:Boolean=true; m_world=new b2World(worldAABB,gravity,doSleep); m_world.SetBoundaryListener(m_boundaryListener); // debug draw start var m_sprite:Sprite; m_sprite = new Sprite(); addChild(m_sprite); var dbgDraw:b2DebugDraw = new b2DebugDraw(); var dbgSprite:Sprite = new Sprite(); m_sprite.addChild(dbgSprite); dbgDraw.m_sprite=m_sprite; dbgDraw.m_drawScale=30; dbgDraw.m_alpha=1; dbgDraw.m_fillAlpha=0.5; dbgDraw.m_lineThickness=1; dbgDraw.m_drawFlags=b2DebugDraw.e_shapeBit; m_world.SetDebugDraw(dbgDraw); // ground bodyDef = new b2BodyDef(); bodyDef.position.Set(4, 12); boxDef = new b2PolygonDef(); boxDef.filter.categoryBits=2; boxDef.SetAsOrientedBox(10, 1,new b2Vec2(5, 1.5), Math.PI/32); boxDef.friction=0.3; boxDef.density=0; body=m_world.CreateBody(bodyDef); body.CreateShape(boxDef); body.SetMassFromShapes(); // another object bodyDef = new b2BodyDef(); bodyDef.position.Set(8, 12); boxDef = new b2PolygonDef(); boxDef.filter.categoryBits=4; boxDef.SetAsBox(1, 3); boxDef.friction=0.3; boxDef.density=0; body=m_world.CreateBody(bodyDef); body.CreateShape(boxDef); body.SetMassFromShapes(); // circle create_circle(); } public function create_circle() { bodyDef = new b2BodyDef(); bodyDef.position.x=6; bodyDef.position.y=2; circleDef = new b2CircleDef(); circleDef.filter.maskBits=Math.ceil(Math.random()*3)*2; circleDef.radius=2; circleDef.density=1.0; circleDef.friction=0.5; circleDef.restitution=0.2; body=m_world.CreateBody(bodyDef); body.CreateShape(circleDef); body.SetMassFromShapes(); } public function Update(e:Event):void { m_world.Step(m_timeStep, m_iterations); if (m_boundaryListener.get_contact()) { m_boundaryListener.no_contact(); m_world.DestroyBody(body); bodyDef = new b2BodyDef(); create_circle(); } } } } |
Let’s see the new lines:
Line 44: Assigning the ground object the collision category 2 using filter.categoryBits
Line 55: Assigning the obstacle the collision category 4 using filter.categoryBits
Remember categories must follow the power of two so next group would be 8 and not 5
Line 70: randomly assigning 2, 4 or 6 to ball collision mask using filter.maskBits. When it’s 2, the ball will collide only with objects in category 2, when it’s 4 the ball will collide only with objects in category 4 and when it’s 6 the ball will collide with objects both in category 2 and 4, because 4+2=6.
And this is the result:
Watch the ball changing its behavior according to objects it can collide with.
No need to download source code, just replace the code in Understanding how Box2D manages boundaries
They can be easily customized to meet the unique requirements of your project.















(7 votes, average: 4.43 out of 5)









This post has 7 comments
Cataclysm Studios
You should really add a play button on your movies, like how Tony Pa does it.
maw
Hey, my antivir found a TROJAN (JS.Pakes.bh) on your blog! Be carefully!
pwrtoppl
interesting stuff about the ability to filter per other objects, box2d is pretty cool
Anthony Sapp
The best, most straightforward explanation of this concept I have found. Thanks!
som911
how to change maskBit in b2CirlceDef ???
naveen
really helped a lot in making bodies not to collide certain bodies and collide certain bodies….
CircusMice! A Game in Three Acts « Circus Mice
[...] called masking. Unfortunately I know nothing about this other than it exists… helpfully Emanuele has a tutorial on it, plus there’s the Box2D manuals and forums. Share [...]