Para construir mapas complejos en 2D basados en Sprite Sheets, lo primero que debemos hacer es crear una estructura lógica que represente cada tile del mapa. Esta estructura se puede organizar como un array multidimensional, donde la primera dimensión corresponde a la coordenada vertical (Y) y la segunda a la horizontal (X), ya que trabajamos en un espacio 2D. Así, cada posición del array almacenará la información necesaria para identificar qué tile se debe dibujar en esa ubicación.
En Java, podemos definir este array con valores literales en el momento de su declaración, lo que facilita la creación de un mapa inicial. Por ejemplo, podemos tener un array con cinco filas y seis columnas, donde cada elemento es un objeto que representa las coordenadas dentro del Sprite Sheet del tile correspondiente. En lugar de almacenar directamente imágenes, lo más práctico es guardar las coordenadas X e Y de cada tile dentro del Sprite Sheet, para luego extraer la imagen correspondiente cuando sea necesario.
Una forma sencilla de representar cada tile es mediante un array de dos enteros, donde el primer valor es la coordenada X y el segundo la coordenada Y dentro del Sprite Sheet. Esto evita la necesidad de crear clases adicionales o enumeraciones, simplificando el manejo cuando solo trabajamos con un mapa.
Por ejemplo, la estructura del mapa lógico podría definirse así:
private final int[][][] mapa = {
{ {9,7}, {9,7}, {9,7}, {9,7}, {9,7}, {9,7} },
{ {9,7}, {9,7}, {9,7}, {9,7}, {9,7}, {9,7} },
{ {9,7}, {9,7}, {9,7}, {9,7}, {9,7}, {9,7} },
{ {9,7}, {9,7}, {9,7}, {9,7}, {9,7}, {9,7} },
{ {9,7}, {9,7}, {9,7}, {9,7}, {9,7}, {9,7} }
};
Aquí, {9,7} representa un tile vacío, que no se dibuja en pantalla.
Una vez definido el mapa lógico, necesitamos transformarlo en un mapa de imágenes que podamos dibujar. Para ello, creamos otro array bidimensional de objetos Image con las mismas dimensiones que el mapa lógico. Luego, mediante un método que recorra cada posición del mapa lógico, extraemos el sprite correspondiente del Sprite Sheet usando las coordenadas almacenadas y lo asignamos a la posición equivalente en el mapa de imágenes.
Este proceso se puede implementar con dos bucles anidados que recorren filas y columnas, y para cada posición obtenemos las coordenadas del tile y pedimos al Sprite Sheet la imagen correspondiente:
for (int y = 0; y < mapa.length; y++) {
for (int x = 0; x < mapa[y].length; x++) {
int tileX = mapa[y][x][0];
int tileY = mapa[y][x][1];
mapaImagenes[y][x] = spriteSheet.getSprite(tileX, tileY);
}
}
Con el mapa de imágenes listo, podemos proceder a dibujarlo en pantalla. Para ello, definimos un método que reciba las coordenadas de origen donde queremos empezar a dibujar el mapa. Luego, recorremos el mapa de imágenes y dibujamos cada sprite en la posición calculada según su índice y el tamaño de cada tile.
El cálculo de la posición de dibujo se basa en sumar al origen un desplazamiento proporcional al índice del tile multiplicado por el ancho o alto del tile. Así, el tile en la posición X=0 se dibuja justo en el origen, el tile en X=1 se dibuja desplazado una vez el ancho del tile, y así sucesivamente. Lo mismo aplica para la coordenada Y.
Este cálculo se puede expresar con las siguientes fórmulas:
float posX = origenX + x * anchoTile;
float posY = origenY + y * altoTile;
Donde anchoTile y altoTile se obtienen del tamaño del primer sprite del Sprite Sheet, asumiendo que todos los tiles tienen las mismas dimensiones.
El método para dibujar el mapa completo quedaría así:
public void draw(float origenX, float origenY) {
int anchoTile = mapaImagenes[0][0].getWidth();
int altoTile = mapaImagenes[0][0].getHeight();
for (int y = 0; y < mapaImagenes.length; y++) {
for (int x = 0; x < mapaImagenes[y].length; x++) {
float posX = origenX + x * anchoTile;
float posY = origenY + y * altoTile;
mapaImagenes[y][x].draw(posX, posY);
}
}
}
Para mejorar la organización y compatibilidad con otras partes del juego, es recomendable que la clase que maneja el mapa implemente la interfaz Renderable de Slick, que obliga a definir un método draw. Esto permite que el mapa pueda ser tratado como cualquier otro objeto dibujable dentro del motor.
Además, podemos modificar el mapa lógico para incluir elementos concretos, como montículos o montañas, asignando las coordenadas adecuadas de los tiles que representan esas estructuras. Aunque hacerlo directamente en código es menos intuitivo que usar un editor visual, nos permite entender cómo se construye el mapa desde cero.
En resumen, el proceso consiste en definir un mapa lógico con las coordenadas de los tiles, transformarlo en un mapa de imágenes extrayendo los sprites del Sprite Sheet, y finalmente dibujar cada tile en la posición correcta en pantalla, calculando su desplazamiento en función de su índice y el tamaño del tile. Este método nos permite crear escenas complejas y personalizadas en juegos 2D de manera eficiente y organizada.