Skip to content

Overlap & cast queries

The collision backends can answer one-off questions about the world without adding trigger colliders: either what is inside a region right now (overlap queries) or what would a ray or moving shape hit (cast queries). Both families are exposed as methods on golem.Server, use the same layer mask rules as Colliders, and do not register persistent shapes—the next tick’s collision step is unaffected.

Built-in golem.collision/resolv and golem.collision/cp implement both capabilities for 2D collision. If no backend is set, or the backend does not implement the corresponding optional interface, overlap methods return nil and cast methods return false / an empty hit / nil for *All variants (see the table below). For 3D methods such as OverlapSphere, OverlapBox3D, and Raycast3D, see 3D collision.

For every query, layerMask uses the same convention as Add: an entity is considered only if (entityLayer & layerMask) != 0. Pass 0xFFFFFFFF to include all layers, or use layers.MaskFor(...) from a CollisionLayers registry (see Colliders):

// Include any entity on the Enemy or Boss layer.
ids := s.OverlapCircle(x, y, 200, layers.MaskFor("Enemy", "Boss"))
// Include everything.
ids = s.OverlapBox(cx, cy, hw, hh, 0xFFFFFFFF)

Use these when you care about volume: aggro bubbles, explosion radii, “who is in this rectangle”, scoring zones.

  • OverlapBox(cx, cy, hw, hh, layerMask) — axis-aligned box centred at (cx, cy) with half-extents (hw, hh), same as CollisionAABB half-size.
  • OverlapCircle(cx, cy, radius, layerMask) — circle centred at (cx, cy) with the given radius.

Returns []int64 entity IDs, or nil if the backend is missing or does not implement SpatialQuery.

srv.OnTick(func(dt float64, s *golem.Server) {
// Enemies inside a 5×3 box centred at (12, 8).
ids := s.OverlapBox(12, 8, 2.5, 1.5, layers.MaskFor("Enemy"))
for _, id := range ids {
e, _ := s.Get(id)
triggerAggro(e)
}
// Everything within radius 4 of an explosion at (20, 20).
ids = s.OverlapCircle(20, 20, 4, 0xFFFFFFFF)
for _, id := range ids {
applyBlast(id)
}
})

Use these when you care about ordered hits along a path: line of sight, bullets, sweeping a hitbox before allowing movement.

Each successful hit is a CollisionRaycastHit (shared by rays and sweeps):

FieldTypeMeaning
EntityIDint64Entity that was hit
PointCollisionVec2World-space contact point
NormalCollisionVec2Unit normal: away from the hit shape, toward the cast origin (ray start or sweep start)
Fractionfloat64Distance along the cast, 0 = start, 1 = end
  • Raycast(x1, y1, x2, y2, layerMask) — first hit on segment (x1,y1)(x2,y2); (hit, true) or zero value and false.
  • RaycastAll(...) — every hit along the segment, sorted by Fraction (closest first); nil if none.
srv.OnTick(func(dt float64, s *golem.Server) {
if hit, ok := s.Raycast(5, 5, 20, 12, layers.MaskFor("Wall")); ok {
fmt.Printf("wall at (%.1f, %.1f), fraction %.2f\n",
hit.Point.X, hit.Point.Y, hit.Fraction)
}
})
hits := s.RaycastAll(ox, oy, ox+dir.X*range_, oy+dir.Y*range_, layers.MaskFor("Enemy"))
for _, h := range hits {
applyDamage(h.EntityID)
}

Sweep origin is (ox, oy); (dx, dy) is the displacement (not an end point). Half-extents (hw, hh) match CollisionAABB; circle radius matches CollisionCircle.

  • BoxCast / BoxCastAll — AABB sweep.
  • CircleCast / CircleCastAll — circle sweep.
// Can a 2×1 crate slide 5 units right without hitting the Wall layer?
if _, ok := s.BoxCast(cx, cy, 1.0, 0.5, 5, 0, layers.MaskFor("Wall")); !ok {
moveCrate(cx+5, cy)
}
hits := s.CircleCastAll(gx, gy, 2.0, vx, vy, 0xFFFFFFFF)
for _, h := range hits {
scheduleBlastAt(h.EntityID, h.Point)
}

Cast methods require the backend to implement CastQuery. Otherwise you get false / empty hit / nil for *All, same as when nothing is hit (for the single-hit APIs, you can’t distinguish “no backend” from “clear line” without checking SetCollisionBackend yourself).

MethodKindReturns when nothing / no backend
OverlapBoxoverlapnil
OverlapCircleoverlapnil
Raycastcastzero CollisionRaycastHit, false
RaycastAllcastnil
BoxCastcastzero hit, false
BoxCastAllcastnil
CircleCastcastzero hit, false
CircleCastAllcastnil

Timing: the backend refreshes shape positions during Update, which runs after OnTick. Calls to OverlapBox, Raycast, etc. from inside OnTick therefore use poses as of the previous tick’s collision step, not movement from the current Ticker.Tick or OnTick. To query against this tick’s positions, run the query on the next tick (or test directly against your own state if you prefer).

Previous: Chipmunk (cp) backend. See also: Colliders, Collision & physics, 3D collision.