Box2D C++ tutorials - Anatomy of a collision

Last edited: May 07 2014

Chinese version -> 中文
Russian version -> русская версия

What's in a collision?


In Box2D it's common to think of bodies colliding with each other, but it's really the fixtures which are used to detect when a collision occurs. Collisions can happen in all kinds of ways so they have a lot of information that can be used in the game logic. For example, you might want to know:
  • when a collision starts and ends
  • what point on the fixtures is touching
  • the normal vector of the contact between the fixtures
  • how much energy was involved and how the collision was responded to
Usually collisions happen very quickly, but in this topic we are going to take one collision and slow it riiiight down so we can take a look at the details of what is going on, and all the possible information we can get from it.

The scenario will be two polygon fixtures colliding, in a zero-gravity world so we can control it easier. One fixture is a stationary box, the other is a triangle moving horizontally towards the box. Anatomy of a collision This scenario is set up so that the bottom of the triangle will just collide with the top corner of the box. For this topic the finer details of this arrangement are not important, the focus is on what kind of information we can get at each step of the process so I will direct you to the source code if you'd like to replicate this.


Getting info about collisions


Information about a collision is contained in a b2Contact object. From this you can check which two fixtures are colliding, and find out about the location and direction of the collision reaction. There are two main ways you can get these b2Contact objects from Box2D. One is to look at the current list of contacts for each body, and the other is to use a contact listener. Let's take a quick look at each of these so we know what we're talking about in the rest of this topic.

  • Checking the list of contacts
    You can look at all the contacts in the world anytime by checking the world's contact list:
    1
    2
    
      for (b2Contact* contact = world->GetContactList(); contact; contact = contact->GetNext())
          contact->... //do something with the contact
    Or you can look at the contacts for a single body:
    1
    2
    
      for (b2ContactEdge* edge = body->GetContactList(); edge; edge = edge->next)
          edge->contact->... //do something with the contact
    A very important point to note if you do this, is that the existence of a contact in these lists does not mean that the two fixtures of the contact are actually touching - it only means their AABBs are touching. If you want to know if the fixtures themselves are really touching you can use IsTouching() to check. Moron that later.


  • Contact listeners
    Checking the list of contacts becomes inefficient for large scenarios where many collisions are occuring frequently. Setting a contact listener allows you to have Box2D tell you when something interesting happens, rather than you doing all the work of keeping track of when things start and stop touching. A contact listener is a class (b2ContactListener) with four functions which you override as necessary.
    1
    2
    3
    4
    
      void BeginContact(b2Contact* contact);
      void EndContact(b2Contact* contact);
      void PreSolve(b2Contact* contact, const b2Manifold* oldManifold);
      void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse);
    Note that depending on what is happening, some events give us more than just the b2Contact object. During the world's Step function, when Box2D detects that one of these events has occured, it will 'call back' to these functions to let you know about it. Practical applications of these 'collision callbacks' will be looked at in other topics, for now we are focusing on when they occur.

Generally I would recommend the contact listeners method. It may seem a little unwieldy at first but is more efficient and more useful in the long run. I have yet to come across a situation where checking the contact lists would have any great advantage.

Either way you get these contacts, they contain the same information. The most fundamental piece of info is which two fixtures are colliding, which is obtained by:
1
2
  b2Fixture* a = contact->GetFixtureA();
  b2Fixture* b = contact->GetFixtureB();
If you are checking the contact lists of a body, you may already know what one of the fixtures of the contact will be, but if you are using a contact listener you will rely on these two functions to find out what is colliding with what. There is no particular ordering of the A and B fixtures, so you will often need to have user data set in the fixtures or their bodies so you can tell what object the fixtures belong to. From these fixtures, you can GetBody() to find the bodies that collided.


Breakdown of a collision


Now let's take an in-depth look at the sequence of events in the collision above. Hopefully this will be easy to follow in table form, with a visual reference on the left to show what the scene looks like at each step. You might like to download the tutorials testbed from the source code page and run it while reading. In the testbed you can pause the simulation and then restart, then press 'Single Step' to see things happening in detail.

We should start from a point where the AABBs of the fixtures are still not overlapping, so we can follow the whole story. Click the 'AABBs' checkbox to see these as purple rectangles around each fixture. Anatomy of a collision
Anatomy of a collision Fixture AABBs begin to overlap (b2ContactManager::AddPair)

Although the fixtures themselves are not yet overlapping, at this point a b2Contact is made and added to the list of contacts for the world and the list of contacts for each body. If you are checking these contact lists as shown above you will be able to tell that a collision could potentially occur here, but in most cases you don't really care until the fixtures themselves really overlap.

