Colliders
Complete guide to collision shapes in Web Engine. Learn about primitive shapes, complex meshes, sensors, collision groups, and compound colliders.
The Collider component defines the collision shape for physics bodies. Web Engine supports primitive shapes (box, sphere, capsule), complex shapes (convex hull, trimesh, heightfield), and compound colliders. Colliders can be solid (blocking) or sensors (trigger volumes).
Collider Shapes#
Box (Cuboid)
Rectangular box defined by half-extents (X, Y, Z). Fast and stable. Best for crates, walls, buildings.
Sphere (Ball)
Perfect sphere defined by radius. Fastest shape for collision detection. Best for balls, planets.
Capsule
Cylinder with rounded ends (radius + height). Excellent for characters - smooth sliding and no getting stuck.
Heightfield (Terrain)
Grid of height values for terrain. Efficient for large outdoor environments.
Trimesh
Exact mesh collision using triangle mesh. Static bodies only. Best for complex level geometry.
Convex Hull
Convex approximation of a mesh. More expensive but allows dynamic bodies.
Component Properties#
| Property | Type | Default | Description |
|---|---|---|---|
| type | enum | 0 (Box) | Shape: 0=Box, 1=Sphere, 2=Capsule, 3=Heightfield, 4=Trimesh, 5=ConvexHull |
| size | vec3 | [1,1,1] | Box: half-extents (X,Y,Z). Sphere: radius (X). Capsule: radius (X), height (Y) |
| offset | vec3 | [0,0,0] | Local position offset from body center |
| isSensor | boolean | false | True = trigger volume (no collision response) |
| collisionGroup | int | 1 | Membership bitmask (which groups this belongs to) |
| solverGroup | int | 1 | Solver bitmask (which groups to solve with) |
Primitive Shapes#
Box (Cuboid) - Type 0#
Rectangular box defined by half-extents. The size property specifies half the width, height, and depth.
// 2x2x2 cube (size = half-extents){ type: 0, // Box size: [1, 1, 1], // Half-extents: 1m in each direction offset: [0, 0, 0],} // Wall: 10m wide, 5m tall, 0.5m thick{ type: 0, // Box size: [5, 2.5, 0.25], // Half-extents offset: [0, 2.5, 0], // Offset up by half-height}- Fast collision detection
- Stable for stacking
- Good for: crates, walls, buildings, platforms
Sphere (Ball) - Type 1#
Perfect sphere defined by radius. The size.x property specifies the radius.
// Sphere with 1m radius{ type: 1, // Sphere size: [1, 0, 0], // Only X component used (radius) offset: [0, 0, 0],} // Basketball (radius ~0.12m){ type: 1, // Sphere size: [0.12, 0, 0], offset: [0, 0.12, 0], // Offset to sit on ground}- Fastest collision detection
- Rolls smoothly
- Good for: balls, planets, orbs, spherical projectiles
Capsule - Type 2#
Cylinder with rounded ends. Perfect for characters - won't get stuck on edges. size.x = radius, size.y = half-height.
// Character capsule: 0.5m radius, 2m tall{ type: 2, // Capsule size: [0.5, 1.0, 0], // Radius (X), Half-height (Y) offset: [0, 1.0, 0], // Center at character center} // Barrel{ type: 2, // Capsule size: [0.3, 0.5, 0], // Radius 0.3m, height 1m offset: [0, 0.5, 0],}- Smooth collision (no edge catching)
- Best for characters
- Good for: humanoid characters, cylindrical objects
Character Colliders
Always use a Capsule for character colliders. The rounded ends prevent getting stuck on stairs and edges. Avoid box colliders for characters.
Complex Shapes#
Convex Hull - Type 5#
Wraps a convex hull around a set of vertices. More expensive than primitives but can approximate complex shapes. Works with dynamic bodies.
// Convex hull from mesh vertices// (Typically created automatically from mesh){ type: 5, // ConvexHull vertices: meshVertices, // Float32Array of vertices offset: [0, 0, 0],}- Approximates complex shapes
- Works with dynamic bodies
- More expensive than primitives
- Good for: rocks, irregular objects, simplified meshes
Convex Limitation
Convex hulls can only represent convex shapes (no concave indentations). For concave objects, use multiple convex hulls (compound collider) or a trimesh (static only).
Trimesh (Triangle Mesh) - Type 4#
Exact mesh collision using triangles. Most accurate but also most expensive. Only works with static (fixed) bodies.
// Trimesh collider for level geometry{ type: 4, // Trimesh vertices: meshVertices, // Float32Array indices: meshIndices, // Uint32Array offset: [0, 0, 0],} // Note: Parent body MUST be type 1 (Fixed/Static)- Exact mesh collision
- Static bodies only
- Most expensive collision detection
- Good for: level geometry, terrain, buildings
Static Only
Trimesh colliders cannot be used with dynamic bodies. Attempting to create a dynamic trimesh will result in undefined behavior. Use convex hull or compound colliders instead.
Heightfield (Terrain) - Type 3#
Efficient terrain representation using a grid of height values. Perfect for large outdoor environments.
// Heightfield terrain{ type: 3, // Heightfield heights: heightData, // Float32Array of height values nrows: 128, // Number of rows ncols: 128, // Number of columns scale: { x: 1, y: 1, z: 1 }, // Scale factors}- Efficient for large terrains
- Grid-based height data
- Static bodies only
- Good for: outdoor terrain, landscapes
Compound Colliders#
Combine multiple simple shapes to approximate complex objects. More efficient than a single complex mesh. Attach multiple collider entities as children.
// Chair made from boxes// Parent entity: RigidBody (dynamic)// Child 1: Collider (seat - box)// Child 2: Collider (back - box)// Child 3: Collider (leg1 - box)// Child 4: Collider (leg2 - box)// Child 5: Collider (leg3 - box)// Child 6: Collider (leg4 - box) // Each child has position offset// Seat:{ type: 0, // Box size: [0.4, 0.05, 0.4], offset: [0, 0.5, 0],} // Leg:{ type: 0, // Box size: [0.05, 0.25, 0.05], offset: [0.35, 0.25, 0.35],}Compound vs Mesh
Compound colliders (multiple simple shapes) are often faster than a single complex mesh. They also work with dynamic bodies, unlike trimesh.
Sensors (Trigger Volumes)#
Sensors detect overlap but don't generate collision response. Perfect for trigger zones, checkpoints, and area detection.
// Trigger zone that detects player entry{ type: 0, // Box size: [5, 2, 5], // 10x4x10 zone offset: [0, 2, 0], isSensor: true, // No collision response!} // Script to detect sensor eventsexport default (api) => { return (dt) => { // Handle collision events const events = api.getCollisionEvents(); for (const event of events) { if (event.isSensor) { if (event.type === 'START') { api.log(`Entity ${event.eid2} entered trigger!`); } else if (event.type === 'END') { api.log(`Entity ${event.eid2} exited trigger!`); } } } };};- Detects overlap (no blocking)
- Generates collision events
- Zero performance cost for physics response
- Good for: triggers, checkpoints, detection zones
Collision Groups and Masks#
Collision groups control which colliders can interact. Each collider has a membership (which groups it belongs to) and a filter (which groups it can collide with).
| Group | Value | Use Case |
|---|---|---|
| DEFAULT | 0x0001 | Default group for all objects |
| PLAYER | 0x0002 | Player character |
| ENEMY | 0x0004 | Enemies and NPCs |
| PROJECTILE | 0x0008 | Bullets, arrows, projectiles |
| TRIGGER | 0x0010 | Trigger volumes |
| ENVIRONMENT | 0x0020 | Static level geometry |
| INTERACTIVE | 0x0040 | Pickups, doors, interactive objects |
| VEHICLE | 0x0080 | Vehicles |
import { CollisionGroups } from '@web-engine/core'; // Player collides with enemies and environmentconst playerGroup = CollisionGroups.create( CollisionGroups.PLAYER, CollisionGroups.ENEMY | CollisionGroups.ENVIRONMENT); // Projectile hits enemies but not player (friendly fire off)const projectileGroup = CollisionGroups.create( CollisionGroups.PROJECTILE, CollisionGroups.ENEMY | CollisionGroups.ENVIRONMENT); // Ghost object (no collision)const ghostGroup = CollisionGroups.create( CollisionGroups.NONE, CollisionGroups.NONE);Collision Logic
Two colliders A and B collide if both conditions are true:
- A's membership overlaps with B's filter: (A.membership & B.filter) !== 0
- B's membership overlaps with A's filter: (B.membership & A.filter) !== 0
Collider Offset#
The offset property positions the collider relative to the body's center. Useful for non-centered shapes.
// Character: capsule offset up so bottom is at feet{ type: 2, // Capsule size: [0.5, 1.0, 0], // Radius 0.5m, half-height 1m offset: [0, 1.0, 0], // Offset up by half-height} // Vehicle: box offset down to ground{ type: 0, // Box size: [1, 0.5, 2], // Car shape offset: [0, -0.5, 0], // Bottom at ground level}Performance Comparison#
| Shape | Performance | Accuracy | Dynamic Support |
|---|---|---|---|
| Sphere | Fastest | Perfect for spheres | Yes |
| Box | Very Fast | Perfect for boxes | Yes |
| Capsule | Very Fast | Perfect for capsules | Yes |
| Convex Hull | Medium | Approximate | Yes |
| Trimesh | Slow | Exact | No (static only) |
| Heightfield | Medium | Good for terrain | No (static only) |
Shape Selection
Always prefer simple shapes (sphere, box, capsule) over complex shapes. Use compound colliders (multiple simple shapes) to approximate complex objects. Reserve trimesh for static level geometry only.
Common Patterns#
Character Collider#
{ type: 2, // Capsule size: [0.5, 1.0, 0], // Radius 0.5m, height 2m offset: [0, 1.0, 0], // Centered at character isSensor: false, collisionGroup: CollisionGroups.PLAYER,}Level Geometry#
{ type: 4, // Trimesh (exact mesh) vertices: meshVertices, indices: meshIndices, offset: [0, 0, 0], isSensor: false, collisionGroup: CollisionGroups.ENVIRONMENT,} // Body MUST be type 1 (Fixed/Static)Trigger Zone#
{ type: 0, // Box size: [5, 2, 5], // 10x4x10 zone offset: [0, 2, 0], isSensor: true, // Trigger! collisionGroup: CollisionGroups.TRIGGER,}