TOC

This article has been localized into Spanish by the community.

Creando un juego: SnakeWPF:
Chapter introduction:

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!

Creando y moviendo la serpiente

En el último artículo, hicimos un área para la serpiente en nuestra implementación del juego SnakeWPF para moverse alrededor. Con eso en su lugar, es tiempo de crear la serpiente y hacerla moverse alrededor del área. Una vez mas, usaremos la clase Rectanglede WPF para formar una serpiente de cierta longitud, con cada elemento siendo del mismo alto y ancho que los cuadrados de fondo, o como lo llamamos: La constante SnakeSquareSize

Creando la serpiente.

Dibujaremos la serpiente con un método llamado DrawSnake() - el método es de hecho muy simple, pero requiere un poco de trabajo extra, incluyendo una nueva clase llamada SnakePart, también como algunos campos extras en la clase Window. Empecemos con la clase SnakePart, el cual normalmente se define en un nuevo archivo (SnakePart.cs, por ejemplo):

using System.Windows;

namespace WpfTutorialSamples.Games
{
    public class SnakePart
    {
public UIElement UiElement { get; set; }

public Point Position { get; set; }

public bool IsHead { get; set; }
    }
}

Esta clase simple contendrá información sobre cada parte de la serpiente: ¿Dónde esta el elemento posicionado en nuestra área de juego, y ¿es esta la parte de la cabeza de la serpiente o no? Usaremos todo esto luego, pero primero, dentro de nuestra clase Window, necesitamos definir un par de campos para usarlos en nuestro método DrawSnake() ( y luego en otros métodos también).

public partial class SnakeWPFSample : Window  
{  
    const int SnakeSquareSize = 20;  

    private SolidColorBrush snakeBodyBrush = Brushes.Green;  
    private SolidColorBrush snakeHeadBrush = Brushes.YellowGreen;  
    private List<SnakePart> snakeParts = new List<SnakePart>();  
    ......

Definimos dos SolidColorBrushes, uno para el cuerpo y otro para la cabeza. También necesitamos definir una List<SnakePart>, la cual mantendrá una referencia a todas las partes de la serpiente. Con eso en su lugar, ahora podemos implementar nuestro método DrawSnake():

private void DrawSnake()
{
    foreach(SnakePart snakePart in snakeParts)
    {
if(snakePart.UiElement == null)
{
    snakePart.UiElement = new Rectangle()
    {
Width = SnakeSquareSize,
Height = SnakeSquareSize,
Fill = (snakePart.IsHead ? snakeHeadBrush : snakeBodyBrush)
    };
    GameArea.Children.Add(snakePart.UiElement);
    Canvas.SetTop(snakePart.UiElement, snakePart.Position.Y);
    Canvas.SetLeft(snakePart.UiElement, snakePart.Position.X);
}
    }
}

Como puedes ver, este método no es particularmente complicado: Iteramos sobre la lista snakeParts, y por cada parte, revisamos sí un UIElement ha sido especificado por esta parte - sí no, lo creamos (representado por un Rectangle) y lo agregamos al área de juego, mientras guardamos una referencia a este en la propiedad UiElement de la instancia SnakePart. Nota como usamos la propiedad Position de la instancia SnakePart para colocar el elemento actual dentro del GameArea Canvas.

El truco aquí es desde luego que la parte actual de la serpiente sea definida donde sea, permitiéndonos agregar una o varias partes de la serpiente, darles la posición deseada y luego tener al método DrawSnake() haciendo el trabajo por nosotros. Haremos eso como parte del mismo proceso usado para mover la serpiente.

Moviendo la serpiente

Para alimentar algo al método DrawSnake(), necesitamos poblar la lista snakeParts. Esta lista constantemente sirve como la base de donde dibujar cada elemento de la serpiente, asó que también la estaremos usando para crear movimiento para la serpiente. El proceso de mover la serpiente básicamente consiste en agregar nuevos elementos a esta, en la dirección donde la serpiente esta moviéndose, y luego borrar la última parte de la serpiente. Esto hace parecer como si se estuviera moviendo cada elemento, pero de hecho, solo estamos agregando nuevos mientras borramos los viejos.

Así que necesitaremos un método MoveSnake(), el cual te enseñare en un minuto, pero primero, necesitamos agregar mas al inicio de nuestra definición de clase Window:

public partial class SnakeWPFSample : Window  
{  
    const int SnakeSquareSize = 20;  

