Physics
Box2D physics integration: body types, fixtures, constraints, and collision.
Syncromesh integrates Box2D for 2D rigid-body physics. Physics bodies are created at spawn time via Simulation.spawn() or by adding fixtures post-spawn with Entity.createFixture(). Constraints (joints) are created with Simulation.createConstraint(). The simulation steps the physics world automatically each frame.
Body types #
| Value | Behaviour |
|---|---|
static | Does not move. Collides with dynamic bodies. Default. |
dynamic | Fully simulated: forces, velocity, gravity, collisions. |
kinematic | Moves via velocity only; not affected by forces or other bodies. |
Set body.bullet: true in Simulation.spawn() to enable Box2D bullet mode for fast dynamic bodies. Bullet mode improves continuous collision detection against other moving bodies, but costs more CPU and should be used sparingly. Scripts can toggle it later with Entity.setBodyBullet(id, enabled).
Fixture shapes #
Fixtures define the collision geometry attached to a body. Each fixture has a type and shape-specific properties, plus optional density, friction, restitution, isSensor, and Box2D collision filtering.
Common Fixture Options #
densitynumber optional — Defaults to1.0.frictionnumber optional — Defaults to0.3.restitutionnumber optional — Bounciness from0(no bounce) to1(fully elastic). Defaults to0.0.isSensorboolean optional — If true, the fixture reports overlap contacts but does not create a physical collision response.filterFixtureFilter optional — Box2D collision filter. The same fields can also be supplied directly on the fixture for convenience.collisionFixtureFilter optional — Alias forfilter.categoryBitsnumber|string optional — 64-bit collision category bitfield. Use a decimal or0xstring outside JavaScript's safe integer range. Defaults to0x0001.maskBitsnumber|string optional — 64-bit collision mask bitfield. Use a decimal or0xstring outside JavaScript's safe integer range. Defaults to all bits set.groupIndexnumber optional — Signed collision group. Matching positive groups always collide; matching negative groups never collide.
categoryBitsnumber|string optional — 64-bit collision category bitfield. Strings preserve all 64 bits.maskBitsnumber|string optional — 64-bit collision mask bitfield. Strings preserve all 64 bits.groupIndexnumber optional — Signed collision group override.
Box #
typestring —"box"sizePoint optional — Full extents{ x, y }. Mutually exclusive withaabb.aabbAABB optional — Positioned rectangle with centre and half-extents. Mutually exclusive withsize.densitynumber optional — Defaults to1.0.frictionnumber optional — Defaults to0.3.restitutionnumber optional — Bounciness from0(no bounce) to1(fully elastic). Defaults to0.0.
Circle #
typestring —"circle"radiusnumber optional — Circle radius. Defaults to1.0.positionPoint optional — Local offset from the body origin.densitynumber optional — Defaults to1.0.frictionnumber optional — Defaults to0.3.restitutionnumber optional — Bounciness from0(no bounce) to1(fully elastic). Defaults to0.0.
Polygon #
typestring —"polygon"verticesPoint[] — Array of 3-8 vertices defining a convex hull.densitynumber optional — Defaults to1.0.frictionnumber optional — Defaults to0.3.restitutionnumber optional — Bounciness from0(no bounce) to1(fully elastic). Defaults to0.0.
Edge #
typestring —"edge"startPoint — Start vertex.endPoint — End vertex.densitynumber optional — Defaults to1.0.frictionnumber optional — Defaults to0.3.restitutionnumber optional — Bounciness from0(no bounce) to1(fully elastic). Defaults to0.0.
Chain #
typestring —"chain"verticesPoint[] — Array of 2+ vertices forming a chain.densitynumber optional — Defaults to1.0.frictionnumber optional — Defaults to0.3.restitutionnumber optional — Bounciness from0(no bounce) to1(fully elastic). Defaults to0.0.
Constraints (joints) #
Create constraints between two entity bodies with Simulation.createConstraint(options). Every constraint requires entityA, entityB, and a type.
| Type | Purpose | Key options |
|---|---|---|
distance | Keeps two anchor points separated by a target distance (spring/rope-like). | anchorA, anchorB, length, minLength, maxLength, stiffness, damping |
weld | Rigidly attaches two bodies around a shared anchor. | anchor, referenceAngle, stiffness, damping |
prismatic | Constrain relative motion to a single axis (slider). | anchor, axis, enableLimit, lowerTranslation, upperTranslation, enableMotor, motorSpeed, maxMotorForce |
revolute | Hinge constraint around a shared anchor. | anchor, enableLimit, lowerAngle, upperAngle, enableMotor, motorSpeed, maxMotorTorque |
Collision detection #
Use Entity.getContacts(id) to retrieve the IDs of entities currently in contact. Use Simulation.query(aabb) for broad-phase spatial queries.
Examples #
import * as Simulation from 'Syncromesh/Simulation';
import * as Entity from 'Syncromesh/Entity';
// Spawn two dynamic bodies
const ballA = await Simulation.spawn({
position: { x: -4, y: 0 },
body: {
type: 'dynamic',
fixture: [{ type: 'circle', radius: 0.5, density: 1.0, friction: 0.0, restitution: 1.0 }]
}
});
const ballB = await Simulation.spawn({
position: { x: 0, y: 0 },
body: {
type: 'dynamic',
fixture: [{ type: 'circle', radius: 0.5, density: 1.0, friction: 0.0, restitution: 1.0 }]
}
});
// Connect them with a distance constraint
const tether = await Simulation.createConstraint({
type: 'distance',
entityA: ballA,
entityB: ballB,
length: 4.0,
stiffness: 3.0,
damping: 0.6
});
// Set initial velocity (physics-driven movement)
await Entity.setLinearVelocity(ballA, { x: 6, y: 0 });
// Check contacts and constraints
const contacts = await Entity.getContacts(ballA);
const constraints = await Simulation.getEntityConstraints(ballA);
// Cleanup
await Simulation.destroyConstraint(tether);