python-arcade-oo/dzien3_4_platformowka/README.md

141 lines
6.5 KiB
Markdown
Raw Permalink Normal View History

# 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` |