    private SolidColorBrush snakeBodyBrush = Brushes.Green;  
    private SolidColorBrush snakeHeadBrush = Brushes.YellowGreen;  
    private List<SnakePart> snakeParts = new List<SnakePart>();  

    public enum SnakeDirection { Left, Right, Up, Down };  
    private SnakeDirection snakeDirection = SnakeDirection.Right;  
    private int snakeLength;  
    ......

Hemos agregado una nueva enumeración, llamada SnakeDirection , que debería explicarse por sí misma. Para eso, tenemos un campo privado para contener la dirección actual real ( snakeDirection ), y luego tenemos una variable entera para contener la longitud deseada de la serpiente ( snakeLength ) Con eso en su lugar, estamos listos para implementar el método MoveSnake () . Es un poco largo, así que agregué comentarios en línea a cada una de las partes importantes:

private void MoveSnake()  
{  
    // Remove the last part of the snake, in preparation of the new part added below  
    while(snakeParts.Count >= snakeLength)  
    {  
GameArea.Children.Remove(snakeParts[0].UiElement);  
snakeParts.RemoveAt(0);  
    }  
    // Next up, we'll add a new element to the snake, which will be the (new) head  
    // Therefore, we mark all existing parts as non-head (body) elements and then  
    // we make sure that they use the body brush  
    foreach(SnakePart snakePart in snakeParts)  
    {  
(snakePart.UiElement as Rectangle).Fill = snakeBodyBrush;  
snakePart.IsHead = false;  
    }  

    // Determine in which direction to expand the snake, based on the current direction  
    SnakePart snakeHead = snakeParts[snakeParts.Count - 1];  
    double nextX = snakeHead.Position.X;  
    double nextY = snakeHead.Position.Y;  
    switch(snakeDirection)  
    {  
case SnakeDirection.Left:  
    nextX -= SnakeSquareSize;  
    break;  
case SnakeDirection.Right:  
    nextX += SnakeSquareSize;  
    break;  
case SnakeDirection.Up:  
    nextY -= SnakeSquareSize;  
    break;  
case SnakeDirection.Down:  
    nextY += SnakeSquareSize;  
    break;  
    }  

    // Now add the new head part to our list of snake parts...  
    snakeParts.Add(new SnakePart()  
    {  
Position = new Point(nextX, nextY),  
IsHead = true  
    });  
    //... and then have it drawn!  
    DrawSnake();  
    // We'll get to this later...  
    //DoCollisionCheck();      
}

Con eso en su lugar, ahora tenemos toda la lógica necesaria para crear movimiento para la serpiente. Observe cómo usamos constantemente la constante SnakeSquareSize en todos los aspectos del juego, desde dibujar el patrón de tablero de ajedrez de fondo hasta crear y agregar a la serpiente.

Resumen

Desde el primer artículo, ahora tenemos antecedentes y desde este artículo, tenemos el código para dibujar y mover la serpiente. Pero incluso con esta lógica en su lugar, todavía no hay movimiento real o incluso una serpiente real en el área de juego, porque aún no hemos llamado a ninguno de estos métodos.

El llamado a la acción para el movimiento de la serpiente debe provenir de una fuente que se repite, porque la serpiente debe moverse constantemente mientras el juego se esté ejecutando: en WPF, tenemos la clase DispatcherTimer que nos ayudará con eso. El movimiento continuo de la serpiente, usando un temporizador, será el tema del próximo artículo.

This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!