Tetris en Arduino.
Mónica Pastor Abanades
Lidia López Pérez
Doble Grado Ingeniería Informática – ADE
SEyRE – Vicálvaro – 2017
1. INTRODUCCIÓN.
Hemos decido recrear el mítico juego del Tetris con un Arduino y una pantalla.
Sigue el funcionamiento clásico del Tetris, con un joystick se mueven las piezas hacia los lados, y con el curso hacia abajo bajan más deprisa. Pulsando el joystick la pieza gira 90 grados hacia la derecha, y así puedes rotar la pieza las veces que desees.
La pantalla cuenta con un botón reset con el que puedes finalizar una partida y comenzar otra nueva. Por otro lado, en la otra placa de Arduino, contamos con un buzzer que es el encargado de general la canción del Tetris como ambientación al juego, pero cuenta con un botón de apagado por si nos gusta más el modo mute.
2. COMPONENTES Y COSTES.
COMPONENTE |
CANTIDAD |
COSTE |
Pantalla |
1 |
16 € |
Arduino |
2 |
5,5 € |
Joystick |
1 |
2,5 € |
Cables |
Varios |
1 € |
Zumbador |
1 |
1 € |
TOTAL |
26 € |
3. FUNCIONAMIENTO.
Para poder hacer uso de la pantalla usamos la librería MCUFRIEND_kbv, usando los métodos y variables que contenía y que eran necesarios para implementar el código para el Tetris. Tras familiarizarnos con la librería, y cambiando algunos métodos para la visualización correcta de ésta, comenzamos el desarrollo de los métodos en sí del juego.
Tras añadir el resto de librerías necesarias para su correcto funcionamiento, añadimos las definiciones de los pines y tras ellos los colores que usaremos en la pantalla.
Nuestras variables son la pantalla (tft), la matriz de piezas, que almacena las piezas cuando están paradas y es de 32 por 18, después una variable de la posición x y otra de la posición y para saber en qué pixel estamos, y fila y columna que también son para situarnos, pero dentro de la matriz. El joystick cuenta con tres variables, una para el eje X, otra para el eje Y, y otra para cuando pulsamos el joystick y actúa como botón.
Tras ello llegan los métodos. Antes de explicarlos es mejor comentar en qué nos hemos basado para generar estos métodos. El funcionamiento básico es que nuestro Tetris contiene piezas que son matrices (de 4 por 4). Y la pantalla de juego (game) guarda las piezas cuando caen, es decir, cuando tienen una pieza debajo o el final de la pantalla. Ésta es una matriz de ceros y unos, que tendrá un uno cuando una parte de la pieza al caer ocupe ese espacio.
Así, según las piezas van bajando, se muestran por pantalla gracias al método draw, que pinta los píxeles de ésta por pantalla y a la vez se van borrando crear la animación del movimiento de la pieza. Durante todo el proceso de bajar por la pantalla, van comprobando lo que hay en la matriz de paradas, y comparándolo con todas las filas de la pieza (dado que cada una ocupará diferente según el tipo o si está rotada etc) para ver si podemos seguir bajando o no. Una vez no podamos bajar más, porque encontremos el fondo o haya alguna pieza, se detiene y se coloca en la matriz de paradas. Para moverse hacia los lados y girar sigue el mismo procedimiento, comprueba si puede girar, o moverse, y si puede hacerlo lo hace, y en caso contrario se queda como está. En estos casos internamente lo que hace es crear una copia de la pieza sin llegar a pintarla y se prueba a girarla o moverla, en caso de que chocase con algo no se podría mover la pieza original y en caso contrario se realizaría.
Pero, ¿cuál es el objetivo del Tetris? Eliminar líneas. Una línea debe desaparecer cuando una fila esté rellena con 1’s, por tanto, cada vez que colocamos una pieza, tenemos un método que comprueba este hecho, y de ser así, elimina las filas que estén completas. Y el final del juego llega cuando una pieza desborda la matriz por arriba, momento en el que el juego termina y te informa de que has perdido. Después de esto por defecto se reinicia el juego otra vez.
Las piezas se almacenan en una matriz de piezas, que contiene los siete tipos de pieza que tenemos en el Tetris, y a cada una le asigna un color (siempre el mismo), y son elegidas de forma aleatoria.
¿Qué métodos hemos empleado para la implementación?
Hemos dividido los métodos utilizados en estéticos, que hacen cosas de diseño, funcionales, que implementan lo básico del Tetris, comprobación, que permiten -o no- hacer las cosas, principales, que son el motor del juego y configuración.
TIPO |
FUNCIÓN |
EXPLICACIÓN |
ESTÉTICO |
pantallaInicio |
Este método simplemente muestra en la pantalla el mensaje “Tetris”, actuando como página de inicio. |
dibujoInicial |
Crea el fondo de pantalla |
|
inicializarMatrizGame |
Rellena el interior (la matriz) con ceros, ya que no hay piezas aún. |
|
reiniciarPantalla |
Elimina el contenido del interior de la matriz. |
|
dibujarPantalla |
Crea la parte estática de la pantalla, es decir, crea los bordes, que delimitaran la zona de juego del Tetris y donde se muestra la siguiente pieza que vendrá. |
|
mostrarMensajeFin |
Imprime un “Perdiste” al final del juego. |
|
mostrarPiezaDespues |
Nos muestra, en la parte estática de arriba, la pieza siguiente. |
|
FUNCIONAL |
draw |
Imprime por pantalla la pieza que reciba de argumento. |
rotatePieza |
Gira la pieza 90º. |
|
guardarPieza |
Guarda la pieza que acaba de caer en la matriz del juego, para que se integre en ésta. |
|
drawGame |
Es el método encargado de pintar la matriz del juego. |
|
moverDerecha |
Mueve la pieza una posición a la derecha (si puede). |
|
moverIzquierda |
Mueve la pieza una posición a la izquierda (si puede). |
|
eliminarFilas |
Elimina las filas completas (todo a unos) |
|
finPartida |
Si hay una pieza en la primera fila de la matriz indica el final de juego. |
|
PRINCIPAL |
inicioJuego |
Sería nuestra función “principal”, la que inicia la bajada de piezas, las va sacando, va mostrando cual va después, las baja, lee el joystick para saber qué hacer con ellas, hace las comprobaciones, elimina las filas… En definitiva, el juego en sí. |
COMPROBACIÓN |
esCuadrado |
Esta función comprueba si la pieza es el cuadrado, ya que esta pieza es especial y no rota, porque quedaría igual. |
copiarPieza |
Hace una copia de la pieza. Esta copia se utiliza para hacer las comprobaciones a la hora de bajar, girar, moverse hacia los lados y rotar. |
|
tocaSuelo |
Comprueba si la pieza toca el suelo u otra pieza que haya por abajo, para ver cuando tiene que detenerse. |
|
tocaDerecha |
Comprueba si la pieza puede moverse o no a la derecha. |
|
tocaIzquierda |
Comprueba si la pieza puede moverse o no a la izquierda. |
|
verSiPuedeRotar |
Comprueba si puede rotar la pieza. |
|
DEPURACIÓN |
printPieza |
Imprime los ceros y unos de la matriz de la pieza. |
printGame |
Imprime los ceros y uno de la matriz del juego. |
|
CONFIGURACIÓN |
setup |
Hace configuraciones iniciales de la pantalla y el joystick. |
loop |
Se ponen los métodos para realizar el juego completo. Pone la pantalla en negro, muestra la de inicio, dibuja la del juego, inicia la matriz, y luego da inicio al juego con la función “principal” comentada anteriormente. |
Ya hemos mencionado que la pantalla funciona gracias a su librería. Pero siendo más concretos, el funcionamiento de la pantalla se basa en dibujar los pixeles correspondientes en cada momento para lo que sea que necesitemos.
En cuanto al joystick, funciona gracias a la intensidad de corriente. Según te muevas en el eje X y en el eje Y, hacia un lado o hacia otro, obtenemos un voltaje diferente. Por lo tanto, actuamos en consecuencia a ese voltaje, moviéndonos hacia un lado o hacia otro. Cuando deslizamos el joystick hacia abajo lo que hacemos es aumentar la velocidad a la que baja la pieza. Y cuando pulsamos el joystick, la pieza rota 90º.
Para el funcionamiento del zumbador hemos utilizado un código ya existente, que consiste en programar diferentes notas, y la duración de éstas, para generar canciones con ellas, y en este caso hemos generado la música del Tetris.
4. ESQUEMA HARDWARE.
Como se puede observar, hemos tenido que utilizar dos arduinos. El motivo es la corriente que necesita la pantalla y el joystick, no podíamos utilizar pines de la pantalla puesto que se creaba ruido y el joystick no funcionaba correctamente. Por lo tanto, este se nutre de la corriente de un Arduino, que además hemos añadido un zumbador y la pantalla utiliza la del otro.
Cabe destacar que, a simple vista, la pantalla acapara la mayoría de pines, y aunque es así, hay muchos que pese a estar conectados, no son utilizados (por ejemplo la tarjeta SD que se permite usar en la pantalla, nosotras no la utilizamos etc). Los utilizados se pueden observar en el esquema de arriba.
5. CONSTRUCCIÓN.
Nuestro proyecto no requiere un gran montaje hardware. Simplemente hay que encajar la pantalla con el Arduino.
La parte más complicada fue buscar los pines que la pantalla no utilizaba para poder utilizar el joystick. Una vez encontramos estos, los unimos mediante cables, como se puede observar en las imágenes.
Los cables negro naranja, blanco y ocre son los encargados del eje X, eje Y, y de la función al pulsar el botón del joystick respectivamente.
Por otro lado, el joystick se conectó al otro Arduino, donde también conectamos el zumbador. Esta vez sin problemas de pines.
Tras la parte construida de electrónica pasamos a la parte de la carcasa. La construcción es de madera, donde hemos atornillado los arduinos, el joystick y el zumbador junto al botón.
Después soldamos los cables que conectan el joystick con el arduino, y los que conectan el zumbador con el interruptor.
Por último, construimos una tapa para ocultar la construcción, dejando a la vista tan solo el joystick, la pantalla, y el botón que sirve para apagar y encender el zumbador. Hemos agujereado la zona donde está el zumbador, para que simule un “altavoz”.
6. PROBLEMAS ENCONTRADOS.
Durante el desarrollo de la práctica hemos encontrado diversos problemas de los que podemos destacar:
– Refresh de la pantalla: la pantalla tenía que actualizar constantemente su contenido para que se mostrase como las piezas bajaban, giraban, rotaban… ¿Cómo lo hacía? Actualizando el contenido. El problema es que actualizaba toda la pantalla, por lo que todo el rato la pantalla se borraba y se dibujaba, dando la sensación de iba a saltos (véase foto a continuación). La solución que dimos a este problema es mantener fijas las cosas de las pantallas que no necesitaban cambios, y tan solo hacer refresh a la parte de la pantalla donde se muestran las piezas.
– Rotación correcta: en un primer planteamiento del método rotar, las piezas en lugar de rotar 90º en sentido horario, rotaban 180º. La solución fue plantear las piezas en matrices, y pensar en qué dirección había que mover los unos que dibujaban la pieza, para que hicieran el giro correcto de 90º. Además había piezas como el cuadrado que rotaban dando una sensación extraña (véase la foto a continuación), por lo que ésta decidimos no rotarla.
– Dibujar las piezas correctamente: este fue uno de los problemas más complicados de solventar. El problema es que a veces las piezas se paraban donde no debían, o que se integraban con una pieza que ya estuviera abajo ocupando su lugar en la matriz. El problema radicaba en los pixeles que estaban ocupando de la pantalla, en dibujar estos correctamente. El modo de solucionarlo fue pensando las dimensiones de la pantalla, y en concreto del rectángulo en el que íbamos a desarrollar el juego. Esta zona la tuvimos que dividir en filas y columnas, y tras ello hacer coincidir los pixeles con el espacio que ocupaba cada pieza dentro de la matriz, para que se guardasen en el lugar correcto.
– Detener las piezas: relacionado con el problema anterior, surgía el problema de detener las piezas correctamente, ya fuera porque había una pieza por debajo que la impidiese bajar, o directamente el suelo (véase las fotos a continuación). La solución es que, en la matriz game se crea cuando la pieza está bajando, constantemente está comprobando las filas que hay por debajo, en las columnas en las que se encuentra, para tener seguro que puede seguir bajando, o detenerse en caso contrario.
– Rotación de las piezas: a la hora de rotar las piezas, no solo rotaban incorrectamente, sino que a veces rotaban sin poder hacerlo, porque hubiera otra pieza o los límites de la matriz. Para solucionarlo creamos una copia de la pieza, que, si rota, y se comprueba si entra en conflicto con los límites u otra pieza, y en caso de no hacerlo, ya gira la pieza original.
– Tetris a la inversa: un problema curioso es que, las cosas se mostraban al revés, como si se tratase de un espejo. No tenía ningún sentido, pero el problema estaba en la librería, donde tras modificar algunas cosas, conseguimos que las cosas se vieran correctamente.
7. ANEXOS.
– Vídeo de la evolución.