3D collision
3D collision
Section titled “3D collision”Use the 3D collision backend when a project has simulation.dimensions: 3 and the server needs authoritative volume checks: spheres around pickups, boxes around hazards, raycasts for line of sight, or a central contact list for gameplay rules. The built-in 3D backend is pure Go and detection-only; it does not simulate rigid bodies or move entities for you.
Enable 3D positions
Section titled “Enable 3D positions”3D collision expects generated entities to expose Position3D, so start by enabling 3D positions in golem.yaml:
simulation: dimensions: 3After golem-bake, generated Go entities have SetPosition3D(x, y, z) and clients receive posZ / PosZ alongside X/Y. Interest management still uses the XY projection; see Interest management.
Wire the backend
Section titled “Wire the backend”Create the pure-Go backend and give it to the server:
backend := golem.NewCollisionSimple3DBackend()srv.SetCollision3DBackend(backend)Each tick, the server reads Position3D() from live entities, updates the backend, runs collision detection, and calls OnContact3D if any pairs overlap.
Register shapes
Section titled “Register shapes”Register shapes from entity lifecycle hooks, just like 2D colliders. The 3D MVP accepts raw layer and mask bitmasks:
const ( layerPlayer uint32 = 1 << iota layerEnemy layerWorld)
type Player struct { *synced.SyncedPlayer}
func (p *Player) OnSpawn() { backend.Add( p.EntityID(), golem.CollisionSphere{R: 0.5}, layerPlayer, layerEnemy|layerWorld, false, )}
func (p *Player) OnRemove() { backend.Remove(p.EntityID())}Available 3D shapes:
| Type | Fields | Description |
|---|---|---|
golem.CollisionSphere | R float64 | Sphere centered on the entity’s Position3D |
golem.CollisionAABB3D | W, H, D float64 | Axis-aligned box centered on the entity’s Position3D |
The trigger argument follows the 2D convention: trigger contacts are reported with Depth == 0.
Read contacts
Section titled “Read contacts”Register a central callback when one place should handle every 3D overlap:
srv.OnContact3D(func(contacts []golem.CollisionContact3D) { for _, c := range contacts { if c.Depth == 0 { handleTrigger3D(c.A, c.B) continue } handleSolid3D(c.A, c.B, c.Normal, c.Depth) }})CollisionContact3D.Normal is a golem.CollisionVec3 push direction for entity A, pointing away from entity B. Depth is the penetration distance for solid contacts.
Per-entity enter/stay/exit interfaces are currently 2D-only. For 3D collision, use OnContact3D and track transitions in game code if you need enter/exit semantics.
Query the world
Section titled “Query the world”Use 3D overlap queries for volume checks:
nearby := srv.OverlapSphere(px, py, pz, 8, layerEnemy)insideRoom := srv.OverlapBox3D(cx, cy, cz, 5, 3, 5, layerPlayer)Use raycasts for ordered line checks:
hit, ok := srv.Raycast3D( golem.CollisionVec3{X: eyeX, Y: eyeY, Z: eyeZ}, golem.CollisionVec3{X: targetX, Y: targetY, Z: targetZ}, layerWorld|layerEnemy,)if ok { log.Println("hit entity", hit.EntityID, "at", hit.Point)}RaycastAll3D returns every hit sorted by Fraction, closest first.
Limits
Section titled “Limits”The built-in 3D backend is intentionally small:
- It detects sphere/sphere, sphere/AABB, and AABB/AABB overlaps.
- It supports
OverlapSphere,OverlapBox3D,Raycast3D, andRaycastAll3D. - It does not integrate velocity, apply impulses, solve constraints, rotate boxes, or provide character controllers.
- It uses a simple pairwise pass, so very large collider counts should move to a future broadphase or native physics backend.
For 2D physics simulation, see Chipmunk (cp) backend. For 2D overlap and cast queries, see Overlap & cast queries.