Skip to main content

GridMap Module

The GridMap module provides a 2D grid data structure optimized for games and applications that require tile maps, pathfinding, and cell-based collision detection. Each cell stores an integer value from 0 to 255, allowing representation of different terrain types, obstacles, or states.

JARU GridMap module screenshot for creating tile maps, levels, and paths in games

This module is especially useful for developing arcade-style games, grid-based puzzles, map simulations, and any application that needs intelligent navigation between points.

Usage

use GridMap

Constructor

new

The GridMap.new(width, height) function creates a new grid with the specified dimensions. All cells are initialized to 0.

use GridMap

// Create a 20x15 cell grid
var map = GridMap.new(20, 15)
ParameterTypeDescription
widthintegerNumber of columns in the grid
heightintegerNumber of rows in the grid

load

The GridMap.load(file) function loads a map from a .gmap file.

var map = GridMap.load("level1.gmap")
ParameterTypeDescription
filestringPath to the .gmap file

Return

Returns a GridMap object with the data loaded from the file, including dimensions and tile size.


Properties

Properties are accessed and assigned directly as fields of the object.

x / y

Position of the map in screen space. Useful when rendering with draw.gridBitmap or handling collisions with an offset.

map.x = 32
map.y = 16
println(map.x) // 32.0
PropertyTypeDescription
xfloatHorizontal position of the map
yfloatVertical position of the map

wrapX / wrapY

Enable toroidal movement (map edges connect). When active, pathfinding and collision operations take them into account automatically.

map.wrapX = true   // Left and right edges connect
map.wrapY = false
println(map.wrapX) // true
PropertyTypeDescription
wrapXbooleanConnects the right edge to the left edge
wrapYbooleanConnects the bottom edge to the top edge

solids

List of cell values considered solid (obstacles). The path, dMap, nearest, and hits functions use this list automatically if no additional parameters are passed.

map.solids = [1, 2, 3]   // Tiles 1, 2, and 3 are solid
println(map.solids) // [1, 2, 3]
map.solids = nil // No solids defined
PropertyTypeDescription
solidslist | nilList of solid cell values

platforms

List of cell values that act as one-way platforms (only block from above). Used by hits.

map.platforms = [4]
PropertyTypeDescription
platformslist | nilList of cell values for one-way platforms

tileset

Bitmap linked to the map, used by draw.gridMap to render tiles automatically.

var ts = Bitmap.load("tileset.bmp")
map.tileset = ts
println(map.tileset) // <bitmap>
map.tileset = nil // Unlink
PropertyTypeDescription
tilesetBitmap | nilTileset associated with the map

bouncy / elasticity

Control bounce behavior when resolving sprite collisions via hits.

map.bouncy = true
map.elasticity = 0.8
PropertyTypeDescription
bouncybooleanEnables bouncing on collision
elasticityfloatBounce factor (0.0 = no bounce, 1.0 = full bounce)

Instance Methods

set

The set(x, y, value) method sets the value of a specific cell.

map.set(5, 3, 1)   // Wall
map.set(0, 0, 0) // Empty floor
map.set(2, 0, 5) // Point of interest
ParameterTypeDescription
xintegerX coordinate (column)
yintegerY coordinate (row)
valueintegerValue to assign (0-255)

get

The get(x, y) method gets the value of a specific cell.

var value = map.get(5, 3)
println("Value at (5,3): ", value)
ParameterTypeDescription
xintegerX coordinate (column)
yintegerY coordinate (row)

Return

Returns the integer value (0-255) stored in the cell. Throws an exception if the coordinates are out of range.

width / height

The width() and height() methods return the dimensions of the grid.

println("Width:  ", map.width())
println("Height: ", map.height())

clear

The clear(value) method sets all cells to the specified value.

map.clear(0)   // Clear the entire map
map.clear(1) // Fill everything with walls
ParameterTypeDescription
valueintegerValue to assign to all cells (0-255)

clone

The clone() method creates a deep copy of the grid.

var copy = map.clone()
copy.set(0, 0, 99)
println(map.get(0, 0)) // Original value unchanged
println(copy.get(0, 0)) // 99

Return

Returns a new GridMap object with the same values as the original.

tSize

The tSize() / tSize(width, height) method acts as a getter/setter for the pixel size of each tile. This value is stored in the .gmap file and is used by the rendering system.

