En este artículo, trataré por qué y cómo usar varias capas en nuestros diseños de escenarios. Es un artículo un poco largo, así que intentaré explicarlo de forma ordenada para que queden claros los conceptos que quiero tratar. Para cualquier duda o sugerencia, pueden usar los comentarios e intentaré ayudarles.
Introducción
En el ejemplo anterior vimos cómo dibujar nuestro mapa con libGDX. Lo cierto es que el escenario dibujado era simple; un mapa con una sola capa y un único tileset. El ejemplo presenta un problema de cara a trabajar con el mapa. Veamos de nuevo la imagen para explicar en qué consiste:

Supongamos ahora que tenemos nuestro personaje principal caminando por la escena. Una de las cosas que habrá que detectar son las colisiones con el escenario. Una aproximación muy básica puede ser comprobar si la posición donde se encuentra el personaje está ocupada por un tile o está vacía. El problema es que en nuestro ejemplo, dibujamos en la misma capa elementos con los que sí se colisiona (suelo, agua) y otros elementos de decoración con los que en principio no debe haber colisión (casa, arbustos). Por lo tanto si usamos este sistema para detectar colisiones, nuestro personaje colisionará también con los elementos de decoración.
Una solución a este problema es separar los elementos de nuestro escenario en varias capas: tendremos una principal por donde se muevan nuestros personajes, y tantas como necesitemos para decorar y ambientar la escena.

Además…
Hemos señalado el problema de utilizar una sola capa a la hora de detectar las colisiones, pero esto no tiene por qué ser así ya que podemos utilizar otros sistemas de detección de colisiones que no dependan directamente del mapa del escenario (mapas de durezas, motor de físicas, etc.). Sin embargo, otra ventaja que nos aporta el uso de varias capas es poder organizar mejor nuestros recursos o assets; ya que podemos tener tilesets de decoración que podremos reutilizar en distintos mapas de nuestro juego, aunque el tileset principal del escenario cambie.
A continuación veremos cómo usar varios tilesets y varias capas en un mapa de nuestro juego.
Usar varios tilesets
Para seguir avanzando en nuestro ejemplo, crearemos un segundo tileset, con tiles de 16×16 píxeles en el que usaremos elementos para decorar la escena. En mi caso, he llamado a este archivo decoration.png, está guardado en la misma ruta donde se encuentra nuestro mapa, y contiene los siguientes tiles:

Modificar el mapa
A continuación, haremos las siguientes modificaciones a nuestro mapa:
- Tendremos una primera capa, a la que llamaremos «terrain«, donde dejaremos sólo las partes del escenario que afectarán al movimiento de los personajes del juego: tierra y agua. Esta capa utiliza los tiles de tileset.png.
- En una segunda capa, llamada «background«, colocaremos la casa y los arbustos. Aquí también se utiliza el tileset tileset.png.
- Habrá una tercera capa, llamada «decoration«, en la que usaremos el nuevo tileset decoration.png, y en la que crearemos nubes y árboles para decorar la escena.
Este es el resultado de los cambios:

A tener en cuenta
Todas las capas utilizadas son «Capas de patrones«, que son las utilizadas para dibujar el escenario a partir de nuestros tilesets.
Según nos indica la documentación oficial de libGDX, podemos usar las capas que necesitemos, pero es importante que en una capa sólo usemos tiles de un tileset. Esto es así porque al dibujar una capa con más de una textura, se ve afectado negativamente el rendimiento al dibujar en pantalla.
Crear un nuevo proyecto
Para continuar con el ejemplo, podemos seguir sobre el proyecto del artículo anterior o crear un nuevo proyecto. En mi caso, haré una copia del proyecto anterior utilizando mi herramienta de clonado:

Tratando capas en libGDX
libGDX nos da funciones para tratar y dibujar el mapa completo, o hacerlo por capas.
Obtener capas
Para tratar las capas de un mapa, podemos hacerlo usando su nombre o su índice dentro de la lista de capas existentes. La clase que representa una capa de un mapa creado con Tiled Map Editor es TiledMapTileLayer. En este ejemplo veremos cómo usar los dos métodos.
La capa de terreno («terrain«) nos interesa tenerla de forma aislada en un objeto de la clase TiledMapTileLayer, ya que nos facilitará el trabajo en el futuro. Por otro lado, para el resto de capas usaremos un array de enteros llamado decorationLayersIndices, donde colocamos los índices de las capas «decoration» y «background«.
... public class MyGdxTiledGame extends ApplicationAdapter { ... private TiledMap map; private TiledMapTileLayer terrainLayer; private int[] decorationLayersIndices; ... @Override public void create () { ... // Reading map layers MapLayers mapLayers = map.getLayers(); terrainLayer = (TiledMapTileLayer) mapLayers.get("terrain"); decorationLayersIndices = new int[] { mapLayers.getIndex("decoration"), mapLayers.getIndex("background") }; ... } ... } |
Cuando trabajamos con varias capas usando un array de índices, es importante el orden en el que se colocan los elementos. Esto es, el orden del array de índices determina el orden en el que se dibujan las capas. En nuestro caso, cuando dibujemos el array decorationLayersIndices, primero se dibujará la capa «decoration» y luego la capa «background» por encima de la primera.
Dibujar capas
El dibujado de capas, al igual que si dibujáramos el mapa completo, se hace con un objeto de la clase OrthogonalTiledMapRenderer. Veamos como sería:
public class MyGdxTiledGame extends ApplicationAdapter { ... private OrthogonalTiledMapRenderer renderer; ... @Override public void create () { ... // Instantiation of the render for the map object renderer = new OrthogonalTiledMapRenderer(map); } @Override public void render () { // Clear screen Gdx.gl.glClearColor(.5f, .7f, .9f, 1); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); // Update the camera camera.update(); renderer.setView(camera); // Rendering renderer.render(decorationLayersIndices); renderer.getBatch().begin(); renderer.renderTileLayer(terrainLayer); renderer.getBatch().end(); } ... } |
Importante
Si observamos el método render() del ejemplo, vemos que existen diferencias entre dibujar una capa directamente o dibujarla mediantes sus índices. En el primer caso, es neceseario iniciar el batch de dibujo antes de dibujar las capas y finalizarlo al terminar.
Por último, indicar que primero se dibujan las capas de decoración y luego se dibuja la capa de terreno para que éste última quede «por encima» del resto.
Código fuente
Podéis consultar el proyecto de ejemplo desde mi respositorio de GitHub.