Saltar al contenido principal

Módulo GridMap

Un GridMap es una rejilla de celdas en la que cada celda guarda un número de 0 a 255. Es el mapa de un juego: qué tile se pinta en cada celda (con draw.tilemap), qué celdas son pared para los sprites, por dónde se puede ir de un sitio a otro y dónde está cada cosa.

Captura del módulo GridMap de JARU para crear mapas de tiles, niveles y rutas en juegos

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.

use GridMap

Cómo es un GridMap​

Celdas y valores​

Cada celda se nombra por su columna x y su fila y, contadas desde 0: en un mapa de 20 × 15, x va de 0 a 19 e y de 0 a 14. Cada celda guarda un valor de 0 a 255, que es lo que tú decidas:

ValorUso típico
0Suelo vacío / transitable
1Pared / obstáculo
2-254Diferentes tipos de terreno o estados
255En un mapa de tiles, celda vacía que no se pinta; en un mapa de distancias de dMap, inalcanzable

Para pintar el mapa y para que choque con los sprites, el mapa necesita además el tamaño en píxeles de su tile (tSize) y su posición en la pantalla (x, y). Un mapa que solo usas para buscar caminos no los necesita.

Sólidos y plataformas​

Las listas solids y platforms dicen qué valores de celda son pared y cuáles son plataformas que solo frenan desde arriba. Las usan:

  • las colisiones con sprites: hits y sprite.hitsGrid;
  • la búsqueda de caminos: path, dMap y nearest pasan por las celdas que no están en solids.

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 de laberinto donde el personaje puede atravesar los bordes del mapa. Lo tienen en cuenta get, las colisiones, draw.tilemap (que repite el mapa) y la búsqueda de caminos.

mapa.wrapX = true
mapa.wrapY = true
var camino = mapa.path(0, 5, 19, 5) // Puede rodear por los bordes
Wrap en los dos ejes

path, dMap y nearest no distinguen los ejes: en cuanto wrapX o wrapY vale true, conectan los bordes en horizontal y en vertical. El parámetro permitirWrap de dMap y nearest hace lo mismo. Si solo quieres un eje, cierra el otro con una fila o una columna de paredes.

Coste​

  • Una cuadrícula ocupa un byte por celda: una de 100 × 100, unos 10 KB.
  • path, dMap y nearest recorren el mapa entero (búsqueda en anchura, coste O(ancho × alto)); ten en cuenta el tamaño del mapa en un juego que las llama a menudo.
  • Para que varios enemigos vayan hacia lo mismo, calcula dMap una vez y llama a dir una vez por enemigo en cada fotograma.

Funciones​

FunciónQué haceDevuelve
GridMap.new(ancho, alto)Crea un mapa con todas las celdas a 0Un GridMap
GridMap.load(fichero)Carga un mapa de un fichero .gmapUn GridMap

Los métodos y las funciones de GridMap lanzan una excepción cuando algo falla (un argumento de otro tipo, una celda fuera del mapa…), que se captura con try (ver Errores y excepciones).

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ámetroTipoDescripción
anchoenteroNúmero de columnas de la cuadrícula
altoenteroNúmero de filas de la cuadrícula

El mapa nuevo no tiene tamaño de tile (tSize() es [0, 0]), ni tileset, ni solids. Con un ancho o un alto de 0 lanza ERR_INVALID_VALUE; con decimales, ERR_TYPE_MISMATCH.

load​

La función GridMap.load(archivo) carga un mapa desde un fichero .gmap.

var mapa = GridMap.load("Maps/Nivel1_cells.gmap")
ParámetroTipoDescripción
archivocadenaRuta al fichero .gmap

Devuelve un GridMap con las celdas del fichero y su tamaño de tile. No trae ni el tileset, ni solids, ni platforms: pónselos tú.

El IDE crea los .gmap al compilar, a partir de los mapas del editor de mapas: Maps/<mapa>_tiles.gmap con los tiles y Maps/<mapa>_cells.gmap con los valores de celda. Qué lleva cada uno y cómo es el fichero lo cuenta Lo que guarda y lo que genera.

ErrorCuándo
ERR_FILE_NOT_FOUNDEl fichero no está.
ERR_INVALID_FORMATNo es un .gmap, es de otra versión o le faltan celdas.
ERR_INVALID_VALUEEl mapa del fichero mide 0 de ancho o de alto.
ERR_TYPE_MISMATCHLa ruta no es una cadena.