map.tSize(16, 16)          // Set 16x16 px tiles
var size = map.tSize() // Get -> [16, 16]
println(size[0], "x", size[1])
UsageParametersReturn
Getternone[tileWidth, tileHeight] (list)
Setterwidth:integer, height:integer (1-255)nil

tEmpty

The tEmpty() / tEmpty(value) method acts as a getter/setter for the empty tile index. Cells with that value are not drawn when rendering the map.

map.tEmpty(0)          // Tile 0 is the "empty" tile (default: 255)
println(map.tEmpty()) // 0
UsageParametersReturn
Getternoneinteger (0-255)
Settervalue:integernil

Query Methods

count

The count(value) method counts how many cells have a specific value.

var numWalls = map.count(1)
println("Walls: ", numWalls)
ParameterTypeDescription
valueintegerCell value to search for (0-255)

Return

Returns an integer with the number of cells that have that value.

has

The has(value) method checks whether at least one cell with the given value exists.

if (map.has(5)) then
println("There are still points on the map")
end
ParameterTypeDescription
valueintegerCell value to search for (0-255)

Return

Returns true if at least one cell with that value exists, false otherwise.

find

The find(value) method searches for all cells with a specific value and returns their coordinates.

var positions = map.find(5)   // All cells with value 5
for (var i = 0; i < positions.size(); i += 2)
var x = positions[i]
var y = positions[i + 1]
println("Point at (", x, ", ", y, ")")
end
ParameterTypeDescription
valueintegerCell value to search for (0-255)

Return

Returns a flat list [x1, y1, x2, y2, ...] with the coordinates of all matches (in row-by-row order). Empty list if none found.

around

The around(x, y [, use8 [, allowWrap]]) method gets the values of the cells neighboring a position.

var neighbors4 = map.around(5, 3)              // 4 cardinal neighbors
var neighbors8 = map.around(5, 3, true) // 8 neighbors (+ diagonals)
var neighborsW = map.around(0, 0, false, true) // 4 neighbors with toroidal wrap
ParameterTypeDescription
xintegerX coordinate of the center cell
yintegerY coordinate of the center cell
use8boolean(Optional) If true, includes the 4 diagonals. Default: false
allowWrapboolean(Optional) If true, edges connect. Default: false

Return

Returns a list with the values of the valid neighboring cells. Without wrap, cells outside the bounds are ignored (the list may have fewer than 4/8 elements).

see

The see(x1, y1, x2, y2, blockers) method checks whether there is a clear line of sight between two cells using Bresenham's algorithm.

// Can the player see the ghost? Walls (value 1) block the view
if (map.see(playerX, playerY, ghostX, ghostY, 1)) then
println("The ghost can see you")
end

// Multiple blocking values
if (map.see(playerX, playerY, ghostX, ghostY, [1, 2, 3])) then
println("Line of sight is clear")
end
ParameterTypeDescription
x1, y1integerOrigin coordinates
x2, y2integerDestination coordinates
blockersinteger | list | arrayCell values that block the line of sight

Return

Returns true if the path between origin and destination does not pass through any blocking cell, false otherwise.


Pathfinding Methods

Pathfinding methods automatically derive walkable cells from the map's solids property (walkable = NOT solid). Toroidal wrap is taken from the wrapX and wrapY properties. If different behavior is needed, dMap and nearest accept optional parameters to manually specify walkable cells and wrap.

path

The path(sx, sy, gx, gy) method finds the shortest path between two points using BFS.

// Define which tiles are solid (obstacles)
map.solids = [1]

var route = map.path(0, 0, 10, 10)

if (route != nil) then
println("Path with ", route.size() / 2, " steps")
for (var i = 0; i < route.size(); i += 2)
println(" -> (", route[i], ", ", route[i+1], ")")
end
else
println("No path available")
end
ParameterTypeDescription
sxintegerStarting X coordinate
syintegerStarting Y coordinate
gxintegerGoal X coordinate
gyintegerGoal Y coordinate

Return

Returns a flat list [x0, y0, x1, y1, ..., gx, gy] with the path coordinates, or nil if no path exists or if the start/goal are not walkable.

Walkable cells

Walkable cells are automatically calculated as the complement of map.solids. Make sure to assign map.solids before calling path. Wrap is taken from map.wrapX and map.wrapY.

dMap

The dMap(targets [, walkable [, allowWrap]]) method builds a BFS distance map from all cells that have the target values.

