python-arcade-oo/dzien5_projekt/WYMAGANIA.md
Oskar Kapala 7c33b6bb63 scaffold: 5-dniowy kurs OOP + arcade
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-03 10:24:17 +02:00

4.8 KiB
Raw Blame History

Dzień 5 — własna gra od zera

Projekt zaliczeniowy. Piszesz własną grę — nie przepisujesz tutoriala. Pomysł jest Twój; poniżej są tylko wymagania techniczne, które kod musi spełnić.

Zakres

Gra ma być mała i skończona. Lepiej prosta gra, która działa i ma ekran końca, niż ambitna, która się nie uruchamia. Przykłady o dobrym rozmiarze:

  • uciekaj przed ścigającymi Cię wrogami i zbieraj punkty,
  • prosty shooter (statek strzela, przeciwnicy lecą z góry),
  • wąż / snake z przeszkodami,
  • gra zręcznościowa: unikaj spadających obiektów,
  • mini-platformówka z jednym poziomem i bossem.

Wymagania obowiązkowe

1. Minimum 3 własne klasy

Nie licząc klas widoków. Np. Player, Enemy, Bullet, PowerUp.

2. Jedna hierarchia dziedziczenia: baza + co najmniej 2 klasy pochodne

        Enemy            <- klasa bazowa: wspólne dane i wspólne zachowanie
       /     \
  Chaser    Shooter      <- klasy pochodne: każda nadpisuje update() inaczej

Warunki:

  • klasa bazowa musi mieć realną treść — wspólny __init__ i wspólną implementację update(), a nie samo pass,
  • każda klasa pochodna nadpisuje przynajmniej jedną metodę i woła w niej super().<metoda>(...),
  • wszystkie obiekty tej hierarchii trzymasz w jednej SpriteList i aktualizujesz jednym lista.update(delta_time) — bez if-a po typie. To jest sens polimorfizmu: dodanie trzeciego rodzaju wroga nie wymaga zmiany ani jednej linii w pętli gry.

Pamiętaj o sygnaturze z arcade 3.x:

def update(self, delta_time=1 / 60, *args, **kwargs):
    super().update(delta_time, *args, **kwargs)
    ...

3. Minimum jedno property z walidacją

Np. health (0100), ammo (nie mniej niż 0), poziom_trudnosci (15), speed (z górnym limitem).

@property
def ammo(self):
    return self._ammo

@ammo.setter
def ammo(self, wartosc):
    if wartosc < 0:
        raise ValueError("Amunicja nie może być ujemna")
    self._ammo = min(wartosc, self.max_ammo)

Walidacja musi coś robić — przycinać zakres, rzucać wyjątek albo wywoływać efekt uboczny (np. health spadające do 0 przełącza grę na ekran końca). Samo return self._x bez settera nie liczy się jako walidacja.

4. Stany gry przez arcade.View

Minimum trzy widoki, każdy jako osobna klasa:

  • MenuView — ekran startowy („naciśnij ENTER, żeby zacząć”),
  • GameView — właściwa rozgrywka,
  • GameOverView — wynik + możliwość powtórki.

Przełączanie: self.window.show_view(GameView()). Nie rób stanów przez if self.stan == "menu" w jednym wielkim on_draw() — o to właśnie chodzi w tym wymaganiu.

5. Kod w wielu plikach w src/

Struktura, do której dążymy:

dzien5_projekt/
├── WYMAGANIA.md
├── main.py              # tylko uruchomienie: tworzy okno i pokazuje MenuView
└── src/
    ├── __init__.py
    ├── settings.py      # stałe: rozmiar okna, prędkości, kolory
    ├── entities.py      # Player, Enemy + klasy pochodne, Bullet...
    └── views.py         # MenuView, GameView, GameOverView

Import między plikami:

from src.settings import SZEROKOSC, WYSOKOSC
from src.entities import Player, Chaser

Uruchamiasz z katalogu dzien5_projekt/:

python main.py

Podział na pliki jest sensowny wtedy, gdy entities.py nie musi nic wiedzieć o views.py. Jeśli oba pliki importują się nawzajem — to znak, że podział jest zły; przenieś wspólne rzeczy do settings.py.

Sposób pracy

  1. Najpierw narysuj na kartce, jakie masz klasy i co która trzyma.
  2. Zrób settings.py i main.py, które otwierają puste okno. Commit.
  3. Dodaj MenuView i GameView z pustą planszą. Commit.
  4. Dodaj gracza, ruch. Commit.
  5. Dodaj hierarchię wrogów. Commit.
  6. Dodaj property, kolizje, punkty, GameOverView. Commit.
  7. Dopiero na końcu — dźwięki, grafika z https://kenney.nl/assets, efekty.

Checklista przed oddaniem

  • python main.py uruchamia się bez błędów (PerformanceWarning: draw_text is an extremely slow function to nie błąd — arcade sugeruje obiekty arcade.Text zamiast arcade.draw_text; przy naszej liczbie napisów można to zignorować)
  • 3+ własne klasy (bez widoków)
  • hierarchia: baza + 2 pochodne, każda z nadpisanym update() i super()
  • wrogowie w jednej SpriteList, aktualizowani bez if-a po typie
  • property z działającą walidacją (pokaż, że odrzuca złą wartość)
  • 3 widoki arcade.View, przełączanie działa w obie strony
  • kod w src/, main.py ma mniej niż 20 linii
  • historia gita: kilka commitów, każdy z działającym stanem gry
  • w kodzie nie ma zmiennych globalnych trzymających stan gry (stałe z settings.py są OK — one się nie zmieniają)