Propiedades​

Las propiedades se leen y se asignan directamente, como campos del objeto.

PropiedadTipoDe seriePara qué
x, ydecimal0Dónde va la celda (0, 0), en píxeles
wrapX, wrapYsí/nofalseConectar los bordes
solidslista o nilnilLos valores de pared
platformslista o nilnilLos valores de plataforma
tilesetBitmap o nilnilLa imagen de los tiles
bouncysí/nofalseRebotar contra el mapa
elasticitydecimal, de 0 a 10Cuánto rebota

x / y​

Posición del mapa en el espacio de pantalla, en píxeles: dónde va la esquina de arriba a la izquierda de la celda (0, 0). La tienen en cuenta draw.tilemap, draw.scene y las colisiones con el mapa; draw.gridBitmap no la usa, porque recibe su propia posición.

mapa.x = 32
mapa.y = 16
println(mapa.x) // 32
PropiedadTipoDescripción
xdecimalPosición horizontal del mapa
ydecimalPosición vertical del mapa

Para hacer scroll sin escena, cámbialas: con valores negativos, el mapa se va hacia la izquierda y hacia arriba. Asignarles algo que no es un número lanza ERR_INVALID_VALUE.

wrapX / wrapY​

Activan el movimiento toroidal (los bordes del mapa se conectan). Cuando están activos, la búsqueda de caminos y las colisiones los tienen en cuenta automáticamente (ver Movimiento toroidal).

mapa.wrapX = true   // Los bordes izquierdo y derecho se conectan
mapa.wrapY = false
println(mapa.wrapX) // true
PropiedadTipoDescripción
wrapXsí/noConecta el borde derecho con el izquierdo
wrapYsí/noConecta 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
PropiedadTipoDescripción
solidslista | nilLista de valores de celda sólidos

El mapa guarda la misma lista que le das, no una copia: si después la cambias, cambian los sólidos. Con algo que no es una lista ni nil (un array, por ejemplo), lanza ERR_TYPE_MISMATCH.

platforms​

Lista de valores de celda que actúan como plataformas unidireccionales (solo bloquean desde arriba). Usada por hits y por sprite.hitsGrid.

mapa.platforms = [4]
PropiedadTipoDescripción
platformslista | nilLista de valores de celda para plataformas de un solo sentido

tileset​

Bitmap vinculado al mapa, usado por draw.tilemap para renderizar automáticamente los tiles, y por draw.scene para el mapa de la escena.

var ts = Bitmap.load("Images/tileset.bmp")
mapa.tileset = ts
println(mapa.tileset) // <Obj Bitmap>
mapa.tileset = nil // Desvincular
PropiedadTipoDescripción
tilesetBitmap | nilTileset asociado al mapa

bouncy / elasticity​

Controlan el rebote de los sprites al resolver sus colisiones con el mapa mediante hits.

mapa.bouncy = true
mapa.elasticity = 0.8
PropiedadTipoDescripción
bouncysí/noActiva el rebote al colisionar
elasticitydecimalFactor de rebote (0.0 = sin rebote, 1.0 = rebote total)

Con bouncy = false, el sprite no rebota: se para contra la pared. Con true, el rebote combina la elasticity del mapa y la del sprite. Un valor fuera de 0–1 se queda en el límite.

Métodos del mapa​

