Box2D C++ tutorials - World querying
Last edited: July 14 2013Chinese version -> 中文
World querying
Often you will want to know what entities are in a given part of the scene. For example if a bomb goes off, everything in the vicinity should take some damage, or in an RTS type game you might want to let the user select units by dragging a rectangle around them. The method of quickly checking what objects are in that part of the world to use for further detailed processing is known as 'world querying'.
Box2D provides two tools for this - ray casting and AABB testing. Ray casting... didn't we just do that? Yes, we did it the manual way, by looping through every fixture in the world and checking the ray against them all to find out which one was the closest. This can be very inefficient when you have a large number of fixtures in the scene. A better way is to use the RayCast function of the world itself. This allows the engine to focus on fixtures which it knows are near the ray's path.
Ray casting, the efficient way
Since we have already looked at ray casting in some detail, we'll just take a quick look at the world's RayCast function without making a demonstration. The function looks like this:
1 | void RayCast(b2RayCastCallback* callback, const b2Vec2& point1, const b2Vec2& point2); |
1 2 | //in b2RayCastCallback class float32 ReportFixture(b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, float32 fraction); |
The final point to cover is the return value that you should give for this callback function. Remember that if your ray is long enough there could be many fixtures that it intersects with, and your callback could be called many times during one RayCast. Very importantly, this raycast does not detect fixtures in order of nearest to furthest, it just gives them to you in any old order - this helps it to be efficient for very large scenes. The engine lets you decide how you want to deal with each fixture as it is encountered. This is where the the return value of the callback comes in. You will return a floating point value, which you can think of as adjusting the length of the ray while the raycast is in progress. This can be a little confusing so take it slow...
- return -1 to completely ignore the current intersection
- return a value from 0 - 1 to adjust the length of the ray, for example:
- returning 0 says there is now no ray at all
- returning 1 says that the ray length does not change
- returning the fraction value makes the ray just long enough to hit the intersected fixture
- To find only the closest intersection:
- return the fraction value from the callback
- use the most recent intersection as the result - To find all intersections along the ray:
- return 1 from the callback
- store the intersections in a list - To simply find if the ray hits anything:
- if you get a callback, something was hit (but it may not be the closest)
- return 0 from the callback for efficiency
Area querying (aka AABB querying)
The Box2D world has another function for finding fixtures overlapping a given area, the AABB query. This one allows us to define a rectangular region, and the engine will then find all fixtures in that region and call a callback function for each of them. Typically the callback is used to fill a list of fixtures in preparation for some other processing to follow, but this method allows you to implement other kinds of processsing efficiently too. For example, if there are a large number of fixtures in the given area but all you want to do is find out if their total mass is more than a certain amount, you could add their masses as the query progressed, and as soon as you find the necessary mass, the query can finish.
The function for this is called QueryAABB and it gets the name because the area must be specified as an 'axis-aligned bounding box'. This just means that the area is rectangular and can't be rotated at an odd angle. It also means that instead of testing whether fixtures are within the area, it tests if the AABBs of fixtures are within the area. You can turn on rendering of the AABBs for fixtures in the testbed with one of the checkboxes on the right. Here is an example of the AABBs (purple) of some fixtures.

1 | void QueryAABB(b2QueryCallback* callback, const b2AABB& aabb); |
1 | bool ReportFixture(b2Fixture* fixture); |
We will set up a test where we define a rectangular area, and then draw marker on each fixture which is currently overlapping the area. We'll use the same scene as at the beginning of the last topic, so go copy the code from there.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //FooTest class member variable b2Vec2 mouseDownPos, mouseUpPos; //FooTest class functions void MouseDown(const b2Vec2& p) { mouseDownPos = mouseUpPos = p; Test::MouseDown(p); } void MouseUp(const b2Vec2& p) { mouseUpPos = p; Test::MouseUp(p); } //in Step() function, draw the rectangle b2Vec2 lower( min(mouseDownPos.x,mouseUpPos.x), min(mouseDownPos.y,mouseUpPos.y) ); b2Vec2 upper( max(mouseDownPos.x,mouseUpPos.x), max(mouseDownPos.y,mouseUpPos.y) ); glColor3f(1,1,1);//white glBegin(GL_LINE_LOOP); glVertex2f(lower.x, lower.y); glVertex2f(upper.x, lower.y); glVertex2f(upper.x, upper.y); glVertex2f(lower.x, upper.y); glEnd(); |

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 | //subclass b2QueryCallback class MyQueryCallback : public b2QueryCallback { public: std::vector<b2Body*> foundBodies; bool ReportFixture(b2Fixture* fixture) { foundBodies.push_back( fixture->GetBody() ); return true;//keep going to find all fixtures in the query area } }; //in Step() function MyQueryCallback queryCallback; b2AABB aabb; aabb.lowerBound = lower; aabb.upperBound = upper; m_world->QueryAABB( &queryCallback, aabb ); //draw a point on each body in the query area glPointSize(6); glBegin(GL_POINTS); for (int i = 0; i < queryCallback.foundBodies.size(); i++) { b2Vec2 pos = queryCallback.foundBodies[i]->GetPosition(); glVertex2f( pos.x, pos.y ); } glEnd(); |
