Box2D C++ tutorials - Fixtures

Last edited: July 14 2013

Chinese version -> 中文

Fixtures


Fixtures are used to describe the size, shape, and material properties of an object in the physics scene. One body can have multiple fixtures attached to it, and the center of mass of the body will be affected by the arrangement of its fixtures. When two bodies collide, their fixtures are used to decide how they will react. The main properties of fixtures are:
  • shape - a polygon or circle
  • restitution - how bouncy the fixture is
  • friction - how slippery it is
  • density - how heavy it is in relation to its area
We'll go through each of these and experiment with them (there is also an isSensor property which will make the fixture a 'sensor' which I will cover in a later topic) Let's start with a single dynamic body created as in the first part of the 'Bodies' topic, so the constructor for the FooTest class will look like this:
1
2
3
4
5
6
7
  FooTest() {
    b2BodyDef myBodyDef;
    myBodyDef.type = b2_dynamicBody; //this will be a dynamic body
    myBodyDef.position.Set(-10, 20); //a little to the left
    
    b2Body* dynamicBody1 = m_world->CreateBody(&myBodyDef);
  }


Shapes


Every fixture has a shape which is used to check for collisions with other fixtures as it moves around the scene. A shape can be a circle or a polygon. Let's set up a circle shape...
1
2
3
  b2CircleShape circleShape;
  circleShape.m_p.Set(0, 0); //position, relative to body position
  circleShape.m_radius = 1; //radius
... and a fixture which uses this shape:
1
2
3
  b2FixtureDef myFixtureDef;
  myFixtureDef.shape = &circleShape; //this is a pointer to the shape above
  dynamicBody1->CreateFixture(&myFixtureDef); //add a fixture to the body
Running the program now you should see a circle in place of the square we had before: Fixture When setting the position of the circle, the coordinates will be relative to the body's position. In this case we set the location of the circle as (0,0) but this will be attached to a body which is at (0,20), so the circle will be at (0,20) too.

Now let's try a polygon shape. With polygon shapes, you can set each individual vertex of the polygon to create a customized shape, or make use of a couple of convenience functions if you want boxes or lines. Here is a customized polygon with five vertices:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  //set each vertex of polygon in an array
  b2Vec2 vertices[5];
  vertices[0].Set(-1,  2);
  vertices[1].Set(-1,  0);
  vertices[2].Set( 0, -3);
  vertices[3].Set( 1,  0);
  vertices[4].Set( 1,  1);
  
  b2PolygonShape polygonShape;
  polygonShape.Set(vertices, 5); //pass array to the shape
  
  myFixtureDef.shape = &polygonShape; //change the shape of the fixture
  myBodyDef.position.Set(0, 20); //in the middle
  b2Body* dynamicBody2 = m_world->CreateBody(&myBodyDef);
  dynamicBody2->CreateFixture(&myFixtureDef); //add a fixture to the body
Fixture
There are a few things to be careful of when creating polygon fixtures this way. Firstly there is a limit of 8 vertices per polygon by default. If you need more you can adjust the value b2_maxPolygonVertices in the file b2Settings.h. The vertices must also be specified in counter-clockwise order, and they must describe a convex polygon. A convex polygon is one which, if you were to walk around the edges of it, you always turn the same way at each corner (either always left, or always right).

If you want a rectangular fixture, the easiest way to get one of these is with the SetAsBox function which we used in the last topic:
1
2
3
4
5
  polygonShape.SetAsBox(2, 1); //a 4x2 rectangle
  myBodyDef.position.Set(10,20); //a bit to the right
  
  b2Body* dynamicBody3 = m_world->CreateBody(&myBodyDef);
  dynamicBody3->CreateFixture(&myFixtureDef); //add a fixture to the body
Fixture Notice that the parameters of SetAsBox are the 'half-width' and 'half-height' of the box, and it is centered at the location of the body it gets attached to. Notice also that because the fixture definition holds a reference pointer to the polygonShape we changed, and all we are changing is the shape, we don't need to do any of the other setup code this time.