MétodoQué haceDevuelve
set(x, y, valor)Cambia el valor de una celdanil
get(x, y)Lee el valor de una celdaUn entero
width(), height()Las medidas en celdasUn entero
clear(valor)Pone todas las celdas a un valornil
clone()Hace una copiaUn GridMap
tSize() / tSize(ancho, alto)Lee o cambia el tamaño del tile[ancho, alto] / nil
tEmpty() / tEmpty(valor)Lee o cambia el tile que no se pintaUn entero / true
count(valor)Cuántas celdas tienen un valorUn entero
has(valor)Si alguna celda tiene un valortrue o false
find(valor)Dónde están las celdas con un valorUna lista [x, y, …]
around(x, y [, usar8 [, permitirWrap]])Los valores de las celdas vecinasUna lista
see(x1, y1, x2, y2, bloqueantes)Si hay línea de visión entre dos celdastrue o false
path(sx, sy, gx, gy)El camino más corto entre dos celdasUna lista [x, y, …], o nil
dMap(objetivos [, transitables [, permitirWrap]])Un mapa de distancias a unas celdasUn GridMap
nearest(objetivos, sx, sy [, transitables [, permitirWrap]])El camino a la celda objetivo más cercanaUna lista [x, y, …], o nil
dir(x, y [, usar8])En un mapa de distancias, hacia dónde irUn entero de 0 a 8
hits(sprite)Saca de las paredes a uno o varios spritesUna lista de valores

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ámetroTipoDescripción
xenteroCoordenada X (columna)
yenteroCoordenada Y (fila)
valorenteroValor a asignar (0-255)
ErrorCuándo
ERR_INVALID_VALUEvalor no está entre 0 y 255.
ERR_INDEX_OUT_OF_RANGELa celda está fuera del mapa. set no usa el wrap.
ERR_TYPE_MISMATCHAlgún argumento no es un entero (también los decimales).

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ámetroTipoDescripción
xnúmeroCoordenada X (columna)
ynúmeroCoordenada Y (fila)

Devuelve el valor entero (0-255) almacenado en la celda.

  • Acepta decimales, y los redondea hacia abajo: get(1.9, 0.2) es la celda (1, 0), y -0.5 es la -1.
  • Con wrapX o wrapY, una coordenada fuera del mapa da la vuelta en ese eje: en un mapa de 4 de ancho con wrapX, get(5, 0) es get(1, 0).
  • Fuera del mapa, sin wrap en ese eje, lanza ERR_INDEX_OUT_OF_RANGE.

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ámetroTipoDescripción
valorenteroValor a asignar a todas las celdas (0-255)

Un valor fuera de 0–255 lanza ERR_INVALID_VALUE.

En Windows, draw.tilemap no ve el clear

En Windows, después de clear, draw.tilemap sigue pintando el mapa de antes hasta que cambias una celda con set. Haz un set de cualquier celda después del clear, por ejemplo mapa.set(0, 0, mapa.get(0, 0)).

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

Devuelve un nuevo GridMap con los mismos valores que el original, y también su posición, su tamaño de tile, el wrap, el rebote, el tile vacío y su tileset (la misma imagen). solids y platforms se copian: cambiar las de la copia no toca las del original.

tSize​

El método tSize() / tSize(ancho, alto) lee o cambia el tamaño en píxeles de cada tile. Lo usan draw.tilemap, draw.scene, hits y sprite.hitsGrid: sin él, no pintan ni chocan.

mapa.tSize(16, 16)           // Establecer tiles de 16x16 px
var tam = mapa.tSize() // Obtener -> [16, 16]
println(tam[0], "x", tam[1])
UsoParámetrosRetorno
Leerninguno[tileAncho, tileAlto] (lista)
Cambiarancho:entero, alto:entero (1-255)nil

Un mapa nuevo tiene [0, 0], y uno cargado de un .gmap, el del fichero. Fuera de 1–255 lanza ERR_INVALID_VALUE.

tEmpty​

El método tEmpty() / tEmpty(valor) lee o cambia el índice del 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
UsoParámetrosRetorno
Leerningunoentero (0-255)
Cambiarvalor:enterotrue

Las celdas con 255 no se pintan nunca, sea cual sea tEmpty.

count​

El método count(valor) cuenta cuántas celdas tienen un valor específico.

var numParedes = mapa.count(1)
println("Paredes: ", numParedes)
ParámetroTipoDescripción
valorenteroValor de celda a buscar (0-255)

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ámetroTipoDescripción
valorenteroValor de celda a buscar (0-255)

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ámetroTipoDescripción
valorenteroValor de celda a buscar (0-255)

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. Sirve, por ejemplo, para poner los enemigos donde el editor de mapas marcó sus celdas.

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ámetroTipoDescripción
xenteroCoordenada X de la celda central
yenteroCoordenada Y de la celda central
usar8sí/no(Opcional) Si true, incluye las 4 diagonales. Por defecto: false
permitirWrapsí/no(Opcional) Si true, los bordes se conectan. Por defecto: false