// Distance map to cell type 5 (dots/pellets)
map.solids = [1]
var distances = map.dMap(5)

// Query distance from any position
var dist = distances.get(playerX, playerY)
println("Distance to nearest dot: ", dist)

// With multiple target types
var distances2 = map.dMap([5, 6])

// With manual walkable list and wrap
var distances3 = map.dMap(5, [0, 5, 6], true)
ParameterTypeDescription
targetsinteger | list | arrayCell value(s) from which distance is measured
walkablelist | array(Optional) Cell values that can be traversed
allowWrapboolean(Optional) Enables toroidal movement

Return

Returns a new GridMap where each cell contains the minimum distance to the nearest target. Unreachable cells have value 255.

Automatic mode

With a single argument, dMap derives walkable cells from map.solids (walkable = NOT solid) and wrap from map.wrapX/map.wrapY.

nearest

The nearest(targets, sx, sy [, walkable [, allowWrap]]) method finds the shortest path from a position to the nearest cell that has one of the target values.

// Player position: which dot is closest?
map.solids = [1]
var route = map.nearest(5, playerX, playerY)

if (route != nil) then
// The last pair of values is the position of the found target
var n = route.size()
var tx = route[n - 2]
var ty = route[n - 1]
println("Nearest dot at (", tx, ", ", ty, ")")
println("Steps: ", n / 2)
else
println("No reachable dots")
end
ParameterTypeDescription
targetsinteger | list | arrayCell value(s) to search for
sxintegerStarting X coordinate
syintegerStarting Y coordinate
walkablelist | array(Optional) Walkable cell values
allowWrapboolean(Optional) Enables toroidal movement

Return

Returns a flat list [sx, sy, ..., tx, ty] with the path from origin to the nearest target cell, or nil if no target is reachable.

dir

The dir(x, y [, use8]) method gets the best direction to follow from a position in a distance map (result of dMap). This allows moving an agent toward the nearest target without recalculating the full path every frame.

map.solids = [1]
var distances = map.dMap(5) // Distance map to the dots

// Ghost AI: move toward the nearest dot
var d = distances.dir(ghostX, ghostY)
if (d == 1) then ghostY -= 1 // Up
elsif (d == 2) then ghostY += 1 // Down
elsif (d == 3) then ghostX -= 1 // Left
elsif (d == 4) then ghostX += 1 // Right
end
ParameterTypeDescription
xintegerCurrent X coordinate
yintegerCurrent Y coordinate
use8boolean(Optional) If true, also considers diagonals. Default: false

Return

Returns an integer indicating the optimal direction:

ValueDirection
0No movement (already at goal or unreachable cell)
1Up (y-1)
2Down (y+1)
3Left (x-1)
4Right (x+1)
5Up-Left (x-1, y-1) — 8-direction mode only
6Up-Right (x+1, y-1) — 8-direction mode only
7Down-Left (x-1, y+1) — 8-direction mode only
8Down-Right (x+1, y+1) — 8-direction mode only

Collisions

hits

The hits(sprite_or_list) method resolves physical collision between one or more sprites and the map. It reads the solids and platforms properties directly from the GridMap to determine which tiles block and which are one-way platforms.

map.solids = [1, 2]
map.platforms = [3]

// Collision with a single sprite
var tilesHit = map.hits(player)

// Collision with multiple sprites at once
var all = [player, enemy1, enemy2]
var hit = map.hits(all)
ParameterTypeDescription
sprite_or_listSprite | listIndividual sprite or list of sprites to resolve

Return

Returns a list with the values of the tiles that were involved in a collision.


Important Concepts

Cell Values

Each cell stores a value from 0 to 255 that can represent:

ValueTypical Use
0Empty floor / walkable
1Wall / obstacle
2-254Different terrain types or states
255Reserved for "unreachable" in distance maps

Toroidal Movement (Wrap)

When wrapX or wrapY are true, grid edges connect:

  • wrapX: moving right from the last column leads to the first column.
  • wrapY: moving down from the last row leads to the first row.

This is useful for Pac-Man style games where characters can traverse map edges.

map.wrapX = true
map.wrapY = true
var route = map.path(0, 5, 19, 5) // Can go around the edges

Algorithmic Complexity

All pathfinding operations use BFS with O(W×H) complexity, where W is the width and H is the height of the grid.


Example: Finding the Path Between Two Points

use GridMap

