In the 'jumpability' topic, we discussed how to detect if a player character is on the ground. Another equally common question is "why does my character get stuck when moving along flat ground?". This typically happens when the ground consists of multiple shapes (polygon or edge shapes) placed adjacently to create a flat surface, with a rectangular character shape moving along it.
This often results from level layouts defined as tiles in a fixed grid - while convenient for many purposes, this approach is somewhat sub-optimal for representing smooth surfaces.
Why does the character get stuck?
Box2D handles collisions between two shapes by calculating overlap and determining the quickest separation path. Key point: collisions are always resolved between exactly two shapes. Collisions are not resolved in threes, neither are they resolved in fours. Of course, five is right out.
So there are actually two individual collisions going on which will be resolved separately:
In each of these the quickest way to push the overlapping fixtures apart is calculated. In the case on the left the solution is almost certainly to push the 'player' up and the ground down:
Corner vs corner collisions are more complex: a small change in the position of the fixtures could cause a big difference in the resulting impulse that is calculated to separate them. Also, as the player body moves, it slightly bounces up and down from collision responses:
At the moment the player collides with the new ground box on the right, one of two things could happen. Zooming in close to the corner we can see two possibilities.
Solutions
1. Clipping Polygon Corners
Clipping corners of either the player or ground polygons can improve collision response:
Box2D V3 has the ability to round any corners: every polygon has a radius which can be zero for sharp corners, or larger to make the corners rounded.
Using circular shapes for the player provides even better results but may still bounce a little when crossing the joints.
2. Chain Edge Shapes
Chain Edge shapes significantly improve upon separate polygons. They consist of a series of connected lines between points and experience fewer collision issues than polygons.
One nice thing about replacing polygons with edges is that you no longer need to worry about keeping them convex and within 8 vertices as you do with polygons. And when you have an area of ground which should be perfectly flat, it's trivial to replace many adjacent edges with a single large edge.
3. Ghost Vertices
Ghost vertices allow smooth segments to have additional vertices that don't participate in collision detection but influence collision response:
Implementation example:
// Create a chain to hold the smooth segments
const chainId = 1;
const chainShape = new b2ChainShape();
chainShape.id = chainId;
// take a look at the side-view car example for how to use chain edges to create an interesting terrain
// Create a smooth segment shape with ghost vertices
const segmentDef = new b2SmoothSegment();
segmentDef.segment = new b2Segment(new b2Vec2(v1.x, v1.y), new b2Vec2(v2.x, v2.y));
segmentDef.ghost1 = new b2Vec2(v0.x, v0.y);
segmentDef.ghost2 = new b2Vec2(v3.x, v3.y);
// Create the shape
const shapeDef = b2DefaultShapeDef();
const shapeId = b2CreateSegmentShape(bodyId, shapeDef, segmentDef);
Important Considerations
When using ghost vertices, ensure segments aren't too small compared to colliding objects. If both v2 and v3 vertices end up inside the player shape, the ghost system may fail.
Credits
This tutorial is adapted from an original piece of work created by Chris Campbell and is used under license.