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

136 lines
4.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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:
```python
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).
```python
@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:
```python
from src.settings import SZEROKOSC, WYSOKOSC
from src.entities import Player, Chaser
```
Uruchamiasz z katalogu `dzien5_projekt/`:
```bash
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ą)