C# – Zbuduj Własnego Tetrisa! Kompletny Przewodnik
- Opis
- Program
- Notice
- Recenzje

Chcesz nauczyć się programowania gier w C#?
Zbuduj od podstaw kultowego Tetrisa i poznaj kluczowe koncepcje programowania! W tym kursie przeprowadzimy Cię przez cały proces – od minimalnej wersji gry (MVP) po bardziej zaawansowane mechaniki i optymalizację.
Czego się nauczysz?
Rozpoczniemy od implementacji z podziałem na warstwy i utrzymaniem najlepszych praktyk programowania.
Zaczniemy od utworzenie projektu w Visual Studio z odpowiednimi warstwami:
Tetris.Domain: Logika gry i modele.
Tetris.Application: Zasady działania gry, logika aplikacji.
Tetris.Infrastructure: Integracja z zewnętrznymi komponentami (jeśli będzie potrzebne).
Tetris.Presentation: WPF – interfejs użytkownika.
Krok 1. MVP naszej gry Tetris, skupiamy się na najbardziej podstawowych elementach gry, takich jak:
Reprezentacja planszy jako siatki.
Jeden prosty klocek.
Mechanika opadania bloku w dół.
Podstawowy interfejs (np. wyświetlanie planszy w WPF).
Nie dodajemy od razu zaawansowanych funkcji, takich jak różne kształty klocków, poziomy trudności czy interakcje gracza, ponieważ najważniejsze jest przetestowanie podstawowej mechaniki. Gdy MVP działa poprawnie i spełnia oczekiwania,
możemy zacząć rozwijać bardziej zaawansowane funkcjonalności.
Następnie przeanalizujemy kod pod kątem zgodności z najlepszymi praktykami i zasadami:
1. SOLID
· Single Responsibility Principle (SRP):
· Open/Closed Principle (OCP):
· Liskov Substitution Principle (LSP):
· Interface Segregation Principle (ISP):
· Dependency Inversion Principle (DIP):
2. Separation of Concerns (SoC)
3. KISS (Keep It Simple, Stupid)
4. DRY (Don’t Repeat Yourself)
5. YAGNI (You Aren’t Gonna Need It)
6. Cohesion i Coupling
Krok 2. Dodamy testy jednostkowe zgodnie z podejściem TDD dla naszego kodu gry Tetris.
Stworzymy testy dla głównych komponentów, zaczynając od podstawowych funkcjonalności.
Testy naszej planszy do gry – klasy GameBoard:
Sprawdzenie poprawnego tworzenia planszy czyli – Testy konstruktora
Weryfikacja, czy komórki są początkowo puste – Testy metody IsCellEmpty
Testowanie umieszczania bloków na planszy – Testy metody PlaceBlock
Testy warunków brzegowych
Testy naszego serwisu – klasy GameService:
Sprawdzenie poprawnego tworzenia nowych bloków – SpawnBlock
Testowanie ruchu bloków w dół – MoveBlockDown
Weryfikacja zachowania przy osiągnięciu dna
Sprawdzenie poprawnego zbierania wszystkich bloków na planszy – GetBlocks
Testy będą wykorzystywać:
xUnit jako framework testowy
Moq do mockowania zależności
FluentAssertions do czytelniejszych asercji
Krok 3 Dodanie różnych typów bloków
Dodanie różnych typów bloków w projekcie wymaga wprowadzenia odpowiednich modyfikacji w kodzie, które będą zgodne z zasadami projektowymi. W szczególności zastosujemy polimorfizm, co pozwoli na efektywne zarządzanie różnymi kształtami bloków. Dzięki temu nasz kod stanie się bardziej czytelny, elastyczny i zgodny z najlepszymi praktykami programowania.
Krok 4 Zastosowanie wzorca Factory Method.
Cel wzorca :
Oddzielenie logiki tworzenia klocków od reszty kodu,
co ułatwi zarządzanie i rozszerzanie aplikacji.Zamiast tworzyć obiekty GameBlock bezpośrednio w różnych miejscach kodu,
będziemy korzystać z fabryki (GameBlockFactory),
co poprawia czytelność i elastyczność kodu.
Plan działań:
Przeniesiemy IGameBoard do folderu Interfaces, aby lepiej uporządkować kod nasz kod.
Dodamy GameBlockType (enum) – reprezentujący różne typy klocków (I, O, T, S, Z, J, L).
Stworzymy interfejs IGameBlockFactory, który określi, jak powinna działać fabryka klocków.
Zaimplementujemy GameBlockFactory, która będzie:
Tworzyć klocek na podstawie zadanego typu (CreateGameBlock).
Tworzyć losowy klocek (CreateRandomGameBlock).
Dzięki temu jeśli w przyszłości dodamy nowe typy klocków, wystarczy zmodyfikować fabrykę, zamiast zmieniać kod w wielu miejscach!
Krok 5 Dodanie obsługi obracania klocków za pomocą klawiatury.
Aby poprawnie zaimplementować obrót klocków w grze, należy uwzględnić kolizje zarówno ze ścianami planszy, jak i innymi blokami. Jeśli obrót nie jest możliwy, kształt klocka powinien pozostać bez zmian.
Plan Implementacji:
1. Modyfikacja klasy GameBlock
– Istniejąca metoda Rotate zostanie dostosowana tak, aby zwracała obrócony kształt klocka.
2. Aktualizacja klasy GameService
– Dodanie metody CanRotate, która sprawdzi, czy obrót jest możliwy.
– Implementacja RotateCurrentBlock do obsługi rotacji klocka.
– Dodanie metod MoveBlockLeft i MoveBlockRight w kontekście obrotów.
3. Dodanie Strategi
– Dodamy interfejs IRotationStrategy.
– Dodamy implementację strategii standardowej rotacji klocka o 90 stopni w prawo.
– Dodamy implementacja braku rotacji dla klocka.
– Dodamy Rotacja o 180 stopni
– Dodamy Obracanie klocek przeciwnie do ruchu wskazówek zegara
– Dodamy Rotację Losową, wybiera jedną z dostępnych strategii rotacji
Krok 6 Dodanie logiki detekcji i usuwania pełnych linii i dodanie punktacji
Dodamy logike detekcji i usuwania pełnych linii oraz punktację do naszej gry. Zacznijmy od stworzenia odpowiednich elementów w domenie.
Implementacja zapewnia:
– Prawidłowe wykrywanie pełnych linii – system skutecznie identyfikuje wiersze wypełnione klockami.
– Usuwanie linii i przesuwanie klocków w dół – po usunięciu pełnych linii pozostałe klocki są automatycznie przesuwane w dół.
– Naliczanie punktów według zadanej tabeli – wynik jest aktualizowany zgodnie z liczbą usuniętych linii i przyjętymi zasadami punktacji.
– Wyświetlanie aktualnego wyniku – UI natychmiast odświeża wynik oraz licznik usuniętych linii po każdej zmianie.
Zastosuj wzorzec Observer Pattern
Observer Pattern idealnie pasuje do powiadamiania UI o zmianach w grze. Observer Pattern pozwoli na oddzielenie logiki gry od interfejsu użytkownika. Dzięki temu UI będzie reagować na zmiany, takie jak:
– Aktualizacja punktacji
– Licznik usuniętych linii
– Czy Możliwość dodania innych zmian (np. Informowanie o końcu gry co teraz też dodamy)
-
1Rozpoczecie Projektu - Podział Na Warstwy
-
2Rozpoczynamy Kodowaniw - warstwa Domain - klasa Block
-
3Warstwa Domain - klasa GameBoard
-
4Warstwa Application - klasa GameService
-
5GameService dodajemy metodę Utworzenie Nowego Klocka
-
6GameService dodajemy metodę do Przesuwania Klocka w dół
-
7GameService dodajemy metodę do Pobierania Bloków Na Planszy
-
8Warstwa Presentation - klasa MainWindow
-
9MainWindow dodajemy metodę do Aktualizowania Stanu Gry
-
10MainWindow dodajemy metodę do Rysowania Elementów Gry
-
11Zagadka
-
12MainWindow dodajemy metodę do Pobierania Koloru
-
13Analiza kodu pod kątem Najlepszych Praktyk i Zasad SOLID
-
14Rozwiązanie Zagadki
-
15Dwa Podejścia do kolejności wymiarów w tablicy 2D
-
16Optymalizacja Renderowania
-
17Najważniejsze Aspekty Testów
-
18Dodanie Testów do klasy GameBoard
-
19GameBoard dodanie testów dla Konstruktora
-
20GameBoard jeszcze więcej testów Konstruktora
-
21GameBoard dodanie testów sprawdzających czy komórka jest pusta
-
22GameBoard dodanie testów dla nieprawidłowych współrzędnych
-
23GameBoard dodanie testów dla metodu umieść blok który powinien rzucić wyjątek
-
24GameBoard dodanie testów dla metodu umieść blok gdy pozycja jest prawidłowa
-
25GameBoard dodanie testów dla metodu umieść blok gdy pozycja jest zajęta
-
26GameBoard dodanie testów dla metodu umieść blok gdy pozycja jest poza zakresem
-
27Dodanie Testów do klasy GameService
-
28GameService dodanie testów dla metody wygeneruj blok
-
29GameService dodanie testów dla metody przesuń blok w dół gdy jest przestrzeń
-
30GameService dodanie testów dla metody przesuń blok gdy osiągnięto dno
-
31GameService dodanie testów dla metody przesuń blok gdy gdy nie powinien przesuwa
-
32GameService dodanie testów dla metody pobierz bloki
-
33Podsumowanie Rozdziału I Co Dalej...
-
34Wprowadzenie
-
35Utworzenie Klasy Bazowej GameBlock
-
36GameBlock - Zwracanie Wspołrzędnych Zajmowanych Przez Klocek
-
37GameBlock - Obracanie Klocka w Prawo
-
38Dziedziczenie - Dodanie Różnych Typów Klocków
-
39Modyfikacja GameService Aby Działał z GameBlock
-
40GameService - Sprawdzenie Czy Klocek Można Przesunąć
-
41GameService - Losowe Generowanie Klocków
-
42GameService - Umieszczanie Klocka Na Planszy Gry
-
43Podsumowanie Rozdziału I Co Dalej...
-
44Wprowadzenie
-
45Dodanie GameBlockType
-
46Dodanie Interfejsu IGameBlockFactory
-
47Dodanie GameBlockFactory
-
48Modyfikacja GameService
-
49Modyfikacja MainWindow
-
50Zalety Implementacj Factory Method
-
51Dostosowanie Testów Klasy GameService
-
52Dostosowanie Testów Klasy GameBlockFactory
-
53CreateGameBlock Powinien Utworzyć Poprawny Typ klocka
-
54CreateGameBlock Powinien Rzucić Argument Exception Dla Niepoprawne
-
55CreateRandomGameBlock Powinien Zwrócić Poprawny Blok Gry (losowy)
-
56CreateRandomGameBlock Powinien Tworzyć Różne Typy
-
57CreateGameBlock Powinien Mieć Poprawne Wymiary
-
58CreateGameBlock Wszystkie Typy Powinny Mieć Cztery Bloki
-
59Podsumowanie Rozdziału I Co Dalej...
-
60Wprowadzenie
-
61Modyfikacja klasy GameBlock
-
62Dodanie metody CanRotate w GameService
-
63Dodanie RotateCurrentBlock w GameService
-
64Dodanie MoveBlockLeft i MoveBlockRight w GameService
-
65Aktualizacja MainWindow
-
66Dodamy interfejsu IRotationStrategy
-
67Dodanie StandardRotationStrategy
-
68Dodanie NoRotationStrategy
-
69Modyfikacja GameBlock aby korzystał z wybranej strategii rotacji
-
70Strategie i co dalej
-
71Dodanie Rotation180Strategy
-
72Dodanie CounterClockwiseRotationStrategy
-
73Dodanie MirrorRotationStrategy
-
74Podsumowanie Rozdziału I Co Dalej...
-
75Wprowadzenie
-
76Dodanie Klasa Score
-
77GameBord Dodanie Usuwania Lini
-
78Znajdujemy wszystkie pełne linie
-
79Sprawdza czy dana linia jest pełna
-
80Znajdujemy wszystkie pełne linie cz.2
-
81Usuwamy wskazane linie
-
82Przesuwa klocki w dół po usunięciu linii
-
83Znajdujemy wszystkie pełne linie cz.3
-
84Modyfikacja GameService
-
85Modyfikacja MainWindow Formularz
-
86Modyfikacja MainWindow Kod
-
87Implementujemy Observer Patern
-
88Dodajemy listę obserwatorów - GameSubject
-
89Modyfikacja GameService
-
90Modyfikacja MainWindow
-
91Podsumowanie Rozdziału I Co Dalej...
-
92Wprowadzenie
-
93Dodanie klasy HighScore
-
94Dodanie interfejsu IHighScoreRepository
-
95Dodanie JsonHighScoreRepository
-
96Dodanie HighScoreService
-
97Dodanie InputDialog
-
98Dodanie InputDialog cz.2
-
99Modyfikacja MainWindow Forms
-
100Modyfikacja MainWindow Code
-
101Dodanie UpdateHighScoresList
-
102Modyfikacja OnGameOver
-
103InpuDialog poprawki
-
104Podsumowani rozdziału I Co Dalej