TOC

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

Tworzenie Gry: 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

Aby stworzyć naszego Snake'a w WPF, zaczniemy od stworzenia mapy. Będzie to zamknięty obszar, wewnątrz którego będzie poruszał się wąż - jeżeli chcesz możesz to nazywać gniazdem węża. Zdecydowałem, że będzie ono wyglądać jak plansza do szachów, wykonana z jednakowych kwadratów w dwóch kolorach, o rozmiarze jednego odcinka ciała naszego węża. Mapę będziemy tworzyć w dwóch iteracjach: Część z niej ułożymy w XAML, bo to proste, natomiast kwadraty tła zaprogramujemy w kodzie za widokiem (Code-behind), bo są powtarzalne i dynamiczne.

Obszar gry w XAML

Tak więc zaczynamy w XAML - proste okno z panelem Canvas, wewnątrz kontrolki Border, pozwoli nam stworzyć zamknięty obszar:

<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>

Nasza gra wygląda teraz tak:

Użyliśmy kontrolki Canvas do stworzenia obszaru gry, ponieważ pozwoli nam to w przyszłości dodać do niej pozostałe potrzebne kontrolki. Zajmiemy się tym w późniejszym czasie, na tę chwilę zwróć uwagę na następujące rzeczy:

  • Nie definiowaliśmy wysokości ani szerokości dla Okna gry samego w sobie - zamiast tego, zdefiniowaliśmy te parametry dla kontrolki Canvas, ponieważ to nad nią chcemy mieć pełną kontrolę. Okno gry automatycznie dopasowuje się do zawartości naszej gry dzięki zdefiniowaniu parametru SizeToContent na wartość WidthAndHeight. Gdybyśmy zamiast tego zdefiniowali szerokość/wysokość Okna, dostępne w nim miejsce zależałoby od tego, ile system operacyjny przeznaczył na obramowanie Okna, co może zależeć od motywów itp.
  • Ustawiliśmy także dla kontrolki Canvas wartość właściwości ClipToBounds na True - jest to ważne, ponieważ dzięki temu kolejne kontrolki, które będziemy dodawać będą umiejscowione wewnątrz panelu Canvas, a nie poza jego granicami.

Definiowanie tła w kodzie za widokiem (Code-behind)

Jak już wspomniałem, tłem naszej gry będzie szachownica. Składa się ona z wielu kwadratów w określonym szyku, z tego względu prostsze będzie zdefiniowanie jej w kodzie za widokiem (Code-behind) (lub użycie obrazu, jednak wtedy tło nie będzie dynamiczne!). Chcemy aby tło było renderowane po tym, gdy wszystkie kontrolki w Oknie gry zostało już zainicjowane/wyrenderowane, na szczęście jest możliwe zdefiniowanie dla Okna takiego zdarzenia: ContentRendered. Zasubskrybujemy to zdarzenie w deklaracji Okna:

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

Teraz przejdźmy do kodu za widokiem (Code-behind) i zaczynajmy! Pierwsze co musimy zrobić to zdefiniować stałą, którą będziemy używać do rysowania węża, kwadratów szachownicy itp. Możemy to zrobić na samej górze klasy naszego Okna:

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

Następnie, w naszym zdarzeniu ContentRendered wywołajmy metodę DrawGameArea(), która zrobi dla nas całą ciężką robotę. Metoda ta wygląda tak:

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

Jak wcześniej było wspomniane, artykuły w tej sekcji wymagają trochę większej wiedzy z C#, niż reszta artykułów tego poradnika, tak więc nie będziemy tłumaczyć każdej linijki tego kody, jednak to jest skrócony opis co ta metoda robi: Wewnątrz pętli while, stale tworzone są instancje kontrolki Rectangle i dodawane są do naszej kontrolki Canvas (GameArea). Wypełniamy te kontrolki Białym lub Czarnym pędzlem (Brush), a wielkość jest definiowana jest naszą stałą SnakeSquareSize. Szerokość i Wysokość są takie same, ponieważ chcemy aby były to kwadraty. Z kazdą iteracją, używamy zmiennych nextX oraz nextY aby kontrolować, rozmieszczenie kwadratów, kiedy przejść do kolejnej lini (gdy dotrzemy do prawej granicy), oraz kiedy zakończyć pętle rysowania (gdy dotrzemy jednocześnie do prawej ORAZ dolnej granicy).

Tutaj mamy rezultat:

Podsumowanie

W tym artykule zdefiniowaliśmy XAML używany jako baza do naszej całej zawartości gry, oraz "namalowaliśmy" szachownicę jako tło obszaru naszej gry, poprzez dodanie kontrolek Rectangle pomalowanych na biały i czarny kolor. W następnym kroku dodamy węża, oraz pożywienie, które będziemy nim zbierać.


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!