Tetris w C#

Tetris w C#

Autor: Marcin Hałaczkiewicz

Opublikowano: 4/26/2007, 12:00 AM

Liczba odsłon: 112139

W tym artykule stworzymy jedną z najstarszych gier komputerowych - Tetrisa. Będziemy mówić o rysowaniu, napiszemy klasę, której właściwości będą dziedziczyć klasy pochodne. Dodatkowo zbudowany element bardzo nam się przyda w kolejnej części, w której napiszemy Snake'a - popularną gierkę spotykaną na komórkach. Pokażemy również jak dodać do formularza kontrolkę "ręcznie", czyli nie używając graficznego edytora Visual Studio.

Jak zwykle na początek tworzymy nowy projekt, nadajemy mu nazwę i wykonujemy pozostałe podstawowe czynności. Zmieniamy rozmiary naszej formy na 354x333, właściwość MaximizeBox ustawiamy na false, a AutoSizeMode na GrowAndShrink. Wszystko to po to, aby nie można było zmaksymalizować ani zmienić wymiarów formy. Dodajmy teraz nowy pusty plik do projektu, nazwijmy go Kratka. Umieścimy w nim klasę Kratka, która będzie reprezentować podstawowy obiekt w naszej grze - pojedynczy element klocka. Aby można było korzystać z funkcji do obsługi grafiki u góry pliku piszemy:

using System.Drawing;

Następnie wyposażamy klasę w następujące zmienne:

private Rectangle kwadrat;
private Color kolor;
private int x, y;
private bool zajeta;
public const int wymiar = 15;

x i y to położenie elementu klocka. Jest ono podawane w kratkach, a nie we współrzędnych (np. x = 2 i y = 3 oznacza trzecią kratkę od lewej i czwartą od góry, trzecia i czwarta bo liczymy od 0). kwadrat służy do przechowywania prostokąta, który reprezentuje element klocka, w kolor pamiętana jest barwa kratki. Zmienna zajeta mówi nam, czy kratka jest wypełniona, czy też wolna. Natomiast wymiar to stała oznaczająca wysokość i szerokość kratki w pikselach. Wyposażamy naszą klasę w zestaw trzech konstruktorów. Pierwszy to konstruktor domyślny nie przyjmujący żadnych argumentów. Tworzy on nowy prostokąt, ustawia współrzędne kratki na (0, 0) oraz nadaje kratce domyślny czarny kolor. Drugi robi to samo ale zmienia współrzędne zgodnie z naszym życzeniem, trzeci dodatkowo nadaje żądany kolor. Wyposażamy klasę również w funkcje do sprawdzenia stanu kratki (wolna/zajęta), do ustawienia jej koloru oraz do odczytania go. Oprócz tego kratka musi posiadać funkcje pozwalające na wyświetlenie elementu oraz ukrycie (czyli wypełnienie określonym kolorem, takim jak tło), więc tworzymy funkcje rysuj i kasuj. Możemy już zamknąć plik Kratka.cs, nie będziemy go więcej modyfikować.

Zajmiemy się teraz zaprogramowaniem figur do gry. Tworzymy nowy plik i nazywamy go figura. W nim piszemy klasę figura. Struktura ta będzie reprezentować klocek używany do gry w tetrisa. Będzie on miał swój kolor, współrzędne oraz kąt o jaki jest obrócony. W klasie będzie znajdować się jedynie konstruktor bezparametrowy oraz funkcja obracająca figurę o odpowiedni kąt. Konstruktor tworzy losowy element oraz nadaje jej losowy kolor. Poszczególne kratki figury przechowywane są w tablicy bool[4, 4]. W funkcji odwrocona zapisane są współrzędne wszystkich klocków w każdej pozycji, dzięki czemu gdy chcemy obrócić element wystarczy, że podamy w argumencie nowy kąt klocka spośród 0, 90, 180 i 270 stopni, a funkcja zwróci figurę ustawioną pod odpowiednim kątem.

Teraz dodajemy do projektu kolejny plik i nazywamy go plansza. Tworzymy w nim klasę o nazwie Plansza, która jest wyprowadzona z System.Windows.Forms.Panel. Plansza posłuży nam do graficznej reprezentacji okna gry. W jej wnętrzu znajdzie się tablica kratek oraz podstawowe funkcje do jej obsługi. Będą to rysujkratke, które narysuje odpowiednią kratkę, gdy jest to możliwe. Kolejna to sprawdzwymiary, która informuje nas o tym czy podane współrzędne mieszczą się na planszy. target="_blank"odswiez i reset odpowiednio przerysują lub skasują wszystkie kratki. Plansza posiada także dwa konstruktory, które odpowiednio ją inicjują.

