Tutorial de Tiles 1ª parte, el primer mapa

Escrito por el 1 mayo, 2012

Como lo prometido es deuda, aquí tenemos el primer capítulo de una nueva serie de tutoriales, ésta vez centrados en una de las técnicas más antiguas y que mejores resultados a conseguido en el mundo de la programación de videojuegos: Los juegos de mapas basados en tiles (cuadrículas). Un mapa basado en tiles no es más que una matriz de dos dimensiones, por ejemplo mapa[filas][columnas]. En cada casilla de la matriz, metemos un objeto (o número) que indica el tipo de casilla que ocupa ese lugar. Todas las casillas ocupan el mismo tamaño, asi que luego, para pintar el mapa, sólo tenemos que ir recorriendo esa matriz, y pintando cuadrados con los colores o imágenes acordes al dato almacenado para esa posición.

Incluso podemos usar esa matriz, para detectar las colisiones. Por ejemplo, el valor 1 quiere decir que la casilla está vacía. Un 2, que es una pared infranqueable. Cuando ordenamos al personaje que se mueva, simplemente miramos en la matriz la posición a dónde queremos movernos. Si es una pared, entonces le impedimos al personaje dicho movimiento. Si es la casilla vacía, entonces lo movemos sin problemas. En resumen, ésta es la idea básica que hay que tener en mente.

Como viene siendo habitual, para ilustrar el tutorial, hice un minijuego basado en el famoso Pac-Man, el cual podeis probar aquí, y descargar un zip con los archivos aquí. Sin embargo el código lo hice hace mucho tiempo (aunque recientemente lo he reescrito ligeramente siguiendo el estilo de programación orientada a objetos que vimos en el tutorial anterior a esta serie), y cometí errores importantes en cómo reparto las tareas entre las clases. Así que he optado por empezar de cero, aunque ahí he dejado los enlaces para el que quiera, que pueda echarle un vistazo al código. Como nota, diré que el error más importante que cometí en el juego, es hacer que el personaje ocupe completamente una de las casillas de la matriz. Así que no hay ninguna animación cuando un personaje pasa de una casilla a otra, simplemente lo hace de golpe, lo que hace que el juego se vea muy feo. Otro error que produce esa situación es que a veces, las colisiones no se producen, puesto que al principio las detectaba sólo si el pacman y algún fantasma estaban en la misma casilla. Eso producía que a veces, el fantasma “saltara” por encima del pacman cuando estaban en casillas adyacentes. Aunque incluí código para detectar esa situación, es un problema que en realidad no debería existir. Asi que borrón y cuenta nueva.

Buscando y rebuscando por la internet, algún índice sobre el que basarme para explicar la programación orientada a tiles (y qué coño, para aprender yo mismo también), he encontrado una serie de útiles tutoriales en las siguientes direcciones:

Asi que para seguir un guión me basaré en el mismo esqueleto que todos ellos, sobre todo el del segundo enlace. Particularmente, el último enlace me ha provocado una duda filosófica. ¿Es acertado traducir el tutorial del segundo enlace, cuando el trabajo ya lo ha hecho el autor de la página redribera.es?¿Es lícito copiar el texto que ha traducido otro para evitar reescribirlo?¿Cuánto del código echo para Flash es útil para javascript? Así que finalmente he optado por una tercera vía: Aprender los conceptos básicos de esos tutoriales, y crear de cero mis propios ejemplos para JavaScript, siguiendo siempre que pueda, el mismo guión y los mismos ejemplos que aparecen en esos tutoriales. En cuanto al texto, será mayoritariamente de cosecha propia.

El maravilloso mundo de los Tiles

Tile, traducido al español, significa azulejo, o baldosa. Como dije al principio, básicamente lo que queremos hacer es pintar un mapa mediante trozos cuadrados de imágenes (o colores planos). Para crear un mapa con mucho detalle, no es necesario tener una imagen que lo abarque todo. Por ejemplo:

Aquí tenemos un mapa que podemos dividir en cuadrículas. Como ves, las casillas 1-2 son la misma imagen, y lo mismo ocurre con 3-4, y 5-6-7. Si quisiera crear éste mapa, crearía un conjunto de tiles con las imágenes de 1, 3, 5 y las 4 de la fuente, y lasde las otras esquinas moradas diferentes a 3. A cada tile le asigno un índice numérico, y el mapa sólo sería una matriz de dos dimensiones, almacenando en cada casilla, el índice de la imagen que luego pintaré en el CANVAS.

En resumen, crear un mapa es como crear un mosaico, repitiendo una y mil veces cada una de las imágenes que tenemos almacenadas como un conjunto de tiles, dibujándolas en un orden determinado.

Lo veremos más claro con el siguiente código.

El primer mapa

En el iframe de arriba podemos ver el primer ejemplo ejecutándose, Puedes verlo también abriendo este enlace. Un sencillo mapa creado con dos tiles muy básicos: Uno blanco y otro negro. El código necesario, es el siguiente:

<html>
    <head>
        <title>Ejemplo Tile 01</title>
        <script type="text/javascript" src="js/Tile.js"></script>
        <script type="text/javascript" src="js/Mundo.js"></script>
        <script>
            var mundo;
            window.onload=function(){
                mundo=new Mundo("micanvas",30,30); 
            };
        </script>
    </head>

    <body>
        <canvas id="micanvas"></canvas>
    </body>
</html>

El HTML no tiene nada raro. De hecho, tiene menos código que de costumbre. Sólo contiene un elemento CANVAS sobre el que pintar el juego, y una llamada al constructor de la clase Mundo.

