python-arcade-oo/dzien3_4_platformowka/README.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

141 lines
6.5 KiB
Markdown
Raw 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.

# Dni 34 — platformówka wg oficjalnego tutoriala
Podstawa: **Simple Platformer**
<https://api.arcade.academy/en/latest/tutorials/platform_tutorial/index.html>
Tutorial jest po angielsku i ma 14 kroków. Robimy go po kolei, ale **nie kopiuj
całych plików** — przepisuj kod ręcznie do `gra.py` i po każdym kroku uruchamiaj:
```bash
python dzien3_4_platformowka/gra.py
git add -A && git commit -m "dzien3: krok 5 — grawitacja"
```
Assety (grafika, dźwięki) — tutorial używa paczki Kenney'a, pobierz z
<https://kenney.nl/assets> do katalogu `assets/`. Arcade ma też wbudowane
zasoby dostępne przez ścieżki `":resources:images/..."` — na start to wystarczy
i nic nie trzeba pobierać.
---
## Dzień 3 — gracz, platformy, fizyka, skok, kamera
Kroki 17 tutoriala.
- [ ] **Krok 1 — Install and Open a Window.** Klasa okna, `__init__` z `super()`,
`on_draw()` z `self.clear()`. To już umiesz z dnia 1 — przelatujemy szybko.
- [ ] **Krok 2 — Textures and Sprites.** `arcade.Sprite(ścieżka, scale)`.
Zwróć uwagę: teraz sprite ma obrazek, a nie kolorowe koło.
- [ ] **Krok 3 — Many Sprites with SpriteList.** Osobne listy: `player_list`,
`wall_list`. Ułóż podłogę w pętli `for x in range(0, 1250, 64)`.
- [ ] **Krok 4 — Add User Control.** `on_key_press` / `on_key_release`
ustawiają `change_x`.
- [ ] **Krok 5 — Add Gravity.** `arcade.PhysicsEnginePlatformer(player_sprite,
walls=wall_list, gravity_constant=1.0)`.
**To jest KOMPOZYCJA:** okno *ma* silnik fizyki (`self.physics_engine`),
a nie *jest* silnikiem fizyki. Dziedziczenie = „jest czymś”,
kompozycja = „ma coś”. Nie myl tych dwóch.
Skok: w `on_key_press` sprawdź `self.physics_engine.can_jump()` i ustaw
`change_y = PLAYER_JUMP_SPEED`.
W `on_update()` wołaj `self.physics_engine.update()` — silnik sam
przesuwa gracza i zatrzymuje go na ścianach (nie wołaj tu
`player_list.update()`, bo ruch policzyłby się dwa razy).
- [ ] **Krok 6 — Resetting.** Wydziel metodę `setup()` i zawołaj ją z `__init__`.
Dzięki temu restart gry to jedno wywołanie.
- [ ] **Krok 7 — Adding a Camera.** W arcade 3.x klasa nazywa się
`arcade.Camera2D` (stare tutoriale pokazują `arcade.Camera` — to 2.x).
Dwie kamery: jedna śledzi gracza (świat), druga stoi w miejscu (GUI/punkty).
**Koniec dnia 3:** gracz biega po platformach, skacze, kamera jedzie za nim.
Commit: `dzien3: platformowka - ruch, skok, kamera`.
---
## Dzień 4 — monety, dźwięki, wrogowie, poziomy, ekran końca
Kroki 814 tutoriala.
- [ ] **Krok 8 — Collecting Coins.** `arcade.check_for_collision_with_list`
+ `remove_from_sprite_lists()` (znasz z dnia 2).
- [ ] **Krok 9 — Adding Sound.** `arcade.load_sound(...)` w `setup()`,
`arcade.play_sound(...)` przy zebraniu monety i przy skoku.
Dźwięki wczytuj RAZ w `setup()`, nie w `on_update()` — inaczej gra zacznie
zwalniać.
- [ ] **Krok 10 — Adding a Score.** Licznik punktów + `arcade.draw_text` na
kamerze GUI.
- [ ] **Krok 11 — Using a Scene.** `arcade.Scene` zamiast ręcznie trzymanych
list. To znowu kompozycja: scena *ma* listy sprite'ów i wie, w jakiej
kolejności je rysować.
- [ ] **Krok 12 — Loading a Map From a Map Editor.** Mapa z pliku `.tmx`
(edytor [Tiled](https://www.mapeditor.org/)), `arcade.load_tilemap`.
- [ ] **Krok 13 — More Types of Layers.** Warstwy: platformy ruchome, kolce,
tło.
- [ ] **Krok 14 — Multiple Levels.** Numer poziomu w atrybucie `self.level`,
wczytywanie pliku `mapa_{self.level}.tmx`.
### Zadania własne (poza tutorialem)
- [ ] **Wrogowie.** Wykorzystaj klasę `Enemy` z dnia 2 — przenieś ją tutaj
i dostosuj do platformówki (np. wróg chodzi po platformie i zawraca na
jej końcu). Kolizja gracza z wrogiem = utrata życia.
- [ ] **Ekran końca gry.** Zrób go przez `arcade.View`: klasa `GameView`
(rozgrywka) i klasa `GameOverView` (napis + „naciśnij ENTER”).
Przełączanie: `self.window.show_view(GameOverView())`.
To przedsmak dnia 5 — stany gry jako osobne klasy.
- [ ] **ZADANIE OOP DNIA — `property health` (0100).**
Dodaj graczowi punkty życia tak, żeby **nie dało się** ustawić wartości
spoza zakresu 0100 — nawet przez pomyłkę w innym miejscu kodu.
```python
class Player(arcade.Sprite):
def __init__(self, sciezka, scale):
super().__init__(sciezka, scale)
self._health = 100 # podkreślnik = "to jest wewnętrzne, nie ruszaj z zewnątrz"
@property
def health(self):
"""Getter — działa jak zwykły odczyt atrybutu: print(gracz.health)"""
return self._health
@health.setter
def health(self, wartosc):
"""Setter — uruchamia się przy `gracz.health = 120` i pilnuje zakresu."""
# TODO: przytnij wartość do przedziału 0-100
# (podpowiedź: max(0, min(100, wartosc)))
# TODO: jeśli po przycięciu jest 0 — ustaw self.zywy = False
...
```
Sens `property`: z zewnątrz `gracz.health -= 30` wygląda jak zwykłe działanie
na atrybucie, ale pod spodem wykonuje się TWÓJ kod z walidacją. Nie trzeba
pisać `gracz.set_health(gracz.get_health() - 30)` ani pamiętać o sprawdzaniu
zakresu w każdym miejscu, gdzie gracz obrywa.
Sprawdź, że działa:
```python
gracz.health = 150 # -> 100
gracz.health = -20 # -> 0, i gracz przestaje żyć
```
- [ ] Narysuj pasek życia (`arcade.draw_lbwh_rectangle_filled`) o szerokości
proporcjonalnej do `gracz.health`.
**Koniec dnia 4:** kompletna platformówka z monetami, dźwiękiem, wrogami,
paskiem życia i ekranem końca.
Commit: `dzien4: platformowka - monety, dzwieki, wrogowie, health property`.
---
## Ściąga — API arcade 3.x
| Co | Jak |
| ---------------------- | --------------------------------------------------------------- |
| czyszczenie ekranu | `self.clear()` (nie `arcade.start_render()`) |
| odświeżenie listy | `sprite_list.update(delta_time)` (nie `on_update`) |
| metoda w klasie sprite | `def update(self, delta_time=1/60, *args, **kwargs):` |
| kamera | `arcade.Camera2D` (nie `arcade.Camera`) |
| prostokąt | `arcade.draw_lrbt_rectangle_filled` / `draw_lbwh_rectangle_filled` |
| kolor tła | `self.background_color = arcade.color.X` |