Eileen, comenzando…

Tuesday, October 3rd, 2006 @ 9:15 | Eileen, Proyectos

Este fin de semana he estado ampliando y mejorando a Nessi. Para poner a prueba esas nuevas funcionalidades y con cierta mosca detrás de la oreja (nunca he hecho un tetris), he comenzado Eileen. A parte de los conceptos que todo juego ha de tener, como el de game loop, timing… para este juego tenía que pensar cómo gestionar las piezas y el tablero de juego (el rectángulo que va conteniendo las piezas que se van generando…). Bien…

La matriz Matrix

Pensando un poco sobre ello… una ruta a tomar podría ser basarlo en coordenadas y colisiones de los sprites (o piezas). Sin profundizar demasiado, me viene a la mente alguna de las limitaciones de tal planteamiento… como por ejemplo la eliminación de una fila tras hacer línea. Igual se podría hacer algún tipo de cálculo para solucionar cosas así, pero teniendo la experiencia exitosa de una matriz (en el buscaminas, el ajedrez y otros programillas que tengo por ahí con el interface más que simplón…) por debajo que controle todo… me decanté por ella.

Matrix quedó definida con 20 filas y 14 columnas… (el espacio del tablero dibujado en photoshop entre 25, tamaño del cuadro base). A través de una función de Reset, todos sus elementos son inicializados a cero, código que he tomado como marca de que esa casilla está vacía.

Las piezas

Otro aspecto importante, quizás de lo más importante, son las piezas. Antes he hablado del tamaño del cuadro base. En el tetris las piezas se desplazan por una especie de casillero con celdas (que no se ven, pero cada cuadro que forma una pieza, al desplazarse, se mueve de celda a celda). A donde quiero llegar es a cómo estarán hechas las piezas. Una pieza que sólo ocupe un cuadro o celda en cada movimiento (llamémosla A), no tiene complicación. Pero como es obvio, un tetris de sólo esa pieza quedaría muy pobre y poco jugable. Si meto una pieza que ocupe dos cuadros horizontales.. los problemas surgen. Una de las ideas era formar las piezas por unión de sprites únicos (la citada A): generar el tipo de pieza y en funcion de ello dibujar en pantalla la forma correspondiente, actualizando las posiciones de todos los A de la pieza y de todas las piezas. Mi principal problema con esto era gráfico. Me gustaba que las piezas tuvieran un borde alrededor, así que uniendo A con otra A, en el medio de la unión, tendría un borde algo feote. Como solución a esto podría haberme currado la pieza A sin borde una vez en cada lado… pero decidí optar por otra forma de hacerlo.

Cada pieza es un gráfico. En total, de momento, hay 10 piezas disponibles, que se irán generando con un random y moviéndose por Matrix. A tales efectos, cada pieza irá actualizando los elementos de Matrix que ocupe / libere con cada movimiento. Es decir, la pieza A comenzará en el elemento 0,0. Un ciclo despues… si el usuario no mueve hacia los lados, ocupará la posición 1,0. La manera de indicar ese ocupamiento es mediante números. (El número de pieza (1,2,3… hasta 10)) Para entender mejor a lo que me refiero… mira esta captura. En ella se ve Matrix y todos sus valores. Los números negativos indican que esa posición está ocupada por ese pieza. (-2, por la 2, -4, por la 4…).

Dibujando las piezas

Como comentaba en algún párrafo anterior, Eileen se basa en Matrix. A través del valor de sus elementos, el procedimiento void DrawMatrix(int Matrix[20][14]); dibuja el juego. En una instancia de Nessi_Sprite llamada piece, cargo todas las piezas, con el orden de piezas ya comentado. (la pieza uno la cargo primera, la pieza 2, segunda…). Hecho esto con dos bucles recorro cada elemento de la matriz. Si el elemento es mayor que cero, dibujo la pieza, seteando el frame activo a Matrix[j][k]-1. (El número de frames de un sprite comienza en cero).