var map = GridMap.new(8, 5)
map.solids = [1]

// Build a small map with a corridor in the middle
// . . . . . . . .
// . # # # # # . .
// . . . . . . . .
// . . # # # # # .
// . . . . . . . .
for (var x = 1; x <= 5; x++) map.set(x, 1, 1) end
for (var x = 2; x <= 6; x++) map.set(x, 3, 1) end

var route = map.path(0, 0, 7, 4)

if (route != nil) then
for (var i = 0; i < route.size(); i += 2)
println("(", route[i], ", ", route[i+1], ")")
end
else
println("No path")
end

Example: Enemy Chasing the Player with dMap + dir

use GridMap

var map = GridMap.new(10, 6)
map.solids = [1]

// A few walls
map.set(3, 0, 1) map.set(3, 1, 1) map.set(3, 2, 1)
map.set(6, 3, 1) map.set(6, 4, 1) map.set(6, 5, 1)

var playerX = 0 var playerY = 0
var enemyX = 9 var enemyY = 5

// Build the dMap centered on the player (value 0 = goal)
// Mark the player's cell as type 9 temporarily
map.set(playerX, playerY, 9)
var dist = map.dMap(9)
map.set(playerX, playerY, 0) // restore

// The enemy queries the optimal direction
var d = dist.dir(enemyX, enemyY)

// Movement table
var ddx = [0, 0, 0, -1, 1, -1, 1, -1, 1]
var ddy = [0, -1, 1, 0, 0, -1, -1, 1, 1]

enemyX = enemyX + ddx[d]
enemyY = enemyY + ddy[d]

println("Enemy moves to (", enemyX, ", ", enemyY, ")")

Usage with the Draw Module

The Draw module includes the gridBitmap function for efficiently rendering grids:

use Display, GridMap

var draw = Display.draw

const COLS = 12
const ROWS = 8
const CELL_SIZE = 16

// 0 = empty
// 1 = small dot
// 2 = large dot
var grid = GridMap.new(COLS, ROWS)

var imgPoint = Bitmap.load("Images/dot.bmp")

func InitGrid()
for (var y = 0; y < ROWS; y++)
for (var x = 0; x < COLS; x++)
// Fill almost the entire grid with small dots
grid.set(x, y, 1)
end
end

// Leave some gaps
grid.set(3, 2, 0)
grid.set(4, 2, 0)
grid.set(5, 2, 0)
grid.set(6, 2, 0)

// Four large dots, Pac-Man style
grid.set(1, 1, 2)
grid.set(COLS - 2, 1, 2)
grid.set(1, ROWS - 2, 2)
grid.set(COLS - 2, ROWS - 2, 2)
end

func DrawGridEfficient()
const X0 = 20
const Y0 = 20

// Efficient render:
// draws all cells with value 1 at once
draw.gridBitmap(grid, 1, imgPoint, X0, Y0, CELL_SIZE, CELL_SIZE)

// Special elements are drawn separately
draw.color = 0x0000FF

if (grid.get(1, 1) == 2) then
draw.ellipse(X0 + 1 * CELL_SIZE, Y0 + 1 * CELL_SIZE, 4, 4, true)
end

if (grid.get(COLS - 2, 1) == 2) then
draw.ellipse(X0 + (COLS - 2) * CELL_SIZE, Y0 + 1 * CELL_SIZE, 4, 4, true)
end

if (grid.get(1, ROWS - 2) == 2) then
draw.ellipse(X0 + 1 * CELL_SIZE, Y0 + (ROWS - 2) * CELL_SIZE, 4, 4, true)
end

if (grid.get(COLS - 2, ROWS - 2) == 2) then
draw.ellipse(X0 + (COLS - 2) * CELL_SIZE, Y0 + (ROWS - 2) * CELL_SIZE, 4, 4, true)
end
end

func main()
Display.open(320, 240)
Display.autoClear = true
Display.showBG = false

InitGrid()

while (true)
DrawGridEfficient()
Display.update()
pause(16)
end
end

main()

Performance Considerations

Memory and Performance
  • A 100×100 grid uses approximately 10 KB.
  • Pathfinding operations are O(W×H); consider map size for real-time applications.
  • Use dMap + dir when multiple agents need to navigate toward the same targets: calculate dMap once and call dir once per agent each frame.

Supported Platforms

PlatformSupportNotes
WindowsFull support
ESP32Full support
Emscripten (Web)Full support