Sometimes instead of a solid shape, you want to have a fixture represented by a simple line with zero thickness. This can be done with another convenience function, SetAsEdge. This function takes two points which will be the ends of the line. Let's make a static body with a long flat line fixture, near the bottom of the scene so the dynamic objects can fall onto it.
1
2
3
4
5
6
  myBodyDef.type = b2_staticBody; //change body type
  myBodyDef.position.Set(0,0); //middle, bottom
  
  polygonShape.SetAsEdge( b2Vec2(-15,0), b2Vec2(15,0) ); //ends of the line
  b2Body* staticBody = m_world->CreateBody(&myBodyDef);
  staticBody->CreateFixture(&myFixtureDef); //add a fixture to the body
Fixture Note: The SetAsEdge function for polygons was removed in versions of Box2D after v2.1.2 because this kind of single-line shape has been given its own shape type, b2EdgeShape. Click here to see the equivalent usage of b2EdgeShape.
1
2
3
4
  b2EdgeShape edgeShape;
  edgeShape.Set( b2Vec2(-15,0), b2Vec2(15,0) );
  
  myFixtureDef.shape = &edgeShape;


Density


By now you are probably wondering why these bodies don't spin around as expected when you grab them with the mouse cursor. This is because we haven't given any density setting for the fixtures. The density of a fixture multiplied by it's area becomes the mass of that fixture. Go back to where we first declared the fixture definition, and set a density for it:
1
2
3
  b2FixtureDef myFixtureDef;
  ...
  myFixtureDef.density = 1; //new code
Since all the bodies were using the same fixture definition this will affect all of them. Fixture In this example, all the fixtures having the same density means they weigh about as much as their visual size suggests they would, the circle being the lightest. Experiment by changing the density for each fixture and see how this setting gives a body it's 'weight'. Remember that because all bodies are using the same fixture definition, you'll have to make sure the density you want is set in the fixture definition before calling CreateFixture each time.


Multiple fixtures


We can attach all three of these fixtures onto one body, and it's actually pretty easy - go back to each time you called CreateFixture, and instead of doing it for dynamicBody1, dynamicBody2 and dynamicBody3, change those calls so that all three fixtures get added to dynamicBody1. Fixture As you can see this causes all the fixture locations to be relative to dynamicBody1 now. It's quite intuitive to see the fixtures globbed together on their body, but it's not strictly necessary for them to be like that. Fixtures can be added anywhere on a body, even leaving empty space between them. For example, to keep the two polygon fixtures in the same places they were before, we would need to go back and manually adjust their locations where we created them.
1
2
3
4
5
6
7
8
9
10
  //for the custom polygon, add 10 to each x-coord
  vertices[0].Set(-1 +10,  2);
  vertices[1].Set(-1 +10,  0);
  vertices[2].Set( 0 +10, -3);
  vertices[3].Set( 1 +10,  0);
  vertices[4].Set( 1 +10,  1);
  ...
  //for the box, use an extended version of the SetAsBox function which allows
  //us to set a location and angle (location is offset from body position)
  polygonShape.SetAsBox(2, 1, b2Vec2(20,0), 0 ); //moved 20 units right, same angle
You should get a see-saw kind of object as below (this is a good illustration of how the body itself is intangible): Fixture

Friction


Now that we know how to set up a body with multiple fixtures, let's start with an empty scene again, and set up a body with four square fixtures on it to use in the next experiments.
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
  FooTest() {
    //set up a dynamic body
    b2BodyDef myBodyDef;
    myBodyDef.type = b2_dynamicBody;
    myBodyDef.position.Set(0, 20); //middle
    b2Body* dynamicBody = m_world->CreateBody(&myBodyDef);
    
    //prepare a shape definition
    b2PolygonShape polygonShape;
    b2FixtureDef myFixtureDef;
    myFixtureDef.shape = &polygonShape;
    myFixtureDef.density = 1;
    
    //add four square shaped fixtures around the body center
    for ( int i = 0; i < 4; i++) {
      b2Vec2 pos( sinf(i*90*DEGTORAD), cosf(i*90*DEGTORAD) ); //radial placement
      polygonShape.SetAsBox(1, 1, pos, 0 ); //a 2x2 rectangle
      dynamicBody->CreateFixture(&myFixtureDef); //add a fixture to the body
    }
    
    //make a static floor to drop things on
    myBodyDef.type = b2_staticBody;
    myBodyDef.position.Set(0, 0); //middle, bottom
    b2Body* staticBody = m_world->CreateBody(&myBodyDef);  
    polygonShape.SetAsEdge( b2Vec2(-15,0), b2Vec2(15,3) ); //slightly sloped  
    staticBody->CreateFixture(&myFixtureDef); //add a fixture to the body
  }