Outcome:
  • Contact exists but IsTouching() returns false
Continues until fixtures themselves overlap...
Step n
Anatomy of a collision
Step n+1 (non bullet bodies)
Anatomy of a collision
Step n+1 (triangle as bullet body)
Anatomy of a collision
Fixtures begin to overlap (b2Contact::Update)

Zooming in on the upper corner of the box, you will see the transition as shown in the upper two images on the left. This occurs in one time step, which means that the real collision point (as shown by the dotted line in the top image) has been skipped over. This is because Box2D moves all the bodies and then checks for overlaps between them, at least with the default settings. If you want to get the real impact position you can set the body to be a 'bullet' body which will give you the result shown in the bottom image. You can do this by:
  
  bodyDef.bullet = true; //before creating body, or      
  body->SetBullet(true); //after creating body

Bullet bodies take more CPU time to calculate this accurate collision point, and for many applications they are not necessary. Just be aware that with the default settings, sometimes collisions can be missed completely - for example if the triangle in this example had been moving faster it may have skipped right over the corner of the box! If you have very fast moving bodies that must NOT skip over things like this, for example uh... bullets :) then you will need to set them as bullet bodies. For the rest of this discussion we will be continuing with the non-bullet body setting.

Outcome:
  • IsTouching() now returns true
  • BeginContact callback function will be called





Collision points and the normal


At this point we have a contact which is actually touching, so that means we should be able to answer some of the questions at the beginning of this topic. First let's get the location and normal for the contact. For the code sections below, we'll assume that these are either inside the BeginContact of the contact listener, or that you have have obtained a contact to use by manually checking the contact lists of the body or world.

Internally, the contact stores the location of the collision point in local coordinates for the bodies involved, and this is usually not so great for us. But we can ask the contact to give us a more useful 'world manifold' which will hold the collision location in world coordinates. 'Manifold' is just a fancy name for the line which best separates the two fixtures.
1
2
3
4
5
6
7
8
9
10
11
12
  //normal manifold contains all info...
  int numPoints = contact->GetManifold()->pointCount;
  
  //...world manifold is helpful for getting locations
  b2WorldManifold worldManifold;
  contact->GetWorldManifold( &worldManifold );
  
  //draw collision points
  glBegin(GL_POINTS);
  for (int i = 0; i < numPoints; i++)
      glVertex2f(worldManifold.points[i].x, worldManifold.points[i].y);
  glEnd();
