Generic 2D grid library for game development with rectangular and hexagonal layouts, spatial queries, and pathfinding.
go get github.com/gravitton/gridRectangular grid:
import (
geom "github.com/gravitton/geometry"
"github.com/gravitton/grid"
)
type Tile struct {
Walkable bool
Cost float64
}
g := grid.NewRectGrid[Tile](geom.Sz(100, 100), geom.Sz(32.0, 32.0))
g.Fill(Tile{Walkable: true, Cost: 1.0})
cell := g.At(geom.Pt(499.0, 123.4))
cell.Set(Tile{Walkable: false})Hexagonal grid:
g := grid.NewHexagonFlatTopGrid[Tile](geom.Sz(20, 20), geom.SzU(32.0))Pathfinding:
path := g.Path(
geom.Pt(0, 0),
geom.Pt(10, 10),
func(c *grid.Cell[Tile]) bool {
return c.Get().Walkable
},
func(current, next *grid.Cell[Tile]) float64 {
return next.Get().Cost
},
)Iterating a viewport:
for cell := range g.Iter(&grid.IterConfig{Bounds: viewport}) {
draw(cell)
}Iter resolves the draw order automatically from the grid type so tiles are always yielded back-to-front (painter's algorithm):
| Grid type | Iteration order |
|---|---|
Rectangular (SquareFlat) |
Row-major, top-to-bottom |
Isometric (SquareIsometric) |
Diagonal depth (col+row) ascending |
Hex flat-top (HexFlatTop) |
Two-pass columns — even then odd |
Hex pointy-top (HexPointyTop) |
Row-major (row offset is X-only) |
One extra row and column are included at every edge of the bounds so partially visible tiles are never culled. Border cells may lie outside the grid — check cell.Valid() before reading data.
Full documentation is available at pkg.go.dev/github.com/gravitton/grid.
| Type | Description |
|---|---|
Grid[T] |
2D grid with spatial mapping and graph operations |
Cell[T] |
Single cell — provides value access, spatial info, and pathfinding |
Array[T] |
Low-level flat 2D array |
Point |
Grid coordinate with spatial query methods (Range, FieldOfView, HasLineOfSight) |
Direction |
One of eight neighbor directions: E, NE, N, NW, W, SW, S, SE |
System |
Movement connectivity: Cardinal (4-dir) or Diagonal (8-dir) |
| Constructor | Description |
|---|---|
NewRectGrid[T](size, cellSize, opts...) |
Rectangular grid, 4-directional movement |
NewRectGrid[T](..., RectGridOpts.DiagonalMovement()) |
Rectangular grid, 8-directional movement |
NewIsometricRectGrid[T](size, cellSize, opts...) |
Isometric (diamond) grid |
NewHexagonPointyTopGrid[T](size, hexSize, opts...) |
Hexagonal grid, pointy-top orientation |
NewHexagonFlatTopGrid[T](size, hexSize, opts...) |
Hexagonal grid, flat-top orientation |
Arr[T](size ints.Size) |
Standalone 2D array |
These functions compute the cellSize / hexSize argument for the grid constructors.
| Function | Use with | Description |
|---|---|---|
RectCellSize(width) |
NewRectGrid |
Square tile, width px wide and tall (1:1) |
IsometricRectCellSize(width) |
NewIsometricRectGrid |
Diamond tile, width px wide at true 30° angle (height ≈ width·0.577) |
IsometricPixelPerfectRectCellSize(width) |
NewIsometricRectGrid |
Diamond tile, width px wide, width/2 px tall (2:1) |
HexFlatTopCellSize(width) |
NewHexagonFlatTopGrid |
Flat-top hex, width px wide, width·√3/2 px tall |
HexPointyTopCellSize(width) |
NewHexagonPointyTopGrid |
Pointy-top hex, width px wide, width·2/√3 px tall |
HexFlatTopIsometricPixelPerfectCellSize(width) |
NewHexagonFlatTopGrid |
Flat-top hex, width px wide, width·3/4 px tall (4:3) |
HexPointyTopIsometricPixelPerfectCellSize(width) |
NewHexagonPointyTopGrid |
Pointy-top hex, width px wide, width px tall (1:1) |
// Size
g.Size() ints.Size
g.Width() int
g.Height() int
// Spatial
g.Bounds() floats.Rectangle
g.CellBounds() floats.Size
g.CellSpacing() floats.Size
// Access
g.Get(index ints.Point) *Cell[T]
g.Has(index ints.Point) bool
g.Set(index ints.Point, value T)
g.At(point floats.Point) *Cell[T] // world-space → cell
g.IndexAt(point floats.Point) ints.Point
// Mutation
g.Fill(value T)
g.Clear()
g.Clone() *Grid[T]
// Iteration (draw order resolved automatically from grid type)
g.Iter(config *IterConfig) iter.Seq[*Cell[T]] // config nil = full grid boundsc.Index() ints.Point
c.Valid() bool
c.Get() *T
c.Set(value T)
c.Center() floats.Point
c.Bounds() floats.Rectangle
c.Polygon() floats.RegularPolygon
c.Neighbors() []ints.Point
c.DistanceTo(to ints.Point) int
c.Range(n int, valid ValidFunc[T]) []ints.Point
c.PathTo(to ints.Point, valid ValidFunc[T], cost CostFunc[T]) []*Cell[T]All algorithms accept an optional ValidFunc to mark cells as passable and an optional CostFunc for weighted traversal. Path uses A* internally.
| Method | Algorithm | Field variant |
|---|---|---|
Path(from, to, valid, cost) |
A* | — |
AStar(from, to, valid, cost) |
A* | — |
GreedyBestFirstSearch(from, to, valid) |
Greedy Best-First | — |
UniformCostSearch(from, to, valid, cost) |
Dijkstra | UniformCostSearchField |
BreadthFirstSearch(from, to, valid) |
BFS | BreadthFirstSearchField |
*Field variants return the full map[ints.Point]ints.Point came-from map instead of a single path.
a.Size() ints.Size
a.Width() int
a.Height() int
a.Len() int
a.Has(index ints.Point) bool
a.Get(index ints.Point) *T
a.Set(index ints.Point, value T)
a.Fill(value T)
a.Clear()
a.Clone() Array[T]
a.Iter() iter.Seq[ints.Point]
a.Iter2() iter.Seq2[ints.Point, *T]g.Distance(from, to ints.Point) int
g.Range(index ints.Point, n int, valid ValidFunc[T]) []ints.PointBoth rectangular and hexagonal grids apply a field-of-view algorithm to Range — blocked cells occlude cells behind them. On rectangular grids this uses Bresenham line-of-sight; on hexagonal grids it uses the hex FoV algorithm.
// Direction constants (ordered counterclockwise from East)
E, NE, N, NW, W, SW, S, SE Direction
// Direction vectors
CardinalDirections [4]ints.Vector // E, N, W, S
DiagonalDirections [4]ints.Vector // NE, NW, SW, SE
Directions [8]ints.Vector // all 8, indexed by Direction constant
d.Opposite() Direction
d.String() string
NeighborOffsets(system System) []ints.Vector
NeighborOffset(system System, direction Direction) ints.Vector
DistanceTo(from, to ints.Point, system System) intPoint is a grid coordinate with spatial query methods, mirroring the hex grid API:
p := grid.Pt(x, y)
p.Range(n int) []ints.Point
p.FieldOfView(candidates, blocking []ints.Point) []ints.Point
p.HasLineOfSight(target ints.Point, blocking []ints.Point) bool
p.Neighbors(system System) []ints.Point
p.DistanceTo(to ints.Point, system System) int
p.Point() ints.Pointtype ValidFunc[T any] func(current *Cell[T]) bool
type CostFunc[T any] func(current, next *Cell[T]) float64The MIT License (MIT). Please see License File for more information.