TOC

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

Creating a Game: 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!

Creating the game area

To create our SnakeWPF game, we'll start by creating the map. It will be a confined area, where the snake has to move within - a snake pit, if you will. I have decided that my snake pit should look like a chess board, made up equally sized squares, which will have the same dimensions as the body of the snake. We'll create the map in two iterations: Some of it will be laid out in XAML, because it's easy, while we'll draw the background squares in Code-behind, because it's repetitive and dynamic.

Игровое поле в XAML

Что же, давайте начнем с XAML - простого Window с панелью Canvas внутри контрола Border для создания ограниченного поля.

<Window x:Class="WpfTutorialSamples.Games.SnakeWPFSample"  
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"  
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"  
xmlns:local="clr-namespace:WpfTutorialSamples.Games"  
mc:Ignorable="d"  
Title="SnakeWPF - Score: 0" SizeToContent="WidthAndHeight">  
    <Border BorderBrush="Black" BorderThickness="5">  
<Canvas Name="GameArea" ClipToBounds="True" Width="400" Height="400">  

</Canvas>  
    </Border>  
</Window>

Наша игра в данный момент выглядит так:

Мы используем панель Canvas в качестве игрового поля поскольку она позволит нам не просто добавлять элементы управления к ней, но и расположить все элементы на свои позиции. Мы используем это позже, а теперь обратите внимание на следующее:

  • В Window не определяются ни ширина ни высота-они будут определены в Canvas, поскольку это те параметры, которыми мы должны возможность полностью управлять. Затем мы удостоверимся что Window изменит свой размер в соответствии с содержимым установив SizeToContent свойство как WidthAndHeight. Если бы мы вместо этого определили ширину/ высоту для Window, доступное пространство внутри него будет зависеть от размера границы окон, установленного в операционной системе , который может зависеть от темы и т.д.
  • Мы устанавливаем свойство ClipToBounds как True для Canvas - это важно, поскольку в противном случае элементы управления, которые мы добавляем могут вылазить за границы панели Canvas .

Отрисовка заднего фона из Code-behind

Как упоминал ранее, я хочу сделать задний фон в в виде шахматного поля. Оно состоит из множества квадратов, так что более простым путем будет добавить их в Code-behind (или использовать изображение, но в такой вариант будем менее динамичным!). Нам требуется сделать это в момент инициализации/рендеринга всех элементов управления внутри Window . И, к счастью, Window обладает событием на этот случай: это событие ContentRendered. Мы подпишемся на него при объявлении Window в XAML:

Title="SnakeWPF - Score: 0" SizeToContent="WidthAndHeight" ContentRendered="Window_ContentRendered"

А теперь перейдем к Code-behind и начнем. Прежде всего нам требуется с размером отрисовываемой змеи, квадратиков заднего фона и т.д. Это может быть выполнено с помощью свойств класса нашего Window:

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

Теперь внутри обработчика события ContentRendered мы вызовем метод DrawGameArea(), который будет делать всю тяжелую работу. Это выглядит вот так:

private void Window_ContentRendered(object sender, EventArgs e)  
{  
    DrawGameArea();  
}  

private void DrawGameArea()  
{  
    bool doneDrawingBackground = false;  
    int nextX = 0, nextY = 0;  
    int rowCounter = 0;  
    bool nextIsOdd = false;  

    while(doneDrawingBackground == false)  
    {  
Rectangle rect = new Rectangle  
{  
    Width = SnakeSquareSize,  
    Height = SnakeSquareSize,  
    Fill = nextIsOdd ? Brushes.White : Brushes.Black  
};  
GameArea.Children.Add(rect);  
Canvas.SetTop(rect, nextY);  
Canvas.SetLeft(rect, nextX);  

nextIsOdd = !nextIsOdd;  
nextX += SnakeSquareSize;  
if(nextX >= GameArea.ActualWidth)  
{  
    nextX = 0;  
    nextY += SnakeSquareSize;  
    rowCounter++;  
    nextIsOdd = (rowCounter % 2 != 0);  
}  

if(nextY >= GameArea.ActualHeight)  
    doneDrawingBackground = true;  
    }  
}

Как ранее упоминалось, данная глава требует немного лучшего знания C# чем остальные главы в этом руководстве, так что я не буду слишком углубляться в детали, а предоставлю общее описание: внутри цикла while мы непрерывно создаем экземпляры элемента управления Rectangle и добавляем их на панель Canvas (GameArea). Мы закрашиваем их с помощью кисти белого либо черного цвета, а ширина и высота определяются нашей постоянной SnakeSquareSize поскольку мы хотим что бы ячейки были квадратными. В каждой итерации мы используем nextX и nextY что бы определять момент перехода на следующую строку (достижение правой границы поля) а затем и момент остановки (когда мы достигли нижней строки И правой границы поля одновременно)

И вот результат:

Заключение

In this article, we have defined the XAML used to host all the game content, and we have "painted" a checkerboard pattern on the game area, by adding WPF Rectangle controls in black and white to it. The next step will be to start adding the actual snake, as well as the food it will be eating.

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!