Tetris w C#
Autor:
Marcin Hałaczkiewicz
Opublikowano:
26 kwietnia 2007
Odsłon:
72 967
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 :)
Komentarze
hmmm..mam dokladnie tak samo ale nie reaguje na klawisze
aigi,
18 czerwca 2007, 19:17
Aby reagowalo na klawisze trzeba kliknac akcje Key Down formy w jej oknie Events i wkleic kod. Wtedy dziala.
blah,
28 czerwca 2007, 15:01
tak tez zrobilam,to byla pierwsza moja mysl,ale tak i tak nie dziala...
aigi,
19 lipca 2007, 15:46
chyba robie blad w dodawaniu pliku,powilnnam dodac klase czy codefile?
aigi,
19 lipca 2007, 17:02
Ja dodałem klase - i działa :)
m,
5 sierpnia 2007, 19:49
aigi pewnie pewnie juz jest za pozno z tym moim komentarzem ale napisze, moze komus innemu sie przyda - jezeli macie taki problem sprobujcie zamienic wlasciwosc KeyPreview dla formy na true - byc moze to pomoze
mardock,
26 listopada 2007, 18:16
Witam. Po zrobieniu wszystkiego wg instrukcji pojawia mi sie blad: The name "licznik" does not exist in the current context . Co mam zrobic? Pomozcie!!!
marcin,
13 stycznia 2008, 18:59
wszystko spoko dziala thx
k4,
22 stycznia 2008, 22:04
Przede wszystkim stosuj się do instrukcji.... Jest napisane przecież:
Przechodzimy do widoku graficznego i dodajemy kontrolkę Timer, nazywamy ją licznik i klikamy na niej 2 razy by utworzyć obsługę zdarzenia Tick.
Jak to zrobisz będzie działało bez problemu
Shl,
22 stycznia 2008, 23:47
cienko opisane metody, co robia? itp wiem, ze jest pomoc w C# i jest wszystko napisane ale jak ktos pisze jakis kurs to powinien tlumaczyc od deski do deski.
dok,
15 kwietnia 2008, 14:30
@dok: Czego konkretnie brakuje Ci w artykule?
Daniel K.,
16 kwietnia 2008, 00:44
Czemu nie ma gotowego pliku do pobrania? :( bo wyskakuje mi kilka błędów i nie wiem co robić a takto bym sobie sprawdził z gotowym plikiem. Może ktoś dać linka do gotowego pliku? Proszę!
Juziogim3,
2 lipca 2008, 15:18
KIEPSKIE ŻE NIEMOGE!!!
Ziom,
25 października 2008, 15:26
u mine wyskakuje błąd :
Error 1 Program "C:\Users\Jakub\Documents\Visual Studio 2008\Projects\Tetrissss\Tetrissss\obj\Debug\Tetrissss.exe" does not contain a static "Main" method suitable for an entry point Tetrissss<br /><br />może jakieś rady ??
dziadjihad,
19 listopada 2008, 00:29
bez statycznej metody Main nie zadziała
xxx,
2 grudnia 2008, 21:57
Witam. Mam również problem z obsługą klawiatury.. Zrobiłam wszystko wg instrukcji i Waszych podpowiedzi i niestety nadal nic.. Program uruchamia się, ale nie reaguje na zdarzenia.. Ustawiłam również grę na "true" , jednak to również nie dało rezultatu.. :( Proszę o pomoc i z góry dziękuję :)
Meg,
28 grudnia 2008, 20:11
Dobry poradnik, wszystko ładnie opisane na prawie wszystko ale poprzednicy to uzupełnili.
P.S.
A Ci co piszą że kiepski itp. To chyba o programowaniu niemają zielonego pjęcia. I chcą odrazu poważniejsze rzeczy pisać. A pozatym ~70% czasu przy projektowaniu jakiejs aplikacji to jest poprawa błędów(takie śą fakty).
voidrek,
26 stycznia 2009, 09:28
cześć mógłby ktoś podesłać mi program na mejla? kamil_lacki@o2.pl
z góry pięknie dziękuje :)
hihot,
28 stycznia 2009, 23:59
Problem z dzialaniem strzalek na klawiaturze, dziala wszystko inne a, s, d, w itd. ale nie dzialaja strzalki. Oczywiscie KeyPreview dla formy na true. Zalezy mi na strzalkach bo pisze troszke bardziej zaawnsowany programik i obsluge bajerow ustawiem na czesci ASCII, a do poruszania sie potrzebne mi strzalki. Prosze o jakies solucje :)
blacky,
23 marca 2009, 12:02
Cóż mogę powiedzieć, mi wszystko działa, wystarczyło do głównej formy dodać obsługę zdarzenia KeyDown
this.KeyDown += new KeyEventHandler(Form1_KeyDown);
Inicjuj(); // tak dla orientacji w kodzie dla zupełnie zielonych;)
ktoś miał problem z licznikiem, upewnij się czy dodałeś do głównej formy Timer"a i zmieniłeś właściwość "Name" na "licznik"
aniol,
5 maja 2009, 19:50
prosze, niech ktos przepchnie na maila. tezcatlipoca_one@o2.pl
gość,
7 maja 2009, 08:21
Moze ktos mi przeslac na maila ten projekt bede bardzo wdzieczny:)
Grzes89iek@gmail.com
Grzesiek,
12 maja 2009, 13:17
Wszystkie jest opisane w tych postach co trzeba zrobić żeby działało innej opcji nie ma - patrz koment "aniol". Ja powiem tyle, że o ile faktycznie to wszystko działa to tłumaczenie jest jedną, wielką, fatalną, żenującą porażką. Ktoś kto już programuje łatwo się w tym znajdzie i zauważy co się czym zajmuje, ale przecież ten art nie jest dla tych co się znają. Więc przydatność dla początkujących marna.
Bobov,
9 czerwca 2009, 00:02
Kurs jest fajny, ale ktoś powinien go zdebugować - przecież nie ma zamieszczonego przykładowego działającego projektu, a pliki, które są zawierają błędy. Też oczywiście miałem problem z obsługą klawiszy - trzeba przecież dodać obsługę klawiczy w Inicjuj().
Mi po wykonaniu tych wszystkich instrukcji nie wiem czemu nie działa opadanie klocków. Dopiero się uczę i naprawdę nie wiem w czym jest problem. Może ktoś wie i się podzieli rozwiązaniem?
Tomek,
9 czerwca 2009, 09:52
wszystko działa, instrukcja jest przejrzyście napisana. Najłatwiej zrobić caly projekt tak, że dodajemy klasy pokolei i wklejamy gotowce, które są tu na stronie. Program może sie wysypać w jednym miejscu: jak dodajemy obsługę zdarzenia "Tick" to moze nam napisac Tick_1 w kodzie zamiast Tick (mi tak robiło). Autor zapomniał o bardzo ważnej rzeczy, którą napisał juz wczesniej kolega anioł:
this.KeyDown += new KeyEventHandler(Form1_KeyDown);
<br />
Inicjuj(); // tak dla orientacji w kodzie
chodzi o tą pierwszą linijkę. Trzeba ją dodać do pliku Form1.cs (druga linijka tylko pokazuje gdzie powinniśmy dodac tą pierwszą).
Po tym wszystko już śmiga, fajnie sie gra, ja juz do 10lvl dobiłem ;D
Acha i jeszcze jedno bardzo ważne: zeby to zrozumiec trzeba conajmniej raz przeczytać całą ta instukcje od poczatku do konca, bo bez tego to raczej trudno.
pzdr
mr.zbro,
9 czerwca 2009, 14:14
Czy tos moze zamiescic kod 2 konstrutorow z pliku Plansza.cs bo strona jest nieosiąglna http://www.centrumxp.pl/dotNet/Podstrony/Plansza.htm#public_Plansza() lub wyslac mi na maila a bardzo chce uruchomic gre pozdrawiam
adasko,
7 października 2009, 12:20
Oki juz poradzilem sobie z moim probleme
adasko,
7 października 2009, 13:02
sam kurs jest ok, ale wydaje mi sie ze troche jest tu niescislosci jesli chodzi o nazewnictwo. chyba ze autor od razu zalozyl ze ten kurs czytaja programisci, a nie poczatkujacy. ja sie tu gubie niestety...
czolg,
13 grudnia 2009, 19:28
Mógłby ktoś dać już całął paczkę wraz z kodem źródłowym ?
igorw6,
28 grudnia 2009, 23:22
czy ktoś może mi wyjaśnić dlaczego visualstudio wywala mi błąd:
Error 1 No overload for method "Kratka" takes "2" arguments C:\C#\TETRIS\tetris\tetris\plansza.cs 24 28 tetris
przy kodzie:
public Plansza_Tetris (int a, int b): base(a, b)
{
x_figury = y_figury = 0;
}
?? będe wdzięczny za pomoc
coldblue,
2 lutego 2010, 17:07
moze ktos mi powiedziec dlaczego te klocki mi nie opadają???
jama,
28 grudnia 2010, 12:48
pytanie - co zrobić by zmieinć wymiary planszy ? tzn żeby powiększyło się pole do gry.. np szerkość na 20 kratek..
tomiii,
10 stycznia 2011, 13:32
Co dokladnie robi
this.Size = new System.Drawing.Size(sz_ekranu * kratka.wymiar
+ 4, wys_ekranu * kratka.wymiar + 4);
Tworzy jakis tam rozmiar??
itachi,
12 stycznia 2011, 13:06
podeślijcie mi proszę projekt na maila: mark.trait@gmail.com ;)
mark.trait,
3 kwietnia 2011, 18:17
"Dodajmy teraz nowy pusty plik do projektu, nazwijmy go Kratka."
jakiego typu ma być ten plik png, txt czy co ?? proszę o odpowiedź.
z góry dziękuje.
greg19,
23 października 2011, 18:00
Pojawia mi sie blad "The type or namespace name "Form1" could not be found (are you missing a using directive or an assembly reference?)" moglby ktos pomoc ? :)
Marcin,
13 listopada 2011, 21:34
Tworzymy pusty plik kratka a w nim klasę, czyli tworzymy ten plik .cs The namespace ... być może Twoja forma nie jest widoczna
Pozny typ,
23 kwietnia 2012, 18:44