Devuelve una lista con los valores de las celdas vecinas válidas, en este orden: arriba, abajo, izquierda, derecha y, con usar8, arriba a la izquierda, arriba a la derecha, abajo a la izquierda y abajo a la derecha. Sin wrap, las celdas fuera de los límites se saltan (la lista puede tener menos de 4/8 elementos), así que en un borde la posición en la lista no dice qué vecino es. Usa el argumento permitirWrap, no wrapX ni wrapY.

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ámetroTipoDescripción
x1, y1enteroCoordenadas de origen
x2, y2enteroCoordenadas de destino
bloqueantesentero | lista | arrayValores de celda que bloquean la visión

Devuelve true si el trayecto entre origen y destino no pasa por ninguna celda bloqueante, false en caso contrario. Las celdas de origen y de destino no cuentan: aunque sean pared, no tapan. No usa el wrap.

path​

El método path(sx, sy, gx, gy) encuentra el camino más corto entre dos puntos, moviéndose en las cuatro direcciones (BFS).

Los métodos de búsqueda de caminos 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.

use GridMap

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

// Un pequeño mapa con dos paredes
// . . . . . . . .
// . # # # # # . .
// . . . . . . . .
// . . # # # # # .
// . . . . . . . .
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
println(len(camino) div 2, " celdas")
println(camino)
else
println("Sin camino")
end
12 celdas
[0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 7, 1, 7, 2, 7, 3, 7, 4]
ParámetroTipoDescripción
sxenteroCoordenada X de inicio
syenteroCoordenada Y de inicio
gxenteroCoordenada X de destino
gyenteroCoordenada Y de destino

Devuelve una lista plana [x0, y0, x1, y1, ..., gx, gy] con las coordenadas del camino, empezando por el origen, o nil si no existe camino posible o si el origen/destino no son transitables. Si el origen ya es el destino, el camino es esa casilla: [x, y]. Con un origen o un destino fuera del mapa lanza ERR_INDEX_OUT_OF_RANGE, y con decimales, ERR_TYPE_MISMATCH.

