Hot course

C# – Zbuduj Własnego Tetrisa! Kompletny Przewodnik

Chcesz nauczyć się programowania gier w C#? Zbuduj od podstaw kultowego Tetrisa i poznaj kluczowe koncepcje programowania! W tym kursie ... Pokaż więcej
11 Uczniowie Zapisano się
0
0 opinii
  • Opis
  • Program
  • Notice
  • Recenzje
tetris1.png

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.

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

  1. 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ń:

  1. Przeniesiemy IGameBoard do folderu Interfaces, aby lepiej uporządkować kod nasz kod.

  2. Dodamy GameBlockType (enum) – reprezentujący różne typy klocków (I, O, T, S, Z, J, L).

  3. Stworzymy interfejs IGameBlockFactory, który określi, jak powinna działać fabryka klocków.

  4. 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)

Kod źródłowy dostępny na githubie

https://github.com/mariuszjurczenko/CsharpOldGames