This article is currently in the process of being translated into Spanish (~99% done).
In this article series, we're building a complete Snake game from scratch. It makes sense to start with the Introduction and then work your way through the articles one by one, to get the full understanding.
If you want to get the complete source code for the game at once, to get started modifying and learning from it right now, consider downloading all our samples!
Adding food for the Snake
En esta etapa de la serie de artículos SnakeWPF, ahora tenemos un fondo de tablero de ajedrez como el área de juego, así como una serpiente verde de aspecto encantador que se mueve a su alrededor. Sin embargo, como se mencionó en la introducción, el objetivo del juego es que la serpiente coma algo de comida: ¡en nuestra versión serán manzanas rojas!
Así que ahora es el momento de comenzar a agregar algo de comida al área de juego. Lo haremos agregando aleatoriamente un círculo rojo en algún lugar dentro de los límites del lienzo de GameArea, pero debemos asegurarnos de que no lo estamos colocando en uno de los cuadrados ya ocupados por la serpiente en constante crecimiento. En otras palabras, uno de los aspectos más importantes de colocar una manzana en el área de juego es el código que decide la siguiente posición. Aquí está el código que usaremos para hacer eso:
private Point GetNextFoodPosition()
{
int maxX = (int)(GameArea.ActualWidth / SnakeSquareSize);
int maxY = (int)(GameArea.ActualHeight / SnakeSquareSize);
int foodX = rnd.Next(0, maxX) * SnakeSquareSize;
int foodY = rnd.Next(0, maxY) * SnakeSquareSize;
foreach(SnakePart snakePart in snakeParts)
{
if((snakePart.Position.X == foodX) && (snakePart.Position.Y == foodY))
return GetNextFoodPosition();
}
return new Point(foodX, foodY);
}
Asegúrese de agregar también esta línea en la parte superior de la declaración de la clase Window, junto con el resto de los campos / constantes:
public partial class SnakeWPFSample : Window
{
private Random rnd = new Random();
......
Entonces, para explicar rápidamente el código: una vez más usamos la constante SnakeSquareSize para ayudarnos a calcular la siguiente posición para nuestra comida, en combinación con la clase Random, que nos dará una posición X e Y aleatoria. . Una vez lo tenemos, revisamos todas las partes actuales de la serpiente y verificamos si su posición coincide con las coordenadas X e Y que acabamos de crear; si lo hacen, significa que hemos alcanzado un área actualmente ocupada por la serpiente y luego pedimos una nueva posición simplemente llamando al método nuevamente (haciendo de este un método recursivo).
Esto también significa que este método puede llamarse a sí mismo una cantidad ilimitada de veces y, en teoría, dar como resultado un bucle sin fin. Podríamos hacer algunas comprobaciones para eso, pero no debería ser necesario, ya que requeriría que la serpiente fuera tan larga que no quedara espacio vacío; mi apuesta es que el juego habrá terminado antes de que esto suceda.
Con eso en su lugar, estamos listos para agregar el código que agregará la comida en la posición recién calculada; lo haremos desde un método llamado DrawSnakeFood () . Gracias a todo el trabajo ya realizado por GetNextFoodPosition () , es bastante simple, pero primero, asegúrese de declarar el campo utilizado para guardar una referencia a la comida, así como el SolidColorBrush utilizado para dibujar el manzana, junto con el otro campo / declaraciones constantes:
public partial class SnakeWPFSample : Window
{
private UIElement snakeFood = null;
private SolidColorBrush foodBrush = Brushes.Red;
......
Aquí está la implementación del método:
private void DrawSnakeFood()
{
Point foodPosition = GetNextFoodPosition();
snakeFood = new Ellipse()
{
Width = SnakeSquareSize,
Height = SnakeSquareSize,
Fill = foodBrush
};
GameArea.Children.Add(snakeFood);
Canvas.SetTop(snakeFood, foodPosition.Y);
Canvas.SetLeft(snakeFood, foodPosition.X);
}
Según lo prometido, es muy simple: tan pronto como tengamos la posición, simplemente creamos una nueva instancia de Ellipse y una vez más usamos la constante SnakeSquareSize para asegurarnos de que tenga el mismo tamaño que los mosaicos de fondo así como cada parte de serpiente. Guardamos una referencia a la instancia de Ellipse en el campo snakeFood , porque la necesitamos más adelante.
Con eso en su lugar, realmente solo necesitamos llamar al método DrawSnakeFood () para ver el resultado de nuestro trabajo. Esto se hará en dos situaciones: al comienzo del juego y cuando la serpiente "come" la comida (hablaremos de eso más adelante). Por ahora, agreguemos una llamada en nuestro método StartNewGame () :
private void StartNewGame()
{
snakeLength = SnakeStartLength;
snakeDirection = SnakeDirection.Right;
snakeParts.Add(new SnakePart() { Position = new Point(SnakeSquareSize * 5, SnakeSquareSize * 5) });
gameTickTimer.Interval = TimeSpan.FromMilliseconds(SnakeStartSpeed);
// Draw the snake and the snake food
DrawSnake();
DrawSnakeFood();
// Go!
gameTickTimer.IsEnabled = true;
}
¡Eso es! Si ejecutas el juego ahora, deberías ver que la serpiente finalmente tiene algo de comida que perseguir:
Bastante trabajo para colocar un punto rojo en la pantalla, ¿verdad?
Resumen
En este artículo, finalmente hemos agregado algo de comida a la mesa para que la serpiente la atrape, pero aún queda trabajo por hacer: necesitamos poder controlar la serpiente, y necesitamos saber cuándo golpea algo (una pared, su propia cola o la comida). Más sobre eso en el próximo artículo.