function Mundo(idCanvas, anchoCelda, altoCelda){
    this.canvas=document.getElementById(idCanvas);
    this.contexto=this.canvas.getContext('2d');
   
    this.anchoCelda=anchoCelda;
    this.altoCelda=altoCelda;
   
    this.conjuntoTiles=[new Tile(this.anchoCelda,this.altoCelda,true,"white"),new Tile(this.anchoCelda,this.altoCelda,false,"black")];
   
    this.mapa=
    [
        [1, 1, 1, 1, 1, 1, 1, 1],
        [1, 0, 0, 0, 0, 0, 0, 1],
        [1, 0, 1, 0, 0, 0, 0, 1],
        [1, 0, 0, 0, 0, 1, 0, 1],
        [1, 0, 0, 0, 0, 0, 0, 1],
        [1, 1, 1, 1, 1, 1, 1, 1]
    ];
   
    this.canvas.width=anchoCelda*this.mapa[0].length;
    this.canvas.height=altoCelda*this.mapa.length;
   
    this.dibujarMapa();
}

Mundo.prototype.dibujarMapa=function(){
    var y=this.mapa.length;
    var x=this.mapa[0].length;
    for (var yi=0;yi<y;yi++)
    {
        for (var xi=0;xi<x;xi++)
        {
            this.conjuntoTiles[this.mapa[yi][xi]].dibujar(this.contexto,xi,yi);
        }
    }
};

Aquí empieza lo interesante. El constructor carga el contexto canvas como siempre, aceptando además del id del canvas, un par de números que definen el ancho de cada tile (celda, azulejo, casilla, etc) del juego. A continuación, definimos el conjunto de tiles del juego. De momento sólo necesitamos dos, el tile blanco, que será las zonas por las que podremos movernos, y el tile negro, que hará de pared impenetrable. El constructor de los tiles acepta 4 parámetros: El ancho y alto que tiene, el color con el que dibujarlo (de momento no usaremos imágenes), y un boolean que indica si los jugadores pueden caminar sobre ese tile.

A continuación, definimos el mapa. Cada casilla del mapa contiene el índice del tipo de Tile que tendrá que dibujarse para esa posición. Se puede ver que para evitar que el jugador se escape del mapa, está completamente rodeados de unos, es decir, de tiles negros infranqueables. Además, hemos añadido un par de obstáculos por el centro, ya que tampoco queremos que sea un solar.

Una vez definido el mapa, podemos calcular el ancho y alto total que tiene que tener el canvas para que podamos dibujarlo todo dentro. Por último, como llamamos al método que se encargará de dibujar el mapa. De momento, nos olvidamos del loop. Como no hay nada que se mueva, con dibujarlo una vez, basta.

El método de dibujarMapa recorre el mapa fila a fila, columna a columna, y van ordenando a cada tipo de tile que se dibuje, según la posición que ocupe en la matriz bidimensional.

function Tile(ancho, alto, caminable, color){
    this.ancho=ancho;
    this.alto=alto;
    this.caminable=caminable;
    this.color=color;
}
Tile.prototype.dibujar=function(contexto,x,y){
    contexto.fillStyle = "#444";
    contexto.fillRect(this.ancho*x,this.alto*y,this.ancho,this.alto);
    contexto.fillStyle = this.color;
    contexto.fillRect(this.ancho*x+1,this.alto*y+1,this.ancho-2,this.alto-2);
};

La clase Tile de momento no tiene mucho. Almacenamos los parámetros que nos llegan en el constructor, y simplemente pintamos un rectángulo en la posición indicada con las variables almacenadas como eran su color, su ancho y su alto. Para que se vean más claro las celdas, en vez de pintar todo el rectángulo con el color adecuado al tipo de Tile, lo que hacemos es pintar un rectángulo gris oscuro (#444) que llene toda la celda, y luego pintar un rectángulo ligerísimamente más pequeño, con el color auténtico.

Si probais el ejemplo en local, podeis modificar el mapa añadiendo más filas y columnas, creando laberintos, habitaciones, etc, todo lo que se te ocurra. Además, podeis añadir nuevos tiles de diferentes colores para dar más detalle al escenario. El límite es vuestra imaginación.

Y con ésto, ya tenemos programado nuestro primer mapa tile. Felicidades, pero no nos chupemos las poyas todavía, aún queda muuuucho trabajo por hacer.

Etiquetas: , , , , , ,

Comentarios (5)

  • Muy bueno gracias, espero con impaciencia las siguientes partes.

  • Impresionante trabajo que te has pegado. Es genial el tutorial y me ha ayudado mucho a comprender como funciona javascript orientado a objetos y a practicar Canvas.
    Me ha costado un poco, pero después de hacerlo unas cuantas veces ya lo veo bastante claro.
    Gracias!

  • podeis explicarme esta parte tio:

    this.conjuntoTiles[this.mapa[yi][xi]].dibujar(this.contexto,xi,yi);

  • Hay algo que me llama mucho la atención. Por que predefine todo con un “this.”? Si no lo colocas, no funciona?

  • Hola rokcrwo
    Contestare por el autor :D
    “this” es un variable especial presente en la mayoría de los lenguajes con cierta orientación a objeto (como javascript). Se usa para acceder a propiedades o incluso métodos del ámbito de clase. Deberías leer un poco sobre ámbito de variables (y también de poo y prototipos) en javascript para entenderlo mejor
    Ve a http://www.librosweb.es, allí tienes todo el material que necesitas

¿Tienes algo que decir?

Gestionado con Wordpress y Stripes Theme Entradas (RSS) | Comentarios (RSS)