/* Dibuja la matrix en pantalla */
void DrawMatrix(int Matrix[20][14])
{
int j,k;

int inicio_row=88;
int inicio_col=15;

Nessi_Sprite piece(9);
piece.AddFrame(”graphics/piece01.png”);
piece.AddFrame(”graphics/piece02.png”);
piece.AddFrame(”graphics/piece03.png”);
piece.AddFrame(”graphics/piece04.png”);
piece.AddFrame(”graphics/piece05.png”);
piece.AddFrame(”graphics/piece06.png”);
piece.AddFrame(”graphics/piece07.png”);
piece.AddFrame(”graphics/piece08.png”);
piece.AddFrame(”graphics/piece09.png”);

for(j=0;j<20;j++)
{
for (k=0;k<14;k++)
{
if (Matrix[j][k]>0)
{
piece.SetCurrentFrame(Matrix[j][k]-1);
piece.SetX(inicio_col+(25*k));
piece.SetY(inicio_row+(25*j));
piece.Draw();
}
}
}
}

Las dos variables int de inicio, es la referencia del width y del height en donde comienza el tablero de juego y a partir del cual basarse en el dibujado de piezas. A pesar de que cada pieza que ocupa varias celdas es un gráfico, a efectos de Matrix cuenta como si fueran varios A, pero en lugar de tomar el valor 1 para todos los cuadros, el primer cuadro de arriba a la izquierda vale el número de pieza y, el resto, el número de pieza en negativo. De ahí el if del segundo bucle. Si por ejemplo se encuentra un 4, con el setframe sabe que ha de dibujar la pieza 4 (la que tiene índice 3 en el array de frames) en la posición x e y calculada.

En la galería he subido varias dos caps del juego y una cap de todas las piezas, para que se entienda mejor la filosofía utilizada. Observando las piezas, es curioso que todas ellas, las disponibles en el juego, tiene el citado cuadrado arriba izquierdo dibujado. Esto es así por el motivo explicado antes: para el dibujado tengo que tener una referencia que me indique qué pieza es. Si el cuadro está vacío… tendría que indicar un cero en lugar del número de pieza, perdiendo esa referencia. Ya se me ocurren algunas cosillas como solución… que para no hacer un post kilométrico comentaré en otra ocasión. ;) :)

Por otro lado, comentar que para el cambio de sentido, como se verá en la cap de piezas, hago un cambio de sprite. Es decir, si tengo una pieza 6… y su pieza girada es, por ejemplo, la número 15… sólo hay que cambiar el id de la pieza actual ajustando el valor de los elementos de Matrix afectados.

Validaciones

Para que el juego de tetris sea tal, es necesario implementar un sistema de validaciones. Que la pieza no se salga del tablero por ninguno de los lados, que una pieza no ocupe una posición o alguna de las posiciones que esté ocupando otra pieza… con Matrix y los valores de sus elementos, se comprueba todo ello según la pieza actual. (básicamente, si hay cero, la celda está libre y se permite el movimiento).

Haciendo línea

Hay que tener en cuenta que cuando una línea esté totalmente llena, hay que borrar esa línea y bajar las superiores a ella. Esto lo logré en tres pasos…

  1. Convierto todas las celdas ocupadas a 1, menos las de la fila que tiene la linea.
  2. Seteo a cero las celdas de la fila que tiene línea.
  3. Muevo progresivamente las celdas hacia abajo. (Matrix[j+1][k]=Matrix[j][k]; ) Para que sea eficiente, una vez una fila es toda de ceros, dejo de mover hacia abajo.

Puntos, velocidad….

Ahora queda llevar el control de puntos, de la velocidad del juego según el nivel… y todos los detalles deseados. Quizá sea la parte menos complicada… así que tampoco entraré en detalles… ;) Todavía me quedan cosillas de esas y algún bug que otro por resolver y mejorar… así que tardaré algo má en publicar el .exe… :roll:

Leave a Reply