Mejora las skills de programación creando un juego de snake en C#

¿Quieres mejorar tu lógica de programación sin depender de frameworks? He notado que a muchos de mis colegas se les dificulta llevar a cabo sus desarrollos si no existe una librería(Nuget, Plugin, Componente, Paquete, Etc…) que haga exactamente lo que se espera como resultado final y es algo que en ocasiones resulta frustrante. No me malinterpreten, las librerías son herramientas valiosas, pero muchas veces, un desarrollo que puede ser sencillo se complica por el afán de ahorrar trabajo y esfuerzo.

Voy a demostrar que con solo conceptos básicos puedes construir algo sorprendente. Haciendo uso de una matriz (o array bidimensional) vamos a crear un juego de snake, desde el motor del juego hasta la parte que renderiza la imagen que se muestra en pantalla y también añadiremos sonidos.



¿Cómo lograrlo?

Este ejemplo lo realizaremos en C# de .Net y solo utilizaremos librerías del propio .Net.
Vamos a hacer uso de una matriz como componente principal para darle vida a nuestro snake. Cada elemento de la matriz tiene un número al que está asociado, llamado "índice", que permite acceder a él.


Fácilmente podemos comparar la representación gráfica de una matriz con un tablero de ajedrez; supongamos que tenemos una peón en la posición [2, 2] y queremos que este se mueva una casilla a nuestra izquierda. Podemos observar que para simular este movimiento a la izquierda la nueva posición de nuestro peón sería [2, 1], por lo tanto, podemos asumir que los movimientos a la izquierda restan y los movimientos a la derecha suman, igual que en la recta numérica, y, del mismo modo para los movimientos hacia arriba y hacia abajo. Aunque la comparación con el tablero de ajedrez es un buen ejemplo, debemos ponernos más técnicos para poder comprender la idea general.

Una comparación más acertada de nuestra matriz o de nuestro tablero de ajedrez sería con el cuarto cuadrante del plano cartesiano:


Una vez en el plano cartesiano, sabemos que para ubicar un punto debemos usar las variables x y y, que a su vez, serán el índice de nuestra matriz:


La gran diferencia entre el plano cartesiano y una matriz, es que en la matriz podemos almacenar diferentes valores en cada uno de los índices o posiciones, por ejemplo tenemos los siguientes valores almacenados en los índices dados:

[4, 3] = 2
[0, 2] = 1
[0, 3] = 1
[2, 1] = 0

se pueden representar en el plano cartesiano/matriz de la siguiente manera:


Ahora, volviendo al ejemplo inicial, si queremos que el número “2” se mueva hacia la izquierda podríamos utilizar la siguiente fórmula

matriz[x - 1, y] = 2

En nuestro caso concreto quedaría de esta manera:

matriz[3 - 1, 4] = 2
matriz[2, 4] = 2

Vemos que el nuevo índice o posición para el número 2 es [2, 4]. Lo que representaría un movimiento a la izquierda en comparación a la posición original: [3, 4], y esto mismo aplicaría para las cuatro direcciones tal cual se describe en el ejemplo inicial:
  • Movimiento a la izquierda: 
matriz[x - 1, y]
  • Movimiento a la derecha: 
matriz[x + 1, y]
  • Movimiento hacia arriba: 
matriz[x, y – 1]
  • Movimiento hacia abajo: 
matriz[x, y + 1]

Gameplay

Esto en principio será el concepto base de nuestro juego de snake, ahora es hora de pasar al gameplay. Todos conocemos la mecánica del juego de snake: se controla la dirección de una serpiente que avanza automáticamente; al iniciar el juego la longitud de esta serpiente es mínima y para aumentar dicha longitud se debe hacer pasar la cabeza de la serpiente sobre un punto que se genera en una posición aleatoria del mapa del juego. Una vez aumenta la longitud de la serpiente aumenta la dificultad debido a que debemos evitar que la cabeza colisione con el cuerpo de la misma serpiente o con los bordes del mapa. De ocurrir cualquiera de estas dos situaciones sería el fin del juego.

Adicional al gameplay, también debemos pensar en cómo vamos a renderizar los gráficos de nuestro juego. Esto a primera vista puede parecer algo un tanto complicado, pero debemos recordar que una imagen es una colección de pixeles que están organizados en una matriz y la base de nuestro juego ¡es una matriz!, teniendo esto en cuenta lo único que debemos hacer para “renderizar” nuestro juego es hacer una copia de la matriz en una imagen. En C# podemos utilizar la librería System.Drawing.Bitmap que nos permite especificar el color de un pixel en un índice dado.

Hay que tener en cuenta que un índice de nuestra matriz equivale a un píxel de la imagen, por lo que no podríamos distinguir la serpiente en la pantalla. Debemos usar una escala al renderizar la imagen, ejemplo: si usamos una escala de 10, cada índice de nuestra matriz equivaldría a 10 píxeles de la imagen que vamos a mostrar en pantalla.

Otro aspecto importante que debemos tener en cuenta es el tiempo. Debemos determinar cuántos frames por segundo va tener nuestro juego, o mejor dicho, cada cuánto vamos a ejecutar las validaciones y las acciones del snake. El intervalo entre un frame y otro lo vamos a conocer como delta time y lo vamos a definir en milisegundos. En cada uno de estos intervalos vamos a hacer avanzar la serpiente un paso a la vez, pero no sin antes verificar que en ese paso siguiente no vaya a colisionar con los bordes del mapa o con su propio cuerpo y del mismo modo valoraremos si ha “comido” un punto para aumentar la longitud.

Código

Para ver el código de ejemplo revisa el repositorio en GitHub Aquí.


Comentarios