Shapes describe the size, outline, and material properties of objects in the physics scene. One body can have multiple shapes, affecting its center of mass. When bodies collide, their shapes determine the reaction. Main shape properties include:
- shape - polygon, circle, capsule or chain edge
- restitution - bounciness
- friction - slipperiness
- density - mass relative to area
Let's start with a single dynamic body:
// Create world first
const worldDef = b2DefaultWorldDef();
worldDef.gravity = new b2Vec2(0, -10);
const worldId = b2CreateWorld(worldDef);
// Create dynamic body
const bodyDef = b2DefaultBodyDef();
bodyDef.type = DYNAMIC; // this will be a dynamic body
bodyDef.position = new b2Vec2(-10, 20); // a little to the left
const dynamicBodyId1 = b2CreateBody(worldId, bodyDef);
Shapes
Every shape needs an edge definition for collision detection.
For a circle
const circle = {
center: new b2Vec2(0, 0), // position, relative to body position
radius: 1 // radius
};
const shapeDef = b2DefaultShapeDef();
const circleShape = b2CreateCircleShape(dynamicBodyId1, shapeDef, circle);
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 (-10,20), so the circle will be at (-10,20) too.
For polygons
Set individual vertices or use convenience functions to get simple boxes or lines:
// Set vertices for polygon
const vertices = [
new b2Vec2(-1, 2),
new b2Vec2(-1, 0),
new b2Vec2(0, -3),
new b2Vec2(1, 0),
new b2Vec2(1, 1)
];
// Create a hull
const hull = b2ComputeHull(vertices, vertices.length);
const polygon = b2MakePolygon(hull, 0);
const shapeDef = b2DefaultShapeDef();
const polygonShape = b2CreatePolygonShape(dynamicBodyId1, shapeDef, polygon);
There are a few things to be careful of when creating polygons this way. Firstly there is a limit of 8 vertices per polygon by default. 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).
For rectangles
Use the convenience function b2MakeBox:
const box = b2MakeBox(2, 1); // a 4x2 rectangle
const shapeDef = b2DefaultShapeDef();
const boxShape = b2CreatePolygonShape(dynamicBodyId1, shapeDef, box);
For capsules
A capsule is a pill shaped object, each end is a semi-circle and they are joined by a box. They are often useful as an approximation for a player character, and also make great 'bones' for ragdolls!
const capsule = new b2Capsule();
capsule.center1 = new b2Vec2(-15, 0);
capsule.center2 = new b2Vec2(15, 0);
capsule.radius = 10.0;
const shapeDef = b2DefaultShapeDef();
const capsuleShape = b2CreateCapsuleShape(dynamicBodyId1, shapeDef, capsule);
For single lines
Sometimes instead of a solid shape, you want to have a simple line with zero thickness.
const segment = new b2Segment();
segment.point1 = new b2Vec2(-15, 0);
segment.point2 = new b2Vec2(15, 0);
const shapeDef = b2DefaultShapeDef();
const segmentShape = b2CreateSegmentShape(dynamicBodyId1, shapeDef, segment);
For chained edges
When you want a lot of lines which are all joined together to make a single line (e.g. to match an uneven terrain, or to define an unusual outline), a chained edge is ideal. Chained edges remove some issues associated with the touching points between single lines or polygons, more on this later.
const points = [new b2Vec2(-12.0, 0.0), new b2Vec2(12.0, 0.0), new b2Vec2(12.0, 4.0), new b2Vec2(8.0, 2.0), new b2Vec2(4.0, 4.0)];
const chainDef = b2DefaultChainDef();
chainDef.points = points;
chainDef.count = points.length;
chainDef.isLoop = false;
const chainShape = b2CreateChain(bodyId, chainDef);
Density
Set density to give shapes mass:
const shapeDef = b2DefaultShapeDef();
shapeDef.density = 1;
The mass of a shape is calculated from the area of the shape multiplied by the density.
Multiple Shapes
Multiple shapes can be attached to one body:
// Create polygon shape and attach it to dynamicBodyId1
const vertices = [
new b2Vec2(-1, 2),
new b2Vec2(-1, 0),
new b2Vec2(0, -3),
new b2Vec2(1, 0),
new b2Vec2(1, 1)
];
const transformedVertices = vertices.map(v => new b2Vec2(v.x + 2, v.y - 2));
const hull = b2ComputeHull(transformedVertices, transformedVertices.length);
const polygon = b2MakePolygon(hull, 0);
const polygonShape = b2CreatePolygonShape(bodyId, shapeDef, polygon);
// Create box shape and attach it to the same body
const box = b2MakeOffsetBox(2, 1, new b2Vec2(20, 0), 0);
const boxShape = b2CreatePolygonShape(dynamicBodyId1, shapeDef, box);
We can create very complex objects by adding simple shapes to a single body!
Friction
Friction values range 0-1:
const shapeDef = b2DefaultShapeDef();
shapeDef.density = 1;
shapeDef.friction = 0;
Friction is a property that affects two surfaces making contact. The friction value used will be the lowest value of the two surfaces (rubber will still slide on ice!). When friction is zero, the object will slide on any surface, however a friction of one does not guarantee that the object will not slide.
Restitution
Restitution measures how 'bouncy' a shape is. Like friction, it is given a value between 0 and 1, where zero means that the shapes will not bounce at all, and one means that all the energy of the bounce will be conserved. When two shapes collide with each other, the resulting restitution tends toward the higher of their restitution values.
const shapeDef = b2DefaultShapeDef();
shapeDef.friction = 0;
shapeDef.restitution = 0;
Changing Shape Attributes
Change attributes at runtime:
b2Shape_SetDensity(shapeId, newDensity);
b2Shape_SetRestitution(shapeId, newRestitution);
b2Shape_SetFriction(shapeId, newFriction);
Iterating Shapes
Get all shapes for a body into an array:
const shapeCount = b2Body_GetShapeCount(bodyId);
const shapes = new Array(shapeCount);
b2Body_GetShapes(bodyId, shapes);
Cleaning Up
Remove shapes:
b2DestroyShape(shapeId);
Remember not to use the shapeId after this, it is now invalid! Also note that when you destroy a body, all of the shapes attached to it are destroyed too, this is another way the shapeId can become invalid.
Credits
This tutorial is adapted from an original piece of work created by Chris Campbell and is used under license.