Anatomy of a collision These are the points that will be used in the collision reaction when applying an impulse to push the fixtures apart.
Although they are not at the exact location where the fixtures would first have touched (unless you're using bullet-type bodies), in practise these points are often adequate for use as collision points in game logic.

Next let's show the collision normal, which points from fixtureA to fixtureB:
1
2
3
4
5
6
7
8
9
10
  float normalLength = 0.1f;
  b2Vec2 normalStart = worldManifold.points[0] - normalLength * worldManifold.normal;
  b2Vec2 normalEnd = worldManifold.points[0] + normalLength * worldManifold.normal;
  
  glBegin(GL_LINES);
  glColor3f(1,0,0);//red
  glVertex2f( normalStart.x, normalStart.y );
  glColor3f(0,1,0);//green
  glVertex2f( normalEnd.x, normalEnd.y );
  glEnd();
It seems that for this collision, the quickest way to resolve the overlap is to apply an impulse that will push the corner of the triangle up and left, and the corner of the square down and right. Please note that the normal is only a direction, it does not have a location and is not connected to either one of the points - I'm just drawing it at the location of points[0] for convenience. Anatomy of a collision It's important to be aware that the collision normal does not give you the angle that these fixtures collided at - remember the triangle was moving horizontally here, right? - it only gives the shortest direction to move the fixtures so that they don't overlap. For example, imagine if the triangle had been moving a little faster and the overlap was like this: Anatomy of a collision ... then the shortest way to separate the two fixtures would be to push the triangle up and right. So it should be clear that using this normal as an indication of the angle that the fixtures collided at is not a good idea. If you want to know the actual direction that these two corners impacted at, you can use:
1
2
3
  b2Vec2 vel1 = triangleBody->GetLinearVelocityFromWorldPoint( worldManifold.points[0] );
  b2Vec2 vel2 = squareBody->GetLinearVelocityFromWorldPoint( worldManifold.points[0] );
  b2Vec2 impactVelocity = vel1 - vel2;
... to get the actual relative velocity of the points on each body that collided. For the simple example we are looking at we could also simply take the linear velocity of the triangle because we know the square is stationary and the triangle is not rotating, but the above code will take care of cases when both bodies could be moving or rotating.

Another thing to note is that not every collision will have two of these collision points. I have deliberatly chosen a relatively complex example where two corners of a polygon are overlapping, but more common collisions in real situations have only one such point. Here are some other collision examples where only one point is necessary:
Anatomy of a collision Anatomy of a collision Anatomy of a collision

Okay, so we've seen how to find the collision points and normal, and we are aware that these points and the normal will be used by Box2D to apply a collision response to correct the overlap. Let's get back to the sequence of events...

While fixtures continue to overlap...
Impact
Anatomy of a collision
Impact + 1
Anatomy of a collision
Impact + 2
Anatomy of a collision
Collision response is applied (b2Contact::Update, b2Island::Report)

When fixtures are overlapping, Box2D's default behavior is to apply an impulse to each of them to push them apart, but this does not always succeed in a single time step. As shown here, for this particular example the two fixtures will be overlapping for three time steps before the 'bounce' is complete and they separate again.

During this time we can step in and customize this behavior if we want to. If you are using the contact listener method, the PreSolve and PostSolve functions of your listener will be repeatedly called in every time step while the fixtures are overlapping, giving you a chance to alter the contact before it is processed by the collision response (PreSolve) and to find out what impulses were caused by the collision response after it has been applied (PostSolve).

To make this clearer, here is the output obtained for this example collision by putting a simple printf statement in the main Step function and each of the contact listener functions:
        ...
        Step
        Step
        BeginContact
        PreSolve
        PostSolve
        Step
        PreSolve
        PostSolve
        Step
        PreSolve
        PostSolve
        Step
        EndContact
        Step
        Step
        ...

Outcome:
  • PreSolve and PostSolve are called repeatedly


PreSolve and PostSolve


Both PreSolve and PostSolve give you a b2Contact pointer, so we have access to the same points and normal information we just looked at for BeginContact. PreSolve gives us a chance to change the characteristics of the contact before the collision response is calculated, or even to cancel the response altogether, and from PostSolve we can find out what the collision response was.

Here are the alterations you can make to the contact in PreSolve:
1
2
3
4
5
  void SetEnabled(bool flag);//non-persistent - need to set every time step
  
  //these available from v2.2.1
  void SetFriction(float32 friction);//persists for duration of contact
  void SetRestitution(float32 restitution);//persists for duration of contact
Calling SetEnabled(false) will disable the contact, meaning that the collision response that normally would have been applied will be skipped. You can use this to temporarily allow objects to pass through each other. A classic example of this is the one-way wall or platform, where the player is able to pass through an otherwise solid object, depending on various criteria that can only be checked at runtime, like the position of the player and which direction direction they are heading, etc.

It's important to note that the contact will revert back to being enabled in the next time step, so if you want to disable contacts like this you'll need to call SetEnable(false) every time step.

As well as the contact pointer, PreSolve has a second parameter from which we can find out info about the collision manifold of the previous time step. If anybody has an idea about what this could be used for, let me know :D

PostSolve is called after the collision response has been calculated and applied. This also has a second parameter, in which we can find information about the impulse that was applied. A common use for this information is to check if the size of the collision response was over a given threshold, perhaps to check if an object should break, etc. See the 'sticky projectiles' topic for an example of using PostSolve to determine whether an arrow should stick into a target when it hits.

Okay, on with the timeline...

Anatomy of a collision
(zoomed out) Anatomy of a collision
Fixtures finish overlapping (b2Contact::Update)

The AABBs are still overlapping, so the contact remains in the contact list for the body/world.

Outcome:
  • EndContact callback function will be called
  • IsTouching() now returns false
Continues until fixture AABBs no longer overlap...
Anatomy of a collision Fixture AABBs finish overlapping (b2ContactManager::Collide)

Outcome:
  • Contact is removed from body/world contact list



While the EndContact call to the contact listener passes a b2Contact pointer, at this point the fixtures are no longer touching, so there will be no valid manifold information to get. However the EndContact event is still an indispensable part of the contact listener because it allows you to check which fixtures/bodies/game objects have ended contact. See the next topic for a basic example usage.


Summary


I hope this topic gives a clear overview of the events going on millisecond-by-millisecond under the hood of a Box2D collision. It may not have been the most interesting read (it sure was the least exciting topic to write so far!) but I get the feeling from reading questions on the forums that some of the details discussed here are often missed, and a lot of time is spent on various workarounds and wondering what's going on. I've also noticed a tendency to shy away from implementing a contact listener, when the listener usually becomes less work in the long run. Knowing these details should allow for a better understanding of what is actually possible, better design, and time saved in implementation.