Módulo GridMap
El módulo GridMap proporciona una estructura de datos de cuadrícula 2D optimizada para juegos y aplicaciones que requieren mapas de tiles, pathfinding y detección de colisiones basada en celdas. Cada celda almacena un valor entero de 0 a 255, permitiendo representar diferentes tipos de terreno, obstáculos o estados.

Este módulo es especialmente útil para desarrollar juegos tipo arcade, puzzles basados en cuadrículas, simulaciones de mapas y cualquier aplicación que necesite navegación inteligente entre puntos.
Uso
use GridMap
Constructor
new
La función GridMap.new(ancho, alto) crea una nueva cuadrícula con las dimensiones especificadas. Todas las celdas se inicializan a 0.
use GridMap
// Crear una cuadrícula de 20x15 celdas
var mapa = GridMap.new(20, 15)
| Parámetro | Tipo | Descripción |
|---|---|---|
ancho | integer | Número de columnas de la cuadrícula |
alto | integer | Número de filas de la cuadrícula |
load
La función GridMap.load(archivo) carga un mapa desde un fichero .gmap.
var mapa = GridMap.load("nivel1.gmap")
| Parámetro | Tipo | Descripción |
|---|---|---|
archivo | string | Ruta al fichero .gmap |
Retorno
Devuelve un objeto GridMap con los datos cargados del fichero, incluyendo dimensiones y tamaño de tile.
Propiedades
Las propiedades se acceden y asignan directamente como campos del objeto.
x / y
Posición del mapa en el espacio de pantalla. Útil cuando se renderiza con draw.gridBitmap o colisiones con offset.
mapa.x = 32
mapa.y = 16
println(mapa.x) // 32.0
| Propiedad | Tipo | Descripción |
|---|---|---|
x | float | Posición horizontal del mapa |
y | float | Posición vertical del mapa |
wrapX / wrapY
Activan el movimiento toroidal (los bordes del mapa se conectan). Cuando están activos, las operaciones de pathfinding y colisiones los tienen en cuenta automáticamente.
mapa.wrapX = true // Los bordes izquierdo y derecho se conectan
mapa.wrapY = false
println(mapa.wrapX) // true
| Propiedad | Tipo | Descripción |
|---|---|---|
wrapX | boolean | Conecta el borde derecho con el izquierdo |
wrapY | boolean | Conecta el borde inferior con el superior |
solids
Lista de valores de celda considerados sólidos (obstáculos). Las funciones path, dMap, nearest y hits usan esta lista automáticamente si no se les pasan parámetros adicionales.
mapa.solids = [1, 2, 3] // Los tiles 1, 2 y 3 son sólidos
println(mapa.solids) // [1, 2, 3]
mapa.solids = nil // Sin sólidos definidos
| Propiedad | Tipo | Descripción |
|---|---|---|
solids | list | nil | Lista de valores de celda sólidos |
platforms
Lista de valores de celda que actúan como plataformas unidireccionales (solo bloquean desde arriba). Usada por hits.
mapa.platforms = [4]
| Propiedad | Tipo | Descripción |
|---|---|---|
platforms | list | nil | Lista de valores de celda para plataformas one-way |
tileset
Bitmap vinculado al mapa, usado por draw.gridMap para renderizar automáticamente los tiles.
var ts = Bitmap.load("tileset.bmp")
mapa.tileset = ts
println(mapa.tileset) // <bitmap>
mapa.tileset = nil // Desvincular
| Propiedad | Tipo | Descripción |
|---|---|---|
tileset | Bitmap | nil | Tileset asociado al mapa |
bouncy / elasticity
Controlan el comportamiento de rebote al resolver colisiones con sprites mediante hits.
mapa.bouncy = true
mapa.elasticity = 0.8
| Propiedad | Tipo | Descripción |
|---|---|---|
bouncy | boolean | Activa el rebote al colisionar |
elasticity | float | Factor de rebote (0.0 = sin rebote, 1.0 = rebote total) |
Métodos de instancia
set
El método set(x, y, valor) establece el valor de una celda específica.
mapa.set(5, 3, 1) // Pared
mapa.set(0, 0, 0) // Suelo vacío
mapa.set(2, 0, 5) // Punto de interés
| Parámetro | Tipo | Descripción |
|---|---|---|
x | integer | Coordenada X (columna) |
y | integer | Coordenada Y (fila) |
valor | integer | Valor a asignar (0-255) |
get
El método get(x, y) obtiene el valor de una celda específica.
var valor = mapa.get(5, 3)
println("Valor en (5,3): ", valor)
| Parámetro | Tipo | Descripción |
|---|---|---|
x | integer | Coordenada X (columna) |
y | integer | Coordenada Y (fila) |
Retorno
Devuelve el valor entero (0-255) almacenado en la celda. Lanza excepción si las coordenadas están fuera de rango.
width / height
Los métodos width() y height() devuelven las dimensiones de la cuadrícula.
println("Ancho: ", mapa.width())
println("Alto: ", mapa.height())
clear
El método clear(valor) establece todas las celdas al valor especificado.
mapa.clear(0) // Limpiar todo el mapa
mapa.clear(1) // Llenar todo de paredes
| Parámetro | Tipo | Descripción |
|---|---|---|
valor | integer | Valor a asignar a todas las celdas (0-255) |
clone
El método clone() crea una copia profunda de la cuadrícula.
var copia = mapa.clone()
copia.set(0, 0, 99)
println(mapa.get(0, 0)) // Valor original inalterado
println(copia.get(0, 0)) // 99
Retorno
Devuelve un nuevo objeto GridMap con los mismos valores que el original.
tSize
El método tSize() / tSize(ancho, alto) actúa como getter/setter del tamaño en píxeles de cada tile. Este valor se almacena en el fichero .gmap y es usado por el sistema de renderizado.
mapa.tSize(16, 16) // Establecer tiles de 16x16 px
var tam = mapa.tSize() // Obtener -> [16, 16]
println(tam[0], "x", tam[1])
| Uso | Parámetros | Retorno |
|---|---|---|
| Getter | ninguno | [tileAncho, tileAlto] (list) |
| Setter | ancho:integer, alto:integer (1-255) | nil |
tEmpty
El método tEmpty() / tEmpty(valor) actúa como getter/setter del índice de tile vacío. Las celdas con ese valor no se dibujan al renderizar el mapa.
mapa.tEmpty(0) // El tile 0 es el "vacío" (por defecto: 255)
println(mapa.tEmpty()) // 0
| Uso | Parámetros | Retorno |
|---|---|---|
| Getter | ninguno | integer (0-255) |
| Setter | valor:integer | nil |
Métodos de consulta
count
El método count(valor) cuenta cuántas celdas tienen un valor específico.
var numParedes = mapa.count(1)
println("Paredes: ", numParedes)
| Parámetro | Tipo | Descripción |
|---|---|---|
valor | integer | Valor de celda a buscar (0-255) |
Retorno
Devuelve un entero con el número de celdas que tienen ese valor.
has
El método has(valor) comprueba si existe al menos una celda con el valor dado.
if (mapa.has(5)) then
println("Quedan puntos en el mapa")
end
| Parámetro | Tipo | Descripción |
|---|---|---|
valor | integer | Valor de celda a buscar (0-255) |
Retorno
Devuelve true si existe al menos una celda con ese valor, false en caso contrario.
find
El método find(valor) busca todas las celdas con un valor específico y devuelve sus coordenadas.
var posiciones = mapa.find(5) // Todas las celdas con valor 5
for (var i = 0; i < posiciones.size(); i += 2)
var x = posiciones[i]
var y = posiciones[i + 1]
println("Punto en (", x, ", ", y, ")")
end
| Parámetro | Tipo | Descripción |
|---|---|---|
valor | integer | Valor de celda a buscar (0-255) |
Retorno
Devuelve una lista plana [x1, y1, x2, y2, ...] con las coordenadas de todas las coincidencias (en orden fila por fila). Lista vacía si no hay ninguna.
around
El método around(x, y [, usar8 [, permitirWrap]]) obtiene los valores de las celdas vecinas a una posición.
var vecinos4 = mapa.around(5, 3) // 4 vecinos cardinales
var vecinos8 = mapa.around(5, 3, true) // 8 vecinos (+ diagonales)
var vecinosW = mapa.around(0, 0, false, true) // 4 vecinos con wrap toroidal
| Parámetro | Tipo | Descripción |
|---|---|---|
x | integer | Coordenada X de la celda central |
y | integer | Coordenada Y de la celda central |
usar8 | boolean | (Opcional) Si true, incluye las 4 diagonales. Por defecto: false |
permitirWrap | boolean | (Opcional) Si true, los bordes se conectan. Por defecto: false |
Retorno
Devuelve una lista con los valores de las celdas vecinas válidas. Sin wrap, las celdas fuera de los límites se ignoran (la lista puede tener menos de 4/8 elementos).
see
El método see(x1, y1, x2, y2, bloqueantes) comprueba si hay línea de visión libre entre dos celdas usando el algoritmo de Bresenham.
// ¿Puede el jugador ver al fantasma? Las paredes (valor 1) bloquean
if (mapa.see(jugX, jugY, fanX, fanY, 1)) then
println("El fantasma te ve")
end
// Múltiples valores bloqueantes
if (mapa.see(jugX, jugY, fanX, fanY, [1, 2, 3])) then
println("Línea de visión libre")
end
| Parámetro | Tipo | Descripción |
|---|---|---|
x1, y1 | integer | Coordenadas de origen |
x2, y2 | integer | Coordenadas de destino |
bloqueantes | integer | list | array | Valores de celda que bloquean la visión |
Retorno
Devuelve true si el trayecto entre origen y destino no pasa por ninguna celda bloqueante, false en caso contrario.
Métodos de pathfinding
Los métodos de pathfinding derivan automáticamente las celdas transitables a partir de la propiedad solids del mapa (transitable = NO sólido). El wrap toroidal se toma de las propiedades wrapX y wrapY. Si se necesita un comportamiento diferente, los métodos dMap y nearest admiten parámetros opcionales para especificar manualmente los transitables y el wrap.
path
El método path(sx, sy, gx, gy) encuentra el camino más corto entre dos puntos usando BFS.
// Definir qué tiles son sólidos (obstáculos)
mapa.solids = [1]
var camino = mapa.path(0, 0, 10, 10)
if (camino != nil) then
println("Camino con ", camino.size() / 2, " pasos")
for (var i = 0; i < camino.size(); i += 2)
println(" -> (", camino[i], ", ", camino[i+1], ")")
end
else
println("No hay camino posible")
end
| Parámetro | Tipo | Descripción |
|---|---|---|
sx | integer | Coordenada X de inicio |
sy | integer | Coordenada Y de inicio |
gx | integer | Coordenada X de destino |
gy | integer | Coordenada Y de destino |
Retorno
Devuelve una lista plana [x0, y0, x1, y1, ..., gx, gy] con las coordenadas del camino, o nil si no existe camino posible o si el origen/destino no son transitables.
Las celdas transitables se calculan automáticamente como el complemento de mapa.solids. Asegúrate de asignar mapa.solids antes de llamar a path. El wrap se toma de mapa.wrapX y mapa.wrapY.
dMap
El método dMap(targets [, transitables [, permitirWrap]]) construye un mapa de distancias BFS desde todas las celdas que tengan los valores objetivo.
// Mapa de distancias al tipo de celda 5 (puntos/píldoras)
mapa.solids = [1]
var distancias = mapa.dMap(5)
// Consultar distancia desde cualquier posición
var dist = distancias.get(jugadorX, jugadorY)
println("Distancia al punto más cercano: ", dist)
// Con múltiples tipos de objetivo
var distancias2 = mapa.dMap([5, 6])
// Con transitables y wrap manual
var distancias3 = mapa.dMap(5, [0, 5, 6], true)
| Parámetro | Tipo | Descripción |
|---|---|---|
targets | integer | list | array | Valor(es) de celda desde los que se mide la distancia |
transitables | list | array | (Opcional) Valores de celda por los que se puede pasar |
permitirWrap | boolean | (Opcional) Permite movimiento toroidal |
Retorno
Devuelve un nuevo GridMap donde cada celda contiene la distancia mínima al target más cercano. Las celdas inalcanzables tienen el valor 255.
Con un solo argumento, dMap deriva los transitables de mapa.solids (transitable = NO sólido) y el wrap de mapa.wrapX/mapa.wrapY.
nearest
El método nearest(targets, sx, sy [, transitables [, permitirWrap]]) encuentra el camino más corto desde una posición hasta la celda más cercana que tenga uno de los valores objetivo.
// Posición del jugador: ¿cuál es el punto más cercano?
mapa.solids = [1]
var camino = mapa.nearest(5, jugadorX, jugadorY)
if (camino != nil) then
// El último par de valores es la posición del objetivo encontrado
var n = camino.size()
var tx = camino[n - 2]
var ty = camino[n - 1]
println("Punto más cercano en (", tx, ", ", ty, ")")
println("Pasos: ", n / 2)
else
println("No hay puntos alcanzables")
end
| Parámetro | Tipo | Descripción |
|---|---|---|
targets | integer | list | array | Valor(es) de celda que se buscan |
sx | integer | Coordenada X de inicio |
sy | integer | Coordenada Y de inicio |
transitables | list | array | (Opcional) Valores de celda transitables |
permitirWrap | boolean | (Opcional) Permite movimiento toroidal |
Retorno
Devuelve una lista plana [sx, sy, ..., tx, ty] con el camino desde el origen hasta la celda objetivo más cercana, o nil si ningún objetivo es alcanzable.
dir
El método dir(x, y [, usar8]) obtiene la mejor dirección a seguir desde una posición en un mapa de distancias (resultado de dMap). Permite mover un agente hacia el objetivo más cercano sin recalcular el camino completo cada frame.
mapa.solids = [1]
var distancias = mapa.dMap(5) // Mapa de distancias a los puntos
// IA del fantasma: moverse hacia el punto más cercano
var dir = distancias.dir(fantasmaX, fantasmaY)
if (dir == 1) then fantasmaY -= 1 // Arriba
elsif (dir == 2) then fantasmaY += 1 // Abajo
elsif (dir == 3) then fantasmaX -= 1 // Izquierda
elsif (dir == 4) then fantasmaX += 1 // Derecha
end
| Parámetro | Tipo | Descripción |
|---|---|---|
x | integer | Coordenada X actual |
y | integer | Coordenada Y actual |
usar8 | boolean | (Opcional) Si true, considera también las diagonales. Por defecto: false |
Retorno
Devuelve un entero indicando la dirección óptima:
| Valor | Dirección |
|---|---|
0 | Sin movimiento (ya en objetivo o celda inalcanzable) |
1 | Arriba (y-1) |
2 | Abajo (y+1) |
3 | Izquierda (x-1) |
4 | Derecha (x+1) |
5 | Arriba-izquierda (x-1, y-1) — solo con 8 direcciones |
6 | Arriba-derecha (x+1, y-1) — solo con 8 direcciones |
7 | Abajo-izquierda (x-1, y+1) — solo con 8 direcciones |
8 | Abajo-derecha (x+1, y+1) — solo con 8 direcciones |
Colisiones
hits
El método hits(sprite_o_lista) resuelve la colisión física entre uno o varios sprites y el mapa. Lee las propiedades solids y platforms directamente del GridMap para determinar qué tiles bloquean y cuáles son plataformas one-way.
mapa.solids = [1, 2]
mapa.platforms = [3]
// Colisión con un sprite
var tilesGolpeados = mapa.hits(jugador)
// Colisión con varios sprites a la vez
var todos = [jugador, enemigo1, enemigo2]
var golpeados = mapa.hits(todos)
| Parámetro | Tipo | Descripción |
|---|---|---|
sprite_o_lista | Sprite | list | Sprite individual o lista de sprites a resolver |
Retorno
Devuelve una lista con los valores de los tiles con los que ha habido colisión.
Conceptos importantes
Valores de celda
Cada celda almacena un valor de 0 a 255 que puede representar:
| Valor | Uso típico |
|---|---|
0 | Suelo vacío / transitable |
1 | Pared / obstáculo |
2-254 | Diferentes tipos de terreno o estados |
255 | Reservado para "inalcanzable" en mapas de distancia |
Movimiento toroidal (Wrap)
Cuando wrapX o wrapY son true, los bordes de la cuadrícula se conectan:
wrapX: moverse a la derecha desde la última columna lleva a la primera columna.wrapY: moverse hacia abajo desde la última fila lleva a la primera fila.
Esto es útil para juegos estilo Pac-Man donde el personaje puede atravesar los bordes del mapa.
mapa.wrapX = true
mapa.wrapY = true
var camino = mapa.path(0, 5, 19, 5) // Puede rodear por los bordes
Complejidad algorítmica
Todas las operaciones de pathfinding usan BFS con complejidad O(W×H), donde W es el ancho y H es el alto de la cuadrícula.
Ejemplo encontrar el camino entre dos puntos
use GridMap
var mapa = GridMap.new(8, 5)
mapa.solids = [1]
// Construir un pequeño mapa con un pasillo en medio
// . . . . . . . .
// . # # # # # . .
// . . . . . . . .
// . . # # # # # .
// . . . . . . . .
for (var x = 1; x <= 5; x++) mapa.set(x, 1, 1) end
for (var x = 2; x <= 6; x++) mapa.set(x, 3, 1) end
var camino = mapa.path(0, 0, 7, 4)
if (camino != nil) then
for (var i = 0; i < camino.size(); i += 2)
println("(", camino[i], ", ", camino[i+1], ")")
end
else
println("Sin camino")
end
Ejemplo enemigo que persigue al jugador con dMap + dir
use GridMap
var mapa = GridMap.new(10, 6)
mapa.solids = [1]
// Unas pocas paredes
mapa.set(3, 0, 1) mapa.set(3, 1, 1) mapa.set(3, 2, 1)
mapa.set(6, 3, 1) mapa.set(6, 4, 1) mapa.set(6, 5, 1)
var jugX = 0 var jugY = 0
var enX = 9 var enY = 5
// Calculamos el dMap centrado en el jugador (valor 0 = objetivo)
// Para eso marcamos la celda del jugador como tipo 9 temporalmente
mapa.set(jugX, jugY, 9)
var dist = mapa.dMap(9)
mapa.set(jugX, jugY, 0) // restaurar
// El enemigo consulta la dirección óptima
var dir = dist.dir(enX, enY)
// Tabla de movimiento
var ddx = [0, 0, 0, -1, 1, -1, 1, -1, 1]
var ddy = [0, -1, 1, 0, 0, -1, -1, 1, 1]
enX = enX + ddx[dir]
enY = enY + ddy[dir]
println("Enemigo se mueve a (", enX, ", ", enY, ")")
Uso con el módulo Draw
El módulo Draw incluye la función gridBitmap para renderizar eficientemente cuadrículas:
use Display, GridMap
var draw = Display.draw
const COLS = 12
const ROWS = 8
const CELL_SIZE = 16
// 0 = vacío
// 1 = punto pequeño
// 2 = punto grande
var grid = GridMap.new(COLS, ROWS)
var imgPoint = Bitmap.load("Images/punto.bmp")
func InitGrid()
for (var y = 0; y < ROWS; y++)
for (var x = 0; x < COLS; x++)
// Rellenamos casi toda la cuadrícula con puntos pequeños
grid.set(x, y, 1)
end
end
// Dejamos algunos huecos
grid.set(3, 2, 0)
grid.set(4, 2, 0)
grid.set(5, 2, 0)
grid.set(6, 2, 0)
// Cuatro puntos grandes, como en Pac-Man
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
// Render eficiente:
// dibuja de una vez todas las celdas cuyo valor sea 1
draw.gridBitmap(grid, 1, imgPoint, X0, Y0, CELL_SIZE, CELL_SIZE)
// Los elementos especiales se dibujan aparte
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()
Consideraciones de rendimiento
- Una cuadrícula de 100×100 ocupa aproximadamente 10 KB.
- Las operaciones de pathfinding son O(W×H); considera el tamaño del mapa para aplicaciones en tiempo real.
- Usa
dMap+dircuando necesites que múltiples agentes naveguen hacia los mismos objetivos: calculadMapuna sola vez y llama adiruna vez por agente cada frame.
Plataformas soportadas
| Plataforma | Soporte | Notas |
|---|---|---|
| Windows | ✅ | Soporte completo |
| ESP32 | ✅ | Soporte completo |
| Emscripten (Web) | ✅ | Soporte completo |