Contacts & events
Contacts & events
Section titled “Contacts & events”For gameplay reactions—pickups, damage zones, bumping into walls—you usually implement per-entity contact events: enter/stay/exit hooks on the entity types that should care, instead of one central function that switches on every pair.
Per-entity contact events
Section titled “Per-entity contact events”Call EnableContactEvents once during setup (after SetCollisionBackend). Then implement any of the six opt-in interfaces on your entity types; the server dispatches them from the same contact list the backend produces each tick.
srv.SetCollisionBackend(backend)srv.EnableContactEvents()EnableContactEvents requires a collision backend; without one no contacts are produced and no events fire.
These per-entity enter/stay/exit interfaces are for 2D collision backends. The 3D collision MVP reports contacts through OnContact3D; see 3D collision.
The six interfaces
Section titled “The six interfaces”| Interface | Method | When called |
|---|---|---|
golem.TriggerEnter | OnTriggerEnter(other Entity) | First tick two trigger shapes overlap |
golem.TriggerStay | OnTriggerStay(other Entity) | Every subsequent tick they remain overlapping |
golem.TriggerExit | OnTriggerExit(other Entity) | First tick they no longer overlap |
golem.CollisionEnter | OnCollisionEnter(other Entity, normal CollisionVec2, depth float64) | First tick two solid shapes overlap |
golem.CollisionStay | OnCollisionStay(other Entity, normal CollisionVec2, depth float64) | Every subsequent tick they remain overlapping |
golem.CollisionExit | OnCollisionExit(other Entity) | First tick they no longer overlap |
Implement only the interfaces you need — unused ones have no overhead.
Normal vector convention
Section titled “Normal vector convention”normal in CollisionEnter and CollisionStay is a unit vector pointing away from other toward the receiver. Both sides receive the correct outward normal: entity A gets the raw contact normal; entity B gets the negated vector. This matches the per-collider convention from Unity.
Example
Section titled “Example”type Player struct{ *synced.SyncedPlayer }
func (p *Player) OnTriggerEnter(other golem.Entity) { if pickup, ok := other.(*Pickup); ok { pickup.Collect(p) }}
func (p *Player) OnCollisionEnter(other golem.Entity, normal golem.CollisionVec2, depth float64) { // normal points away from other, toward p log.Printf("player bumped into %T (depth=%.2f)", other, depth)}nil other
Section titled “nil other”other is nil when the partner entity was removed from the registry before the event fires (typically in the same tick). This most commonly occurs in OnTriggerExit and OnCollisionExit. Always guard against nil if your callback dereferences other:
func (p *Player) OnTriggerExit(other golem.Entity) { if other == nil { return // partner was deleted } // ...}OnTriggerStay performance note
Section titled “OnTriggerStay performance note”OnTriggerStay fires every tick for every active overlap, not just on transitions. For large numbers of simultaneously overlapping entities this scales linearly with overlap count. Prefer OnTriggerEnter/OnTriggerExit when you only need to know when the state changes.
Optional: central OnContact callback
Section titled “Optional: central OnContact callback”If you prefer one function that receives every overlapping pair—for a quick prototype, logging, or a small game with simple global rules—register OnContact. It does not replace per-entity events; it runs in addition when both are set. It does not require EnableContactEvents.
Each golem.CollisionContact has:
A,B— the two entity IDs involved.Normal— a unit vector pointing in the push-out direction for entity A (away from B).Depth— the penetration distance.0for trigger contacts.
srv.OnContact(func(contacts []golem.CollisionContact) { for _, c := range contacts { if c.Depth == 0 { // trigger overlap between c.A and c.B handleTrigger(c.A, c.B) } else { // solid collision — c.Normal and c.Depth describe the push handleBump(c.A, c.B, c.Normal, c.Depth) } }})OnContact is only called when there is at least one contact. If nothing overlaps in a given tick, your handler is not called.
Previous: Colliders. Next: Chipmunk (cp) backend, Overlap & cast queries, 3D collision.