How does planck.js handle edge shapes?
Planck.js, a 2D JavaScript physics engine rewritten from LiquidFun/Box2D, handles terrain generation primarily through the use of Edge and Chain shapes. Unlike closed polygons, edge shapes are line segments designed specifically to represent static, hollow environments such as hills, valleys, and platforms. By stringing these segments together, developers can create complex, continuous terrain without the performance overhead or collision glitches associated with overlapping solid shapes.
Understanding Edge and Chain Shapes
In planck.js, terrain is typically constructed using two geometric primitives:
EdgeShape (planck.Edge): A single line segment defined by two vertices (\(v_1\) and \(v_2\)). It has no volume and only interacts with other shapes from its front face or sides.ChainShape (planck.Chain): A collection of connected edge shapes that form a continuous line. This is the most efficient way to generate terrain because it automatically handles vertex connectivity, preventing moving objects from getting “stuck” on internal seams.
Ghost Vertices and Smooth Collisions
One of the biggest challenges in game physics is the “ghost collision” phenomenon, where a sliding object hits an internal vertex between two perfectly aligned flat surfaces and abruptly stops. Planck.js solves this by utilizing ghost vertices (also known as adjacent vertices).
When you define a chain shape or a series of connected edges, planck.js stores:
- **
m_hasVertex0andm_hasVertex3**: Flags indicating if there are adjacent vertices before the start and after the end of the current edge. - **
m_vertex0andm_vertex3**: The coordinates of those neighboring vertices.
The physics solver uses these ghost vertices to calculate smooth collision normals. When a dynamic body (like a player or a wheel) slides across the terrain, the engine anticipates the next segment’s slope, ensuring a seamless transition across the vertices.
Implementing Terrain Generation
To generate a terrain in planck.js, you typically define an array of 2D vectors representing the landscape heights and pass them to a Chain shape.
const world = planck.World();
const groundBody = world.createBody();
// Define vertices for a simple uneven terrain
const vertices = [
planck.Vec2(0.0, 0.0),
planck.Vec2(10.0, 2.0),
planck.Vec2(20.0, 1.0),
planck.Vec2(30.0, 5.0),
planck.Vec2(40.0, 0.0)
];
// Create a continuous chain shape for the terrain
groundBody.createFixture(planck.Chain(vertices), {
density: 0.0,
friction: 0.6
});Performance and Collision Limits
Because edge and chain shapes are strictly static primitives, they do not collide with each other and cannot be used for dynamic, moving bodies. This limitation is a deliberate optimization. Because they lack area, planck.js can bypass complex mass and inertia calculations, allowing the engine to handle massive, procedurally generated worlds with thousands of terrain vertices at a minimal performance cost.