Cómo usa libGDX + Tiled: usando con varias capas

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*