Serpiente en Arduino cambiando de dirección

Juego de la serpiente (snake) en Arduino

Hoy vamos a ver el desarrollo del videojuego snake en Arduino usando una LCD de 16 x 2.

Lo que haremos será dibujar una serpiente por la pantalla y permitir que se mueva, creciendo en su tamaño, comiendo y refrescando la LCD con las nuevas posiciones.

Es decir, un juego snake completo pero usando un Arduino y una LCD como hardware, nada de computadoras.

Aunque parece simple la verdad es que fue algo difícil.

Snake en Arduino

Antes de comenzar el post dejo el vídeo de demostración. Estoy usando un Arduino Mega, pero supongo que en el UNO debe funcionar muy bien. También uso una LCD (obviamente) con un módulo I2C.

Quiero recalcar que es la primera parte; el primer avance, y no la versión final, pero la parte que hace posible al juego ya está terminada.

Motivación

Lo hice por diversión, ya que anteriormente había querido hacerlo pero me ganó el estrés o lo que sea que haya sido; pero ahora, con más experiencia intenté hacerlo de nuevo y avancé bastante.

Además, quería procrastinar al máximo para evitar hacer mi tarea, así que limpié mi viejo Arduino MEGA, conecté todo y comencé a programar.

Tamaño de los pixeles

La LCD que tengo es de 16 x 2 y permite usar 32 pixeles si lo vemos de ese modo. Pero, ¿Alguien imagina una serpiente que ocupe toda la pantalla y que sea así de gigante?

Aquí la LCD traída de mi post sobre LCD y Arduino:

Entonces una serpiente de ese tamaño no se vería nada bien; no habría espacio. Así que me pregunté…

¿Y si ocupo un pixel pequeño? ya que si nos fijamos, cada cuadro tiene 40 cuadros más pequeños dentro del mismo:

Cuadrícula de LCD
Cuadrícula de LCD

De este modo la serpiente tendría más espacio, ya que suponiendo que ocupara toda la pantalla se tendría el total de 1280 pixeles, con una altura de 16 y una anchura de 80.

A partir de este momento llamaremos pixel a los cuadros pequeños (los que en total son 1280).

Creando una forma personalizada

Pero la LCD no es un lienzo en donde puedas pintar cada pixel, ya que dibuja en un cuadro grande. La buena noticia es que se puede crear una forma personalizada definiendo un arreglo de tipo byte de longitud 8 en donde cada elemento representa un número binario indicando la distribución de la fila.

Por ejemplo, lo siguiente dibujará una sonrisa:

Un 1 es un pixel encendido, un 0 es un pixel apagado. Eso abre un montón de posibilidades ya que podríamos dibujar a la serpiente en cualquier lugar, incluso dibujar su cuerpo por partes.

Los bytes son 8 bits

Aunque tenemos un byte en la forma B00100 en realidad podría ser convertido a decimal por su posición, terminando en que B00100 es 4. Entonces podemos definir el dato (o la fila de la forma) como un byte o un decimal.

Si no entiendes bien, mira la conversión de números binarios a decimal.

Creando figura personalizada a partir de matriz

El juego de snake en Arduino cuenta con una matriz que es el tablero de juego. Al inicio la llené manualmente:

Pero como cada cuadro grande tiene 8 x 5 cuadros tuve que leer en porciones de ese tamaño:

Por cada cuadro gigante tenía que convertir cada fila en un byte y lo colocaba dentro del arreglo que formaría mi carácter personalizado más tarde:

Después de llenar el arreglo, lo almacenaba en la LCD y creaba un char, aumentando además un contador que llevaba el registro de qué número de figura iba:

Dibujando matriz en pantalla LCD

El código completo que dibuja la matriz, convirtiendo los ceros y unos en bytes, además de guardar y crear la figura es el siguiente:

Ese es el problema más complejo al que me enfrenté, porque aunque el código es pequeño, lo complejo fue llegar a la solución ya que las formas personalizadas se crean y dibujan en tiempo de ejecución.

Ahora que muestro este método también debo mostrar el que limpia la matriz:

Nos estamos quedando sin figuras personalizadas

