141 lines
6.5 KiB
Markdown
141 lines
6.5 KiB
Markdown
|
|
# Dni 3–4 — 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 1–7 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 8–14 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` (0–100).**
|
|||
|
|
|
|||
|
|
Dodaj graczowi punkty życia tak, żeby **nie dało się** ustawić wartości
|
|||
|
|
spoza zakresu 0–100 — 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` |
|