Fixture With this scene, you should see the body sliding a little as it rolls down the slope. As each fixture contacts the ground, its friction setting is used to calculate how much it should slide. Friction settings can be given a value between 0 and 1, zero being completely frictionless. When two fixtures collide with each other, the resulting friction tends toward the lower of their friction values.

Try giving the fixture definition zero friction:
1
2
  myFixtureDef.density = 1;
  myFixtureDef.friction = 0; //new code
This time the body should rotate much less since its surface does not stick on the slope at all, and it will actually gain speed as it slides. Now try a friction value of 1 to compare the behaviour. Finally, to illustrate how friction is a property of individual fixtures and not the body itself, how about setting a different friction for each fixture - add a line inside the loop, just before the CreateFixture:
1
  myFixtureDef.friction = i/4.0;
By playing around with the object you should be able to determine how much friction each fixture has.
* Note: a friction value of 1 does not always guarantee that there will be no sliding.


Restitution


Restitution measures how 'bouncy' a fixture is. Like friction, it is given a value between 0 and 1, where zero means that the fixture will not bounce at all, and one means that all the energy of the bounce will be conserved. When two fixtures collide with each other, the resulting restitution tends toward the higher of their restitution values.

Restitution values can be experimented with in the same way as the friction values above. Try setting a different bounciness for each fixture.
1
2
  myFixtureDef.friction = ...;
  myFixtureDef.restitution = 0; //new code
* Note: a restitution value of 0 does not always guarantee that there will be no bouncing
* Note: in reality a tiny amount of energy can be lost in bouncing calculations


Changing fixture attributes at run-time


Sometimes you might want to alter the attributes of a fixture depending on events in the game. You can change the friction, density and restitution by setting these in the fixture using the setter functions. If you already have a reference to the fixture you want to change, this is pretty easy:
1
2
3
  fixture->SetDensity( ... );
  fixture->SetRestitution( ... );
  fixture->SetFriction( ... );
If you only have a reference to the body, you'll need to iterate over the body's fixtures as shown below to find the one you want.

* Setting the density is a special case where one more step is necessary to have the changes take effect. After doing SetDensity() on the fixture, you need to call ResetMassData() on the body that the fixture belongs to.


Iterating over the fixtures in a body


If you have a body and you want to look at all the fixtures attached to it, you can do it as below. The function GetFixureList() returns the first element in a linked list of fixtures.
1
2
3
4
  for (b2Fixture* f = body->GetFixtureList(); f; f = f->GetNext())
  {
      //do something with the fixture 'f'
  }
If you know there is only one fixture on the body, you could just use the first element of the list:
1
2
  b2Fixture* f = body->GetFixtureList();
  //do something with the fixture 'f'


Cleaning up


If you want to remove a fixture from a body, call the body’s DestroyFixture function:
1
2
3
  b2Fixture* myFixture = dynamicBody->CreateFixture(&myFixtureDef);
  ...
  dynamicBody->DestroyFixture(myFixture);
Remember not to use the deleted fixture pointer after this! When you destroy the body that a fixture is attached to, all the fixtures on it will be destroyed as well, so in this case too you must not use the fixtures after that.

Generally if you know the game logic, and how objects are likely to be destroyed, you can arrange your program to avoid using invalid fixture pointers. However if you are working on a complex project you might find it easier to use the 'destruction listener' feature provided by Box2D. This allows you to receive a notification when any fixture is destroyed so you know not to use it any more. Check out the section on 'Implicit destruction' in the 'Loose Ends' section of the user manual.