TOC

This article is currently in the process of being translated into Portuguese (~98% done).

Criando um jogo: 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!

Adding food for the Snake

Neste ponto da série de artigos SnakeWPF, agora temos um tabuleiro de damas como pano de fundo da área do jogo, bem como uma adorável cobra verde movendo-se nele. No entanto, tal como mencionado na introdução, o propósito do jogo é de que a cobra coma algo - na nossa versão serão maças vermelhas.

Então agora é o momento para incluir algum alimento na área do jogo. Faremos isso adicionando, de forma randômica, um círculo vermelho em algum lugar dentro dos limites do GameArea Canvas, mas precisamos ter certeza de que não colocaremos o círculo em um dos quadrados já ocupados pela cobra que cresce continuamente. Em outras palavras, um dos aspectos mais importantes de colocar uma maçã na área do jogo é o código que decide a nova posição. Aqui está o código que usaremos para fazer exatamente isso:

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);
}

Não esqueça de incluir esta linha no topo da declaração de classe Window, junto com o resto dos campos/constantes:

public partial class SnakeWPFSample : Window
{
    private Random rnd = new Random();
    ......

Então, para rapidamente explicar o código: Usamos novamente a constante SnakeSquareSize para nos ajudar a calcular a próxima posição de nosso alimento, em conjunto com a classe Random, o que nos dará uma posição X e Y randômica. Após obter-la, passamos por todas as partes atuais da cobra e verificamos se suas posições são iguais às coordenadas X e Y que acabamos de criar - se sim, isso significa que escolhemos uma área atualmente ocupada pela cobra e então solicitamos uma nova posição simplesmente chamando o método novamente (tornando este método recursivo).

Isto também significa que este método pode chamar a si mesmo um número ilimitado de vezes e, em teoria, resultar em um laço infinito. Poderíamos fazer alguma verificação para isso, mas não será necessário, já que isso exigiria que a cobra fosse tão comprida que já não houvesse nenhum espaço vazio - aposto que o jogo já terá acabado antes que isto aconteça.

Com isso no lugar, estamos prontos para adicionar o código que incluirá o alimento na nova posição calculada - faremos isso a partir de um método chamado DrawSnakeFood(). Graças a todo o trabalho feito por GetNextFoodPosition(), é bem simples, mas primeiro, assegure-se de declarar o campo usado para fazer referencia ao alimento, bem como o pincel SolidColorBrush usado para desenhar a maçã, junto com as outras declarações de campos/constantes:

public partial class SnakeWPFSample : Window  
{  
    private UIElement snakeFood = null;  
    private SolidColorBrush foodBrush = Brushes.Red;
    ......

Aqui está a implementação do 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);
}

Como prometido, é bem simples - tão logo temos a posição, simplesmente criamos uma nova instância Ellipse e de novo usamos a constante SnakeSquareSize para ter certeza de que tem o mesmo tamanho de cada quadrado do fundo bem como de cada parte da cobra. Salvamos uma referencia para a instância Ellipse no campo snakeFood, porque precisaremos disso mais tarde.

Com isso no lugar, precisamos apenas chamar o método DrawSnakeFood() para ver o resultado de nosso trabalho. Isto será feito em duas situações: No início do jogo e quando a cobra "come" o alimento (mais sobre isso depois). Por enquanto, vamos adicionar uma chamada no nosso 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;
}

É isso! Se você rodar o jogo agora, verá que a cobra finalmente tem algum alimento para perseguir:

Uma boa quantidade de trabalho para colocar um ponto vermelho na tela, certo?

Resumo

Neste artigo, finalmente adicionamos algum alimento para que a cobra pegue, mas ainda temos trabalho a fazer: Precisamos ser capazes de controlar a cobra, e precisamos saber quando bate em algo (a parede, a própria cauda ou o alimento). Mais sobre isso no próximo artigo.

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!