Box2D with Complex and Concave Objects, for JavaScript
Intro
Example
The below screenshot has an example of a complex object. The green square + triangle is also concave.
Below is the live version:
The stand alone example for the above demo is available, as well as the source code for review.
Explanation
I'll start using more exact terminology now that we've covered the basics of getting objects onto the screen and interacting with each other. It's important to understand the specifics now that we're getting more complex.
A Body is the rigid chunk of matter.
A Shape is a geometrical object, such as a polygon, circle, square, etc. A Shape must be convex.
A Fixture binds a shape to a body.
Up to this point, we've had a one-to-one relationship between Body, Shape, and Fixture. Turns out that Box2D Bodies can have multiple shapes! If you extrapolate what that means, you can see that we can build Bodies with complex shapes.
Code
With this more specific understanding and terminology, let's use it to build complex bodies (see, I used the right term there :)
I've extended our JSON shorthand object definitions to support one or more polygons for a body. (This is non-standard Box2D code, just something I put together to make it easier to play around.)
var initialState = {"0": {id: 0, x: 10, y: 5, radius: 2},
"1": {id: 1, x: 5, y: 5, polys: [
[{x: 0, y: 0}, {x: 1, y: 0}, {x: 0, y:2}] // triangle
]},
"2": {id: 2, x: 9, y: 4, halfHeight: 1.5, halfWidth: 0.9},
"3": {id: 3, x: 4.5, y: 3, polys: [
[{x: 0, y: -2}, {x: 2, y: 0}, {x: 0, y:2}, {x:-0.5, y: 1.5}] // odd shape
]},
"4": {id: 4, x: 10, y: 10, polys: [
[{x: -1, y: -1}, {x: 1, y: -1}, {x: 1, y: 1}, {x: -1, y: 1}], // box
[{x: 1, y: -1.5}, {x: 2, y: 0}, {x: 1, y: 1.5}] // arrow
], color: "green"}
};
Notice the last definition has two polygon definitions. The first has four points for a box, and the second has three points for a triangle. Just like arbitrary shapes, the points are defined relative to the position of the body as specified by the x,y coordinates.
To construct the Box2D objects from the above definition, I refactored the original code a bit to handle more than one Fixture and Shape per Body.
bTest.prototype.setBodies = function(bodyEntities) {
this.bodyDef.type = b2Body.b2_dynamicBody;
for(var id in bodyEntities) {
var entity = bodyEntities[id];
this.bodyDef.position.x = entity.x;
this.bodyDef.position.y = entity.y;
this.bodyDef.userData = entity.id;
var body = this.world.CreateBody(this.bodyDef);
if (entity.radius) {
this.fixDef.shape = new b2CircleShape(entity.radius);
body.CreateFixture(this.fixDef);
} else if (entity.polys) {
for (var j = 0; j < entity.polys.length; j++) {
var points = entity.polys[j];
var vecs = [];
for (var i = 0; i < points.length; i++) {
var vec = new b2Vec2();
vec.Set(points[i].x, points[i].y);
vecs[i] = vec;
}
this.fixDef.shape = new b2PolygonShape;
this.fixDef.shape.SetAsArray(vecs, vecs.length);
body.CreateFixture(this.fixDef);
}
} else {
this.fixDef.shape = new b2PolygonShape;
this.fixDef.shape.SetAsBox(entity.halfWidth, entity.halfHeight);
body.CreateFixture(this.fixDef);
}
}
this.ready = true;
}
The HTML5 Canvas code didn't change too much. As you can see below, the canvas is translated and rotated once, and then all of the shapes are drawn with a series of moveTo() and lineTo() calls.
PolygonEntity.prototype.draw = function(ctx) {
ctx.save();
ctx.translate(this.x * SCALE, this.y * SCALE);
ctx.rotate(this.angle);
ctx.translate(-(this.x) * SCALE, -(this.y) * SCALE);
ctx.fillStyle = this.color;
for (var i = 0; i < this.polys.length; i++) {
var points = this.polys[i];
ctx.beginPath();
ctx.moveTo((this.x + points[0].x) * SCALE, (this.y + points[0].y) * SCALE);
for (var j = 1; j < points.length; j++) {
ctx.lineTo((points[j].x + this.x) * SCALE, (points[j].y + this.y) * SCALE);
}
ctx.lineTo((this.x + points[0].x) * SCALE, (this.y + points[0].y) * SCALE);
ctx.closePath();
ctx.fill();
ctx.stroke();
}
ctx.restore();
Entity.prototype.draw.call(this, ctx);
}
Summary
Box2D supports bodies with complex and even concave shapes by binding more than one Fixture and Shape to a Body.
Next Up
Did this help clear up shapes and bodies? What would you like to see next? Please leave comments below and let me know.