Colliders
Colliders
Section titled “Colliders”The server drives the per-tick collision step, but it does not know which entities should participate—that is game code’s responsibility. Register a shape when an entity spawns and unregister it when the entity is removed. The cleanest place is the OnSpawn / OnRemove hooks on the entity struct.
This page covers the 2D collision backends. For 3D projects using CollisionSphere or CollisionAABB3D, see 3D collision.
Defining layers
Section titled “Defining layers”Set up your collision matrix once at startup. Call Bind to attach the backend, Define to name your layers, and SetCollides to declare which pairs interact — the rule is always symmetric, so you cannot accidentally create a one-sided filter:
var layers = golem.NewCollisionLayers(). Bind(backend). Define("Player", "Enemy", "Wall", "Projectile"). SetCollides("Player", "Wall"). SetCollides("Enemy", "Wall"). SetCollides("Projectile", "Enemy"). SetCollides("Projectile", "Wall")SetCollides("Player", "Wall") records that Player shapes test against Wall and Wall shapes test against Player. The layer bit and mask are derived automatically when you register or replace a shape — you never write raw uint32 values.
Up to 32 layers can be defined. Define, SetCollides, Add, Set, and Remove all panic on misuse (unknown name, duplicate name, over-32, unbound backend) so mistakes surface at startup rather than as silent filtering bugs.
Registering shapes
Section titled “Registering shapes”Call layers.Add in OnSpawn and layers.Remove in OnRemove. The layer name is the only thing you pass — the correct bit and mask come from the matrix you configured above:
type Player struct { *synced.SyncedPlayer}
func (p *Player) OnSpawn() { layers.Add(p.EntityID(), golem.CollisionCircle{R: 0.5}, "Player", false)}
func (p *Player) OnRemove() { layers.Remove(p.EntityID())}The fourth argument is trigger. When true, the shape reports overlaps but does not request push-out (contact Depth is always 0). Use triggers for overlap zones like item pickups or damage fields.
Available shapes
Section titled “Available shapes”| Type | Fields | Description |
|---|---|---|
golem.CollisionCircle | R float64 | Circle centred on the entity’s position |
golem.CollisionAABB | W, H float64 | Axis-aligned bounding box centred on the entity’s position |
Changing a collider at runtime
Section titled “Changing a collider at runtime”To replace an entity’s shape or trigger flag without removing it from the game, use layers.Set:
// Shrink the player's hitbox while crouching.func (p *Player) OnCrouchStart() { layers.Set(p.EntityID(), golem.CollisionAABB{W: 0.6, H: 0.9}, "Player", false)}
func (p *Player) OnCrouchEnd() { layers.Set(p.EntityID(), golem.CollisionAABB{W: 0.6, H: 1.8}, "Player", false)}Set preserves the entity’s current position. If the entity is not registered when Set is called, it is a no-op; call Add first.
Spatial queries
Section titled “Spatial queries”Layer, Mask, and MaskFor are still available when you need raw bitmask values for spatial queries:
ids := srv.OverlapCircle(x, y, 200, layers.MaskFor("Enemy"))hit, ok := srv.Raycast(x1, y1, x2, y2, layers.MaskFor("Wall", "Enemy"))Raw bitmasks: if you prefer not to use
CollisionLayers, plainuint32constants work fine — the underlying filter is just(A.layer & B.mask) != 0.CollisionLayersis a helper that derives and cross-checks those values for you.
Previous: Collision & physics. Next: Contacts & events, 3D collision.