Si has estado poniendo atención y siguiendo el código, te darás cuenta de que no se está usando toda la pantalla; y eso es porque solo podemos crear 8 figuras personalizadas (estoy limitado por la tecnología de mi tiempo.jpg)

Así que esa es la razón por la que no se ocupa toda la pantalla y solo 8 cuadros.

Primer dibujo de la serpiente

Pero bueno, con la matriz que se veía así (he marcado los unos para mostrar cómo se verá la serpiente):

Matriz para snake en Arduino

Se dibujaba lo siguiente:

Snake dibujada en LCD usando Arduino
Snake dibujada en LCD usando Arduino

Cuando llegué a este punto supe que lo había logrado y que era totalmente posible hacer el juego. Digamos que había terminado la rutina de programación para dibujar el escenario; lo demás era implementar la funcionalidad del juego.

Programando juego de la serpiente en Arduino

Ahora que había terminado la parte del dibujo en la matriz lo demás fue implementar la serpiente. Me inspiré del snake que hice con JavaScript, así que solo tenía que portarlo a C++, pues el Luis del pasado ya se había quebrado la cabeza programando al mismo.

Comenzamos definiendo a la serpiente con una clase, y un arreglo que iba a tener los pedazos de la serpiente:

El arreglo es de tamaño definido, el mismo tiene el valor de todos los pixeles que puede usar la serpiente. También necesité una variable para llevar el conteo de cuánto medía la serpiente (es decir, la longitud del arreglo era la capacidad, pero esta variable era la longitud)

Dirección de la serpiente

La dirección de la serpiente es importante; la he definido como 4 enteros usando #define. La función que cambia la dirección y valida las cosas es la siguiente:

Por defecto la dirección es la derecha, así que la serpiente avanza hacia la derecha.

Agregar pedazo a la serpiente

Cuando la serpiente come, se hace más grande. Veamos la función que agrega un pedazo junto con algunas constantes para validar los límites:

Movimiento de la serpiente

Ya tenemos dirección y tamaño de la serpiente, pero todavía no la estamos moviendo; es decir, no estamos actualizando sus coordenadas en cada paso. El método que lo hace es:

Valida la dirección y si se toca un borde lo convierte en un portal que mueve la serpiente al borde contrario.

Dibujar serpiente en matriz

La serpiente en sí ya está preparada para ser pintada en cualquier lugar, y la matriz del juego ya está siendo dibujada en la LCD; lo que falta aquí es agregar la serpiente dentro de la matriz y el método que lo hace es:

El puntaje

Como gran parte de la pantalla quedará desocupada se me ocurrió dibujar el puntaje ahí. Por el momento es un número estático, sin embargo es fácil de convertir en uno dinámico:

Movimiento artificial de la serpiente

Como no he colocado los métodos con los que se movería la serpiente decidí generar un número aleatorio para moverla. Primero alimenté al generador:

randomSeed(analogRead(0));

Después generé números aleatorios:

cambiarDireccion(random(0, 15));

Aunque la dirección solo va del 0 al 3 quise darle más aleatoridad generando números en un rango más amplio, para que no se moviera en cada paso y se viera más natural.

Poniendo todo junto

Serpiente en Arduino cambiando de dirección
Serpiente en Arduino cambiando de dirección

El código completo queda así:

Puedes verlo en mi perfil de GitHub. También tengo la demostración en mi canal de YouTube.

Conclusión

Como puedes ver, el juego no ha sido terminado; falta colocar la comida y mover a la serpiente con un dispositivo como botones o un joystick; cosa que haré en un futuro.

La razón por la que pausé el desarrollo es que no pude conectar los botones porque están en mal estado, pero tan pronto compre nuevos componentes terminaré la serpiente.

Actualización: ¡el juego está terminado! míralo aquí.

Te invito a seguirme para que, cuando publique la segunda parte seas el primero en enterarte.

También dejo enlaces para aprender más sobre Arduino, ver el juego de snake en JavaScript o ver el juego de la batalla naval en Arduino.

Encantado de ayudarte


Estoy disponible para trabajar en tu proyecto, modificar el programa del post o realizar tu tarea pendiente, no dudes en ponerte en contacto conmigo.

No te pierdas ninguno de mis posts

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.

Dejar un comentario