El cuerpo de la serpiente

Creando el vector serpiente

Ya vimos la forma en que podemos comer manzanas y obtener puntos, pero tomando en cuenta que estamos haciendo un juego de snake, hay un punto muy importante: ¡La serpiente debe crecer! Para simular esto, crearemos un sencillo vector que contendrá nuestro cuerpo, para ello, sustituyamos el punto de nuestro personaje por las siguientes líneas:

	int maxlong = 500;
	Point serpiente[] = new Point[maxlong];

Notemos que creamos antes una variable que indica su máxima longitud, esto es para tener control de nuestro vector de forma dinámica, pues en muchos casos luego cambiamos la máxima longitud por una razón, y en estos caso tendríamos que modificar muchas partes del código. Por eso establecemos esta variable, para solo tener que cambiar esta en dado caso. Por el momento creo que 500 es un buen número (No creo si quiera que alguien llegue a este número, pero siempre es mejor dar de más a que se nos acabe el juego).

A continuación inicializaremos nuestro vector. Es importante hacer esto, pues si intentamos usar un vector que no está inicializado, nos mandará un error y se frenará el juego por completo (Creanme que lo llegué a experimentar mucho cuando comenzaba con esto). Agreguemos entonces el siguiente código a la función init:

		for (int i = 0; i < maxlong; i++)
		{
			serpiente[i] = new Point(0, 0);
		}

La primer línea crea una variable temporal "i" que se inicia en cero [int i = 0], la cual ejecutará el contenido en las llaves mientras su valor sea menor que maxlong [i < maxlong], subiendo su valor en uno cada vez que se ejecuta lo que está entre llaves [i++]. Esta es la forma que funciona un for, que sería lo mismo que hacer lo siguiente:

		serpiente[0] = new Point(0, 0);
		serpiente[1] = new Point(0, 0);
		serpiente[2] = new Point(0, 0);
		serpiente[3] = new Point(0, 0);
		serpiente[4] = new Point(0, 0);
		serpiente[5] = new Point(0, 0);
		serpiente[6] = new Point(0, 0);
		// ...

Y así mientras que sea menor que maxlong. Supongo que la parte dentro de las llaves ya no debo explicarla, ni que i sustituye al número correspondiente en cada caso.

Si eliminaste el punto personaje como te dije, posiblemente ya te alertó NetBeans que tienes decenas de errores, por lo que debemos modificar cada jugador.x por serpiente[0].x y así con las Y (Nota que serpiente[0] es el punto de la cabeza, y los demás números son el cuerpo). Claro hacerlo manual nos llevaría un tiempo y podríamos equivocarnos, por eso lo haremos de una forma más sencilla y automática:

Presiona Ctrl+H para activar la ventana de reemplazo (Tambien puedes hacerlo en la barra de herramientas "edición > reemplazar"). En "Find What" ponemos "personaje" y en "Replace With" ponemos "serpiente[0]". Si lo hemos hecho bien hasta ahora, al correrlo con Shift+F6, podremos jugar nuestro juego como hasta ahora sin ningún cambio.

Lo siguiente es dibujar el cuerpo de nuestra serpiente, para ello, en la función paint sustituiremos el dibujado de la serpiente por el siguiente:

		for (int i = 0; i < longitud; i++)
		{
			g.drawOval(serpiente[i].x, serpiente[i].y, 10, 10);
		}

Podremos notar que aquí dibujamos hasta longitud y no hasta maxlong, de esta forma solo dibujaremos las partes de la serpiente que estén activas. Por último, usa estas líneas para mover el cuerpo de la serpiente:

			//	Se mueve el serpiente
			for (int i = longitud; i > 0; i--)
			{
				serpiente[i].x = serpiente[i-1].x;
				serpiente[i].y = serpiente[i-1].y;
			}

Es importante que este lo pongamos antes de mover la cabeza. Como podrás notar, el movimiento se hace de atrás para adelante. Esto es importante pues de esta forma, una posición toma la de la siguiente, y así consecuentemente, creando esta apariencia de que se está moviendo la serpiente. Para ahora, tu código debe verse de esta forma:

import GameLib.Game;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Point;
import java.awt.Event;

public class snake extends Game
{
	// Aquí se declaran nuestras variables
	boolean PAUSE = true;
	int maxlong = 500;
	Point serpiente[] = new Point[maxlong];
	Point manzana = new Point (100, 100);
	int direccion = 1;
	int longitud = 1;
	
