Backends & wiring
Backends & wiring
Section titled “Backends & wiring”NPCs need to find routes through your world—around walls, through corridors, and across tile maps. golem-engine provides a nav backend system: you pick a backend, build a walkability grid from your map data at startup, and the server’s FindPath delegates to it automatically.
Available backends
Section titled “Available backends”The nav system is pluggable—any type that implements golem.NavBackend works. Two backends ship out of the box:
golem.nav/pathing— grid A* built on quasilyte/pathing. Zero-allocation, the fastest option. Passable/impassable only (no variable terrain costs). Requires external synchronisation ifSetNavWalkableandFindPathare called from different goroutines.golem.nav/kelindar— grid A* built on kelindar/tile. Thread-safe concurrent reads and writes. Supports variable per-cell traversal costs (CostOf). AddsAroundfor BFS radius scans,WriteValue/ReadValuefor runtime terrain changes, and aGrid()accessor for observers, save/load, and bounded tile iteration.
Both have the same constructor signatures (New, NewFromTiledLayer, NewFromLDtkIntGrid) so switching is a one-line import change. If you need a completely different algorithm—flow fields, navmesh, a waypoint graph—implement golem.NavBackend and pass it in; nothing else changes.
The built-in navigation backends are 2D grid pathfinders. In a simulation.dimensions: 3 project, entities can still use 3D positions for replication and collision, but FindPath and NavAgent operate on X/Y waypoints until you provide a custom 3D or navmesh backend.
Wiring up the pathing backend
Section titled “Wiring up the pathing backend”go get golem.nav/pathingimport ( "golem" navpathing "golem.nav/pathing")
layer := tiledMap.LayerByName("Walkable")nav, err := navpathing.NewFromTiledLayer( tiledMap.Width, tiledMap.Height, tiledMap.TileWidth, tiledMap.TileHeight, layer.Data, func(gid int) bool { return gid == wallGID },)if err != nil { log.Fatal(err)}srv.SetNavBackend(nav)Wiring up the kelindar backend
Section titled “Wiring up the kelindar backend”Same constructor signatures—swap the import to switch:
go get golem.nav/kelindarimport ( "golem" navkelindar "golem.nav/kelindar")
layer := tiledMap.LayerByName("Walkable")nav, err := navkelindar.NewFromTiledLayer( tiledMap.Width, tiledMap.Height, tiledMap.TileWidth, tiledMap.TileHeight, layer.Data, func(gid int) bool { return gid == wallGID },)if err != nil { log.Fatal(err)}srv.SetNavBackend(nav)Dynamic walkability updates
Section titled “Dynamic walkability updates”Doors, destructible walls, and triggered barriers change which cells are passable at runtime. Call SetNavWalkable to toggle a cell without rebuilding the grid:
// When a door opens:srv.SetNavWalkable(door.X, door.Y, true)
// When a door closes:srv.SetNavWalkable(door.X, door.Y, false)SetNavWalkable is a no-op if no backend is set or if the backend does not support dynamic updates. Both built-in backends support it.
Path length and large maps
Section titled “Path length and large maps”The pathing backend handles arbitrarily long paths transparently by chaining multiple internal searches. The kelindar backend has no path length limit. Neither requires any path-length management from your code; FindPath always returns the complete route or nil, false if none exists.
See also: Map sources, NavAgent, kelindar backend.