Chipmunk (cp) backend
Chipmunk (cp) backend
Section titled “Chipmunk (cp) backend”Swap the import and create a CpBackend instead:
import collisioncp "golem.collision/cp"
backend := collisioncp.New()srv.SetCollisionBackend(backend)By default all entities are registered as kinematic bodies: your code controls their velocity and position, and the engine reports contacts. For advanced use cases you can access the underlying Chipmunk space to add static geometry (walls, terrain) or switch specific bodies to dynamic mode:
space := backend.Space()// add a static segment wallwall := cp.NewSegment(space.StaticBody, cp.Vector{X: 0, Y: 0}, cp.Vector{X: 100, Y: 0}, 0)wall.SetFilter(cp.NewShapeFilter(cp.NO_GROUP, uint(layerWall), maskAll))space.AddShape(wall)Position corrections from the physics engine are written back to entity structs automatically via SetPosition each tick—clients receive the updated positions without any extra work.
Non-kinematic bodies (cp only)
Section titled “Non-kinematic bodies (cp only)”When two entities should physically stop each other—a ball bouncing off a wall, crates that block movement—you want dynamic bodies. Unlike kinematic bodies where your code sets position each tick, dynamic bodies are owned by the cp physics engine: it integrates their velocity, applies collision-response impulses when they contact solid shapes, and moves them on its own.
Register a dynamic body in OnSpawn using AddDynamic instead of Add:
import ( "golem" collisioncp "golem.collision/cp" cp "github.com/jakecoffman/cp/v2")
// Keep the concrete *CpBackend—AddDynamic and SetVelocity are not on// golem.CollisionBackend. Pass cpBackend to srv.SetCollisionBackend.var cpBackend *collisioncp.CpBackend
func (b *Ball) OnSpawn() { x, y := b.Position() mass := 1.0 moment := cp.MomentForCircle(mass, 0, 0.5, cp.Vector{}) cpBackend.AddDynamic(b.EntityID(), golem.CollisionCircle{R: 0.5}, layerBall, maskAll, false, mass, moment, x, y)}
func (b *Ball) OnRemove() { cpBackend.Remove(b.EntityID())}Use Chipmunk’s moment helpers (cp.MomentForCircle, cp.MomentForBox) for physically sensible values rather than guessing raw moment numbers.
Driving velocity
Section titled “Driving velocity”Dynamic bodies are moved by giving them a velocity, not by setting their position directly. Call SetVelocity in your OnTick callback:
srv.OnTick(func(dt float64, s *golem.Server) { for _, e := range s.All() { if ball, ok := e.(*Ball); ok { cpBackend.SetVelocity(ball.EntityID(), ball.VX(), ball.VY()) } }})SetVelocity is on *CpBackend only, not on golem.CollisionBackend. Keep the concrete type when you create the backend:
cpBackend = collisioncp.New() // *collisioncp.CpBackendsrv.SetCollisionBackend(cpBackend) // SetCollisionBackend accepts the interfaceTick order: the loop runs TickAll → OnTick → collision (Update → Step → ReadBack). Calling SetVelocity in OnTick is the natural place—velocity is in effect when Step runs that tick. Setting velocity inside a per-entity Ticker.Tick (called during TickAll, before OnTick) also works because Update is a no-op for dynamic bodies and does not overwrite it.
How positions sync back
Section titled “How positions sync back”After Step, cp has moved every dynamic body. The server loop calls SetPosition (via ReadBack) on any entity that implements PositionWriter (SetPosition(x, y float32)). All generated Synced* types implement it. Custom entity types must add SetPosition to receive physics-corrected positions—otherwise the position correction has no effect on them.
Triggers and dynamic bodies
Section titled “Triggers and dynamic bodies”AddDynamic accepts trigger: true to register a sensor shape. Overlaps are detected and reported through the same contact pipeline as other shapes (per-entity contact events, and an optional central OnContact if you use it), but cp does not apply collision-response impulses to sensors. Use this for overlap zones (pickups, damage fields) attached to physics-simulated entities.
Restitution, damping, and other tuning
Section titled “Restitution, damping, and other tuning”Global damping and gravity can be set on the space directly:
cpBackend.Space().SetGravity(cp.Vector{X: 0, Y: -9.8})cpBackend.Space().SetDamping(0.9) // velocity multiplied each secondPer-shape restitution and friction are configured on each *cp.Shape you create. For static geometry built via Space(), keep the shape reference and call SetElasticity / SetFriction before adding it to the space. For entity bodies managed by the backend, you can iterate shapes via cpBackend.Space().EachShape(...).
Kinematic and dynamic bodies in the same space
Section titled “Kinematic and dynamic bodies in the same space”Kinematic (Add) and dynamic (AddDynamic) bodies can coexist in the same space and will detect contacts with each other. Don’t use both for the same entity—choose one registration path per entity type and keep it consistent.
Previous: Contacts & events. Next: Overlap & cast queries.