Web Engine Docs
Preparing documentation
Use the search bar to quickly find any topic
Preparing documentation
Use the search bar to quickly find any topic
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).
Rectangular box defined by half-extents (X, Y, Z). Fast and stable. Best for crates, walls, buildings.
Perfect sphere defined by radius. Fastest shape for collision detection. Best for balls, planets.
Cylinder with rounded ends (radius + height). Excellent for characters - smooth sliding and no getting stuck.
Grid of height values for terrain. Efficient for large outdoor environments.
Exact mesh collision using triangle mesh. Static bodies only. Best for complex level geometry.
Convex approximation of a mesh. More expensive but allows dynamic bodies.
| 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) |
Rectangular box defined by half-extents. The size property specifies half the width, height, and depth.
// 2x2x2 cube (size = half-extents){type: 0, // Boxsize: [1, 1, 1], // Half-extents: 1m in each directionoffset: [0, 0, 0],}// Wall: 10m wide, 5m tall, 0.5m thick{type: 0, // Boxsize: [5, 2.5, 0.25], // Half-extentsoffset: [0, 2.5, 0], // Offset up by half-height}
Perfect sphere defined by radius. The size.x property specifies the radius.
// Sphere with 1m radius{type: 1, // Spheresize: [1, 0, 0], // Only X component used (radius)offset: [0, 0, 0],}// Basketball (radius ~0.12m){type: 1, // Spheresize: [0.12, 0, 0],offset: [0, 0.12, 0], // Offset to sit on ground}
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, // Capsulesize: [0.5, 1.0, 0], // Radius (X), Half-height (Y)offset: [0, 1.0, 0], // Center at character center}// Barrel{type: 2, // Capsulesize: [0.3, 0.5, 0], // Radius 0.3m, height 1moffset: [0, 0.5, 0],}
Character Colliders
Always use a Capsule for character colliders. The rounded ends prevent getting stuck on stairs and edges. Avoid box colliders for characters.
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, // ConvexHullvertices: meshVertices, // Float32Array of verticesoffset: [0, 0, 0],}
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).
Exact mesh collision using triangles. Most accurate but also most expensive. Only works with static (fixed) bodies.
// Trimesh collider for level geometry{type: 4, // Trimeshvertices: meshVertices, // Float32Arrayindices: meshIndices, // Uint32Arrayoffset: [0, 0, 0],}// Note: Parent body MUST be type 1 (Fixed/Static)
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.
Efficient terrain representation using a grid of height values. Perfect for large outdoor environments.
// Heightfield terrain{type: 3, // Heightfieldheights: heightData, // Float32Array of height valuesnrows: 128, // Number of rowsncols: 128, // Number of columnsscale: { x: 1, y: 1, z: 1 }, // Scale factors}
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, // Boxsize: [0.4, 0.05, 0.4],offset: [0, 0.5, 0],}// Leg:{type: 0, // Boxsize: [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 detect overlap but don't generate collision response. Perfect for trigger zones, checkpoints, and area detection.
// Trigger zone that detects player entry{type: 0, // Boxsize: [5, 2, 5], // 10x4x10 zoneoffset: [0, 2, 0],isSensor: true, // No collision response!}// Script to detect sensor eventsexport default (api) => {return (dt) => {// Handle collision eventsconst 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!`);}}}};};
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
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, // Capsulesize: [0.5, 1.0, 0], // Radius 0.5m, half-height 1moffset: [0, 1.0, 0], // Offset up by half-height}// Vehicle: box offset down to ground{type: 0, // Boxsize: [1, 0.5, 2], // Car shapeoffset: [0, -0.5, 0], // Bottom at ground level}
| 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.
{type: 2, // Capsulesize: [0.5, 1.0, 0], // Radius 0.5m, height 2moffset: [0, 1.0, 0], // Centered at characterisSensor: false,collisionGroup: CollisionGroups.PLAYER,}
{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)
{type: 0, // Boxsize: [5, 2, 5], // 10x4x10 zoneoffset: [0, 2, 0],isSensor: true, // Trigger!collisionGroup: CollisionGroups.TRIGGER,}