Celdas 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(objetivos [, 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ámetroTipoDescripción
objetivosentero | lista | arrayValor(es) de celda desde los que se mide la distancia
transitablesentero | lista | array(Opcional) Valores de celda por los que se puede pasar
permitirWrapsí/no(Opcional) Permite movimiento toroidal

Devuelve un nuevo GridMap donde cada celda contiene la distancia mínima al objetivo más cercano, en pasos de una celda. Las celdas objetivo valen 0 y las inalcanzables, 255. Las distancias de 254 o más se quedan en 254. El mapa nuevo es solo de distancias: no tiene tamaño de tile, tileset ni solids.

Modo automático

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(objetivos, 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 div 2 - 1)
else
println("No hay puntos alcanzables")
end
ParámetroTipoDescripción
objetivosentero | lista | arrayValor(es) de celda que se buscan
sxenteroCoordenada X de inicio
syenteroCoordenada Y de inicio
transitablesentero | lista | array(Opcional) Valores de celda transitables
permitirWrapsí/no(Opcional) Permite movimiento toroidal

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 (también si el origen es una pared). Si el origen ya es un objetivo, devuelve [sx, sy].

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 en cada fotograma.

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ámetroTipoDescripción
xenteroCoordenada X actual
yenteroCoordenada Y actual
usar8sí/no(Opcional) Si true, considera también las diagonales. Por defecto: false

Devuelve un entero indicando la dirección óptima:

ValorDirección
0Sin movimiento (ya en objetivo o celda inalcanzable)
1Arriba (y-1)
2Abajo (y+1)
3Izquierda (x-1)
4Derecha (x+1)
5Arriba-izquierda (x-1, y-1) — solo con 8 direcciones
6Arriba-derecha (x+1, y-1) — solo con 8 direcciones
7Abajo-izquierda (x-1, y+1) — solo con 8 direcciones
8Abajo-derecha (x+1, y+1) — solo con 8 direcciones

Elige el vecino con menos distancia; si empatan, el primero en ese orden. No usa el wrap: en el borde del mapa no da la vuelta. Con 8 direcciones solo mira la distancia de la celda en diagonal, no si las dos de al lado son pared: puede cortar una esquina.

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 de un solo sentido.

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ámetroTipoDescripción
sprite_o_listaSprite | listaSprite individual o lista de sprites a resolver

Devuelve una lista con los valores de los tiles con los que ha habido colisión, vacía si no hay ninguna.

A diferencia de sprite.hitsGrid, que solo pregunta, hits mueve los sprites: saca de las paredes a los que se han metido y les quita la velocidad contra la pared (o los hace rebotar, con bouncy). Para eso:

  • Solo trabaja con cuerpos DYNAMIC. Los sprites de otro tipo de la lista se saltan; por eso le puedes pasar la lista entera.
  • El mapa necesita su tamaño de tile (tSize). Sin él, no hace nada.
  • Usa el recorrido desde prevX, prevY: un sprite rápido no atraviesa una pared fina. En una escena, scene.update() los pone al día.
  • Llámalo en cada fotograma, después de scene.update(): la física de la escena mueve los cuerpos, pero no mira el mapa (lo cuenta Scene).
  • Pone onGround() a true en los sprites que el mapa sostiene (sprite.onGround).

Acepta un sprite o una lista; con un array u otra cosa, lanza ERR_TYPE_MISMATCH.

Ejemplo completo​

Un perseguidor (rojo) va hacia el jugador (verde) por un laberinto: cada seis fotogramas calcula con dMap las distancias hasta la celda del jugador y da un paso con dir. Las paredes se pintan con rectángulos, así que no necesita imágenes.

use Display, GridMap

const CELDA = 20

// El laberinto: 1 es pared y 0 es pasillo
var filas = [
"111111111111",
"100000010001",
"101111010101",
"100001000101",
"111101111101",
"100000000001",
"101111011101",
"100000010001",
"111111111111"
]
var mapa = GridMap.new(12, 9)
for (var y = 0; y < 9; y++)
for (var x = 0; x < 12; x++)
if (filas[y][x] == "1") then mapa.set(x, y, 1) end
end
end
mapa.solids = [1]

var jugX = 10 // el jugador, quieto
var jugY = 7
var perX = 1 // el perseguidor
var perY = 1
var ruta = mapa.path(perX, perY, jugX, jugY)
println("Camino más corto: ", len(ruta) div 2 - 1, " pasos") // 15 pasos

// dir() devuelve 0 (quieto), 1 arriba, 2 abajo, 3 izquierda o 4 derecha
var dx = [0, 0, 0, -1, 1]
var dy = [0, -1, 1, 0, 0]

Display.open(240, 180)
Display.fps = 30
var draw = Display.draw
for (var f = 0; f < 300; f++) // 10 segundos
if (f % 6 == 0) then // un paso cada 6 fotogramas
mapa.set(jugX, jugY, 9) // la celda del jugador, objetivo del dMap
var dist = mapa.dMap(9)
mapa.set(jugX, jugY, 0)
var d = dist.dir(perX, perY)
perX = perX + dx[d]
perY = perY + dy[d]
end
draw.color = 0x3050A0
for (var y = 0; y < 9; y++)
for (var x = 0; x < 12; x++)
if (mapa.get(x, y) == 1) then
draw.rect(x * CELDA, y * CELDA, CELDA, CELDA, true)
end
end
end
draw.color = 0x40FF40
draw.rect(jugX * CELDA + 4, jugY * CELDA + 4, 12, 12, true)
draw.color = 0xFF4040
draw.rect(perX * CELDA + 4, perY * CELDA + 4, 12, 12, true)
Display.update()
end
println("El perseguidor está en (", perX, ", ", perY, ")") // (10, 7)
Display.close()

Para pintar el mapa con imágenes en vez de rectángulos, usa draw.tilemap (un tile por celda, con un tileset) o draw.gridBitmap (una imagen en las celdas con un valor).

Trampas y problemas conocidos​

  • path, dMap y nearest conectan los dos ejes en cuanto uno tiene wrap (ver Movimiento toroidal).
  • En Windows, draw.tilemap no ve lo que cambia clear hasta el siguiente set (ver clear).
  • set no acepta decimales, y get sí. Si calculas la celda dividiendo píxeles entre el tamaño del tile, usa div para set (mapa.set(px div 16, py div 16, 1)).
  • solids y platforms guardan tu lista, no una copia: si cambias la lista después, cambian también en el mapa.
  • count, has y find no comprueban el valor: uno fuera de 0–255 da la vuelta en un byte (count(256) cuenta los 0).
  • hits solo mueve cuerpos DYNAMIC, y solo con el tamaño de tile puesto (ver hits).

Todos los problemas conocidos de JARU, juntos, están en Problemas conocidos.