	public void init()
	{
		// Comienza descarga asíncrona de recursos pesados
		setBackground(Color.black);
		for (int i = 0; i < maxlong; i++)
		{
			serpiente[i] = new Point(0, 0);
		}
	}

	public void game()
	{
		// Aquí va el código de nuestro juego
		if (!PAUSE)
		{
			//	Se mueve el serpiente
			for (int i = longitud; i > 0; i--)
			{
				serpiente[i].x = serpiente[i-1].x;
				serpiente[i].y = serpiente[i-1].y;
			}
				
			//	Se mueve el serpiente[0]
			switch	(direccion)
			{
				case 0:
					serpiente[0].y -= 10;
					break;
				case 1:
					serpiente[0].x += 10;
					break;
				case 2:
					serpiente[0].y += 10;
					break;
				case 3:
					serpiente[0].x -= 10;
					break;
			}
			
			//	Salida del escenario
			if (serpiente[0].x < 0)
			{
				serpiente[0].x = this.getWidth()-10;
			}
			if (serpiente[0].x > this.getWidth()-10)
			{
				serpiente[0].x = 0;
			}
			if (serpiente[0].y < 10)
			{
				serpiente[0].y = this.getHeight()-10;
			}
			if (serpiente[0].y > this.getHeight()-10)
			{
				serpiente[0].y = 10;
			}
			
			//	Colisión serpiente[0]-Manzana
			if (serpiente[0].x == manzana.x && serpiente[0].y == manzana.y)
			{
				longitud += 1;
				manzana.x = (int)(Math.random() * (this.getWidth()/10 - 1)) * 10;
				manzana.y = (int)(Math.random() * (this.getHeight()/10 - 2)) * 10 + 10;
			}
		}
	}
	
	public void paint(Graphics g)
	{
		// Aquí se dibujan todos los objetos
		g.clearRect(0, 0, this.getWidth(), this.getHeight());
		g.setColor(Color.green);
		for (int i = 0; i < longitud; i++)
		{
			g.drawOval(serpiente[i].x, serpiente[i].y, 10, 10);
		}
		g.setColor(Color.red);
		g.drawOval(manzana.x, manzana.y, 10, 10);
		g.setColor(Color.white);
		g.drawString("Longitud: " + longitud, 10, 10);
		if (PAUSE)
		{
			g.drawString("PAUSA", this.getWidth()/2-20, this.getHeight()/2);
		}
	}
	
	public boolean keyDown(Event e, int key)
	{
		switch(key)
		{
			//	Cambia el rumbo de la serpiente
			case Event.UP:
				direccion = 0;
				break;
			case Event.RIGHT:
				direccion = 1;
				break;
			case Event.DOWN:
				direccion = 2;
				break;
			case Event.LEFT:
				direccion = 3;
				break;
			case Event.ENTER:
				PAUSE = !PAUSE;
				break;
		}
		return true;
	}
}

Corramos el código con Shift+F6, y podremos comprobar que la serpiente y crece conforme va comiendo manzanas.

Evitando las medias vueltas

Quizá ya hayamos notado que hay algo que no debería ser así: podemos movernos a todos lados, incluyendo dar media vuelta, cosa que no debería ocurrir en el juego original (Y menos cuando detectemos la autocolisión, que eso daría una muerte inmediata). Por tanto, corregiremos esto en el teclado, limitando los movimientos para que no pueda dar media vuelta, quedando nuestra función de la siguiente forma:

	public boolean keyDown(Event e, int key)
	{
		switch(key)
		{
			//	Cambia el rumbo de la serpiente
			case Event.UP:
				if (direccion != 2)
				{
					direccion = 0;
				}
				break;
			case Event.RIGHT:
				if (direccion != 3)
				{
					direccion = 1;
				}
				break;
			case Event.DOWN:
				if (direccion != 0)
				{
					direccion = 2;
				}
				break;
			case Event.LEFT:
				if (direccion != 1)
				{
					direccion = 3;
				}
				break;
			case Event.ENTER:
				PAUSE = !PAUSE;
				break;
		}
		return true;
	}

Tras corregir esta sección, demos Shift+F6. Como podremos notar, hemos corregido las medias vueltas, y estamos listos para seguir con nuestro curso. Hasta el momento, tu juego debe verse así:

[Un proyecto de Ayotli Diseño Web]