Następny plik w naszym projekcie nazywa się Plansza_Tetris. W jego wnętrzu umieścimy definicję klasy Plansza_Tetris, którą wyprowadzimy z klasy Plansza. Będzie ona właściwą planszą do tetrisa. Klasa Plansza_Tetris zawiera jedynie zmienne do przechowywania współrzędnych lewego górnego rogu figury, oraz samą figurę. Za to jest wyposażona w sporo funkcji. Pierwsza z nich rysujfigure służy do wyświetlenia wybranej figury w konkretnym miejscu planszy. Kolejne trzy: wdol, wlewo i wprawo pozwalają przesunąć klocek o jeden wiersz lub kolumnę w wybranym kierunku. Z kolei obroc_w_lewo i obroc_w_prawo służą do obrotu klocka o dziewięćdziesiąt stopni w określoną stronę. Funkcja wyczysclinie czyści (zmazuje) wskazany wiersz, a przesunlinie przesuwa linie o jeden wiersz w dół. Metoda linie sprawdza czy powstały jakieś pełne linie na planszy. Jeśli tak, to czyści je i odpowiednio przesuwa pozostałe. Będzie ona wywoływana, gdy klocek opadnie. Na koniec jeszcze funkcja getfigura, która zwraca aktualnie kontrolowany element.

Wyposażeni w takie klasy możemy zacząć projektować główny moduł. Otwieramy plik z formą (pełen kod - Form1.cs) i dodajemy do niej następujące zmienne:

private Plansza_Tetris Plansza_do_gry;
private Plansza_Tetris nastepny;

private Label Pkt, Poziom;
private Label lblPkt, lblPoziom;
private Label info;

public int level;
public int punkty;

private bool szybka;
private bool gra;
private bool pauza;

Plansza_do_gry będzie reprezentować główny ekran gry. nastepny posłuży do wyświetlania kolejnego klocka jaki się pojawi. Etykiety wyświetlą poziom gry oraz zdobyte punkty, które będą przechowywane odpowiednio w level i punkty. Etykieta info będzie pokazywać legendę gry. szybka powie nam czy spadający klocek jest przyspieszony (po naciśnięciu klawisza w dół), gra będzie informować nas o tym czy gra się odbywa, a pauza czy włączyliśmy pauzę. Przechodzimy do widoku graficznego i dodajemy kontrolkę Timer, nazywamy ją licznik i klikamy na niej 2 razy by utworzyć obsługę zdarzenia Tick. Wrócimy do niego później. Teraz dodajemy do formy funkcję Inicjuj. Umieszczony w niej kod zainicjuje wszystkie dodawane przez nas kontrolki odpowiednimi wartościami, określi ich szerokość, położenie, itp. Ponadto odpowiednio ustawi wcześniej utworzone zmienne. Dodajemy teraz kod obsługi zdarzenia Plansza_Paint, które ustawiliśmy dla Plansza_do_gry i nastepny. Będzie się ono wywoływać np. po zminimalizowaniu okna programu i przywróceniu mu poprzednich wymiarów. Jego zadaniem będzie przerysowanie plansz. Wracamy teraz do zdarzenia  Tick dla licznika. Będzie on je generował co pewien okres czasu zależny od poziomu. Po każdym tyknięciu kontrolowana figura musi opaść jeden wiersz w dół, a gdy to niemożliwe - musi pojawić się nowy klocek. Jeśli figura opadnie, procedura sprawdza się czy powstały jakieś linie i w razie potrzeby odpowiednio reaguje. Gdy nie będzie możliwości dodania do planszy nowego elementu (czyli gdy zabraknie już miejsca), gra się zakończy. Ponadto licznik aktualizuje planszę nastepny tak, aby pokazywała ona cały czas klocek, który pojawi się po aktualnym. Kontroluje także ilość zdobywanych punktów i poziom gry. Na koniec dodajemy jeszcze obsługę zdarzenia KeyDown dla naszej formy. Będzie ono przechwycać wciśnięcie klawisza oraz odpowiednio reagować w zależności od jego rodzaju. I tak np. po naciśnięciu Escape gra się zakończy, a gdy wciśniemy strzałkę w dół - klocek przyspieszy. Opis wszystkich używanych przycisków znajduje się w legendzie (etykieta info).

Właśnie ukończyliśmy tetrisa. Jak się okazało wcale nie było to takie trudne. Najważniejsze są klasy Plansza oraz Plansza_Tetris. To one przechowują informacje o grze oraz potrafią zachować się jak na tetrisa przystało (kasowanie lini, itp.). Drugi ważny element - odliczanie -  zrealizowany jest w liczniku i to on sprawia, że klocki opadają w dół. Serdecznie zapraszam do bicia rekordów :)

Jak wykorzystać Copilot w codziennej pracy? Kurs w przedsprzedaży
Jak wykorzystać Copilot w codziennej pracy? Kurs w przedsprzedaży

Wydarzenia