arcade2d
Class

Grid

geometry/grid.ts:67

A fixed-size rectangular lattice of Cells addressed by integer x (column) / y (row) coordinates — the backbone for tile maps, board games, tactics movement, fog-of-war, and anything else that reasons about discrete positions.

Coordinate model

The grid is width columns by height rows. x runs 0 … width - 1 left to right; y runs 0 … height - 1 top to bottom, matching the engine's screen-space convention (so Cell.up moves toward smaller y). Every position is named in x, y order throughout — never row, column.

Fixed size, stable cells

Dimensions are immutable: there is no adding or removing rows. Each cell is created once at construction and lives for the grid's lifetime, so cell references stay valid and reference-equality (===) is a reliable identity check — Grid.cellAt returns the same Cell instance every time.

Lookups never throw

Grid.cellAt returns null for out-of-bounds coordinates rather than throwing, so callers branch on the result. Grid.clamp and Grid.wrap always resolve to a real cell by folding the coordinate back in range.

Navigation, queries, and pathfinding

Walk locally with Cell.neighbors and Cell.left/right/up/ down; sweep areas with Grid.line (Bresenham) and Grid.within (radius); and route with Grid.findPath (A*), which reads Cell.passable/Cell.cost by default and accepts per-call overrides.

Example

type Tile = 'floor' | 'wall';

// Seed every cell with floor, then wall off a column.
const grid = new Grid<Tile>(16, 12, () => 'floor');
for (let y = 0; y < grid.height; y++) {
  const cell = grid.cellAt(8, y)!;
  cell.data = 'wall';
  cell.passable = false;
}
grid.cellAt(8, 6)!.passable = true; // a gap in the wall

const path = grid.findPath({ x: 1, y: 6 }, { x: 14, y: 6 });
// -> ordered Cells routing through the gap, or null if blocked

Constructors

#
constructor(width: number, height: number, init?: GridInit<TData>): Grid<TData>

Creates a grid and all of its cells.

Parameters

  • width number
  • height number
  • init? GridInit<TData>

Throws

An EngineError with ErrorCode.GRID_INVALID_SIZE if width or height is not a positive integer.

Properties

readonly #
height: number

The number of rows. Must be a positive integer.

readonly #
width: number

The number of columns. Must be a positive integer.

Accessors

readonly #
size: number

The total number of cells in the grid (width * height).

Methods

#
[iterator](): IterableIterator<Cell<TData>>

Iterates every cell in row-major order (row 0 left-to-right, then row 1, and so on), making the grid spreadable and for…of-able.

Returns

IterableIterator<Cell<TData>>

Example

for (const cell of grid) {
  if (cell.empty) cell.data = 'floor';
}
const all = [...grid];
#
cellAt(x: number, y: number): null | Cell<TData>

Resolves the cell at an integer coordinate.

Parameters

  • x number
  • y number

Returns

null | Cell<TData>

The cell, or null if the coordinate is out of bounds or not an integer.

#
clamp(x: number, y: number): Cell<TData>

Resolves the in-bounds cell nearest a coordinate by clamping each axis to the grid's edges. Always returns a cell. Non-integer inputs are rounded.

Parameters

  • x number
  • y number

Returns

Cell<TData>
#
column(x: number): Cell<TData>[]

The cells of a single column, top to bottom.

Parameters

  • x number

Returns

Cell<TData>[]

The column's cells, or an empty array if x is out of bounds.

#
contains(x: number, y: number): boolean

Whether an integer coordinate lies within the grid.

Parameters

  • x number
  • y number

Returns

boolean
#
filter(predicate: (cell: Cell<TData>, x: number, y: number) => boolean): Cell<TData>[]

Collects every cell matching a predicate, in row-major order.

Parameters

  • predicate (cell: Cell<TData>, x: number, y: number) => boolean

Returns

Cell<TData>[]

The matching cells.

#
find(predicate: (cell: Cell<TData>, x: number, y: number) => boolean): null | Cell<TData>

Finds the first cell matching a predicate, in row-major order.

Parameters

  • predicate (cell: Cell<TData>, x: number, y: number) => boolean

Returns

null | Cell<TData>

The first matching cell, or null if none match.

#
findPath(start: PointPrimitive, end: PointPrimitive, options?: PathfindingOptions<TData>): null | Cell<TData>[]

Finds the cheapest path between two coordinates using A*. Delegates to the grid findPath helper.

Walkability and movement cost default to each cell's Cell.passable and Cell.cost; override them per call via PathfindingOptions.isPassable / PathfindingOptions.cost without touching the grid. Connectivity follows PathfindingOptions.diagonal (4-connected by default), and diagonal steps cost √2 times the destination cell's cost.

The start is always a valid origin even if impassable; the end must be passable.

Parameters

Returns

null | Cell<TData>[]

The ordered cells from start to end inclusive, or null if either endpoint is out of bounds, the end is impassable, or no route exists.

Example

// Treat a separate boolean mask as the walkability source.
const path = grid.findPath(start, goal, {
  diagonal: true,
  isPassable: (cell) => mask.cellAt(cell.x, cell.y)?.data === true,
});
#
forEach(callback: (cell: Cell<TData>, x: number, y: number) => void): void

Runs a callback for every cell in row-major order.

Parameters

  • callback (cell: Cell<TData>, x: number, y: number) => void

Returns

void
#
line(from: PointPrimitive, to: PointPrimitive): Cell<TData>[]

Traces a straight line of cells between two coordinates using Bresenham's algorithm — the basis for line-of-sight, ray casts, and beam effects. Endpoints are inclusive; cells that fall outside the grid are skipped.

Parameters

Returns

Cell<TData>[]

The in-bounds cells along the line, ordered from from to to.

Example

const sight = grid.line(archer, target);
const blocked = sight.some((cell) => !cell.passable);
#
map(callback: (cell: Cell<TData>, x: number, y: number) => R): R[]

Maps every cell to a value, in row-major order.

Parameters

  • callback (cell: Cell<TData>, x: number, y: number) => R

Returns

R[]

The mapped values.

#
row(y: number): Cell<TData>[]

The cells of a single row, left to right.

Parameters

  • y number

Returns

Cell<TData>[]

The row's cells, or an empty array if y is out of bounds.

#
toString(): string

Returns a string representation of this grid (its dimensions).

Returns

string
#
within(center: PointPrimitive, radius: number, options: WithinOptions): Cell<TData>[]

Collects the cells within a given radius of a centre coordinate. The radius is measured with the chosen GridMetric: 'manhattan' (the default) yields a diamond, 'chebyshev' a square, and 'euclidean' a disc.

Parameters

Returns

Cell<TData>[]

The cells in range. When wrapping, each cell appears at most once.

Example

const blast = grid.within(impact, 3, { metric: 'euclidean' });
const reachable = grid.within(unit, unit.moves, { includeCenter: false });
#
wrap(x: number, y: number): Cell<TData>

Resolves a cell by wrapping the coordinate across the grid's edges, so the grid behaves as a torus. Always returns a cell; negative and overflowing coordinates fold back in range. Non-integer inputs are floored.

Parameters

  • x number
  • y number

Returns

Cell<TData>
ESC