jueves, 30 de mayo de 2013

CraftyJS: Un juego típico en 2D

Los juegos en dos dimensiones, ya sea un Arkanoid, un Comecocos o un juego de naves, se resuelven dividiendo la pantalla en una rejilla que servirá de base para localizar los elementos de nuestro juego.

Quizá el ejemplo más gráfico sea un laberinto. La idea es tener una matriz que describa cada celda de nuestra rejilla.

Tomemos el ejemplo de este laberinto apto para cualquiera de nuestros dirigentes:


Podemos ver que hay 3 tipos de celdas:

Una celda tipo 0, que será el suelo, o la nada si lo queréis llamar así.
Una celda tipo 1, que serán los muros exteriores.
Una celda tipo 2, que serán las paredes interiores del laberinto.

A efectos prácticos, el juego reaccionará de la misma manera en una celda tipo 1 y tipo 2, es decir, no dejará pasar a nuestro personaje. Sin embargo, el dibujo será distinto y de ahí, su diferenciación.

La matriz

Ya nos ha quedado claro que nuestro laberinto quedará definido por una matriz, así que intuitivamente haríamos lo siguiente:

  var mapa = [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
                      [1, 0, 2, 2, 0, 2, 0, 2, 2, 1],
                      [1, 0, 0, 2, 0, 2, 0, 0, 0, 1],
                      [1, 2, 0, 2, 0, 2, 2, 2, 0, 1],
                      [1, 2, 0, 0, 0, 0, 0, 2, 0, 1],
                      [1, 2, 2, 2, 0, 2, 0, 2, 0, 1],
                      [1, 0, 0, 0, 0, 2, 0, 2, 0, 1],
                      [1, 0, 2, 2, 0, 2, 0, 2, 0, 1],
                      [1, 0, 2, 2, 0, 2, 0 ,0 ,0, 1],
                      [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]];

Pero reflexionemos ahora realmente que posiciones representan estos elementos en la tabla:

[0][0]   [0][1]   [0][2]   [0][3]   ...
[1][0]   [1][1]   [1][2]   [1][3]   ...
[2][0]   [2][1]   [2][2]   [2][3]   ...
   ...         ...          ...         ...

Si representamos intuitivamente las posiciones de la tabla por las coordenadas (x,y), vemos que las horizontales vienen representadas por las 'y'  y las verticales por las 'x'. Es decir, justo al contrario de lo que hemos intuido.

¿Cómo podemos resolverlo?

Podemos montar la matriz cambiando filas por columnas, o podemos hacer lo mismo matemáticamente con un procedimiento que calcule la matriz traspuesta (que es como se llama en matemáticas cambiar filas por columnas), o bien podemos ser mucho más guarros y hacer lo que haremos en el ejemplo:
Nos olvidaremos del detalle de que las filas y columnas están invertidas y programaremos el algoritmo como si las horizontales fueran las 'x' y las verticales las 'y', y en el momento de coger el valor de la matriz lo cogeremos como tabla[y][x], en lugar de tabla[x][y].
Ejemplo:
  for (var x = 0; x < número de columnas; x++) {
    for (var y = 0; y < número de filas; y++) {
      if (mapa[y][x]==1) {
        
            Pintamos una entidad de tipo muro exterior;

     }
   }
 }

Como que desde el momento que pintamos la celda esta adquiere un entidad propia, ya no volveremos a leer la matriz y no tendremos que volver a hacer cosas extrañas, ya que desde este punto las 'x' serán 'x' y las 'y' serán 'y'.

El canvas

El canvas es el tápiz en el que dibujaremos nuestro laberinto. El primer paso que tenemos que hacer es definir su tamaño. (Aquí lo podeis ver con más detalle). ¿Pero cuál es ese tamaño? Lo podemos deducir de la matriz.

Para ello declararemos dos propiedades que definan el tamaño de la celda:

anchoCelda : 32,
altoCelda : 32,

Así pues la anchura del cavas será el número de columnas del mapa * anchoCelda y su altura el número de filas del mapa * altoCelda.
Teniendo en cuenta que en Javascript las matrices como tal no existen si no que son arrays de arrays, el número de filas (o largo) del mapa, es el número de elementos del array, y el número de columnas (o ancho) del mapa, es el número de elementos de cualquiera de los arrays (por ejemplo el 0).

var anchoCanvas = mapa[0].length * Game.anchoCelda;
var largoCanvas = mapa.length * Game.altoCelda;

Colocando las celdas

Con todo lo anterior tenemos una matriz de 10 x 10 elementos (o celdas) para colocar sobre un tapiz (o canvas) de 320 x 320. ¿Cómo hallar las 'x' y las 'y' efectivas?  Pues multiplicando la posición de la matriz por el ancho o el alto de la celda.

Así pues:

mapa[0][0]  :   x =  0 * 32  ,   y = 0 * 32  
mapa[0][1]  :   x =  0 * 32  ,   y = 1 * 32
mapa[4][3]  :   x =  4 * 32  ,   y = 3 * 32

Notad que estoy obviando en el planteamiento lógico la transposición de la matriz como hemos dicho que haríamos.

El algoritmo

Con todo esto ya estamos en disposición de presentar el código:

Esto nos dará el siguiente resultado:


¿A que es una maravilla?

NOTA:

  1. Para ejecutar este programa debemos tenes un .html que lo llame. Lo podéis ver aquí.
  2. Si el tema de las entidades os ha sonado a chino, aquí se intenta explicar.






No hay comentarios:

Publicar un comentario