Skip to content

Commit 67e523b

Browse files
authored
Merge from PyLadies CZ Autumn beginners' courses
#513
2 parents c1c0511 + 27c1ebc commit 67e523b

11 files changed

Lines changed: 1099 additions & 1 deletion

File tree

lessons/beginners/exceptions/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ print('Obsah čtverce se stranou', strana, 'je', strana * strana, 'cm2')
212212
Možné řešení pro 1-D piškvorky:
213213

214214
```python
215-
def nacti_cislo(pole):
215+
def tah_hrace(pole):
216216
while True:
217217
try:
218218
pozice = int(input('Kam chceš hrát? (0..19) '))

lessons/projects/snake/index.md

Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
# Hra typu Had
2+
3+
Dnes to všechno — třídy, grafiku, seznamy a tak dále –
4+
spojíme dohromady do závěrečného projektu.
5+
Doufám, že se ti bude líbit!
6+
7+
Naším cílem bude vytvořit klon známé hry [Snake (neboli Had)](<https://en.wikipedia.org/wiki/Snake_(video_game_genre)>)
8+
jejíž princip je tu s námi od roku 1976. Největší popularity se Had dočkal
9+
díky mobilním telefonům Nokia, kde je jako základní hra dostupný od roku 1998
10+
až dodnes.
11+
12+
Projekt není zase tak složitý, protože jeho základní principy už dobře znáš
13+
z domácích projektů a lekcí kurzu. Následující text je tedy spíše
14+
zadání než výukový materiál a v projektu jistě narazíš na něco, co jsme
15+
společně neprobírali. V takovém případě se neboj zeptat nebo si informace
16+
dohledat!
17+
18+
A ještě jedna věc: protože začátečnický kurz končí,
19+
začneme kód psát v angličtině, aby se pak dal sdílet s celým světem.
20+
21+
> [note]
22+
> Procházíš-li si projekt doma, je možné, že narazíš na
23+
> něco s čím si nebudeš vědět rady.
24+
> Kdyby se to stalo, prosím, ozvi se nám!
25+
> Rádi ti s projektem pomůžeme.
26+
27+
## Logika hry a fáze projektu
28+
29+
Základní princip hry máš v malíčku, pokud jsi dokončil{{a}} domácí projekt
30+
po [lekci o seznamech](../../beginners/list/). Pokud jej nemáš, doporučuji
31+
se k němu vrátit.
32+
33+
Práci s pygletem jsme dělali v [lekci o grafice](../../intro/pyglet/).
34+
35+
Teď nám nezbývá než princip tolik populární hry a znalosti z kurzu spojit
36+
dohromady. Doporučuji začít s čistým souborem v prázdné složce a do hotových
37+
programů se koukat jen v případě potřeby.
38+
39+
Jak postupovat, aby se projekt nezdál nedosažitelný už na začátku? Třeba takto:
40+
41+
0. Promysli si, jak bude hra fungovat a jak přeneseme mřížku s hadem
42+
z příkazové řádky do grafického okna.
43+
1. Vykresli hada do grafického okna (ve formě barevných čtverců)
44+
2. Přidej funkci, která bude hadem hýbat.
45+
3. Umožni změnit směr hada pomocí klávesnice.
46+
4. Nenech hada utéct z herní plochy a nabourat do sebe sama.
47+
5. Přidej hadovi jídlo a zajisti, aby po jídle rostl.
48+
6. Vyměň barevné čtverečky za opravdovou grafiku.
49+
50+
Po těchto krocích budeš mít základní hru, ale tou to nekončí, právě naopak!
51+
Budeš mít vlastní hru, jejímuž fungování rozumíš jako nikdo jiný, a to je to pravé pro
52+
přidávání dalších možností. Fantazii se meze nekladou. Například:
53+
54+
1. Ve hře mohou být dva nebo třeba tři hadi najednou – každý ovládaný
55+
jinými klávesami — navzájem soupeřící o jídlo.
56+
2. Kromě jídla se mohou na ploše objevovat i jiné objekty – překážky,
57+
do kterých nesmí had narazit, otrávené jídlo, které hada zkrátí atp.
58+
3. Hrací plocha může být nekonečná a když z ní had vyleze, objeví se
59+
na druhé straně.
60+
61+
## Z příkazové řádky do grafické aplikace
62+
63+
V příkazové řádce měl had souřadnice označující řádek a sloupec. V grafické
64+
aplikaci to bude podobné, ale protože pixelů na obrazovce je mnohem více, budeme
65+
si muset vytvořit pomyslnou síť stejně velkých čtverců, které nám nahradí
66+
řádky a sloupce. Velikost takového čtverce bude konstanta, kterou se vyplatí
67+
mít po celou dobu hry k dispozici, aby se podle ní daly vypočítat
68+
souřadnice k vykreslení obrázků. Pro začátek řekněme, že ideální velikost
69+
takového čtverce bude 64 × 64 pixelů.
70+
71+
Z velikosti čtverce, kterou si můžeme v budoucnu libovolně změnit,
72+
a velikosti okna aplikace můžeme vypočítat, kolik se nám do okna takových
73+
čtverců vejde na šířku a na výšku a tím i zjistit, kolik pomyslných
74+
sloupců a řádků bude naše hrací plocha mít.
75+
76+
## Vykreslení hada
77+
78+
Abychom mohli hada vykreslit, potřebujeme si pro začátek uložit jeho souřadnice.
79+
K tomu můžeš použít seznam dvojic – stejně jako v domácím projektu. Podobných
80+
informací, které se budou v průběhu hry dynamicky měnit, budeme mít už
81+
za malou chvíli více. Proto dává smysl si pro stav hry vytvořit třídu, která
82+
bude tyto informace obsahovat jako atributy a bude s nimi umět pracovat.
83+
84+
> [note]
85+
> I když se může na začátku zdát vlastní třída jako zbytečná
86+
> komplikace, později zjistíš, že ne všechno by se dalo snadno udržovat v globálních
87+
> proměnných.
88+
89+
{{ figure(
90+
img=static('coords.svg'),
91+
alt="Had na „šachovnici“ se souřadnicemi",
92+
) }}
93+
94+
Když už je had definován, budeme potřebovat jednoduchou funkci, která
95+
na ta správná místa umístí obrázky. Pro začátek si vystačíme se zeleným
96+
čtvercem. Obrázek si [stáhni zde]({{ static('green.png')}}) a ulož
97+
do složky k programu.
98+
99+
Stejně jako na lekci i zde použijeme pro vykreslení `Sprite`, kterému už při
100+
vytvoření můžeme zadat obrázek pro vykreslení a vypočtené souřadnice.
101+
Pro jednoduchost stačí `Sprite` vytvořit, vykreslit a „zapomenout“. Není to ale
102+
optimální přístup a tak tohle může být jedním z adeptů pro pozdější vylepšení.
103+
104+
## Rozpohybování hada
105+
106+
Aby se mohl had hýbat, potřebuje znát směr pohybu. V příkazové řádce jsme
107+
vždy počkali, až nám směr zadá uživatel, ale v opravdové hře se bude had
108+
pohybovat sám. Bude tedy potřeba nějaký atribut v naší třídě, kde bude směr
109+
neustále uložen a měnit se bude podle stisknutých kláves v dalším kroku.
110+
111+
Směr pohybu může být uložen v libovolné podobě – světové strany, slovní
112+
označení strany, nebo třeba dvojice s číselným označením pohybu
113+
(`(0, 1)` pro pohyb nahoru, `(-1, 0)` pro pohyb doleva atp.). Podle vybraného
114+
formátu pak bude třeba směr zpracovat.
115+
116+
Pro tuhle chvíli mu tedy bude stačit nastavit směr napevno a napsat funkci,
117+
nebo metodu, která hadem pohne. Pohyb bude probíhat
118+
naprosto stejně jako v příkazové řádce – přidáme do seznamu souřadnice,
119+
kde by měla být „nová hlava“ a umažeme poslední kousek hada.
120+
121+
Protože se pohyb má provádět pravidelně, bude potřeba tuto operaci provádět
122+
automaticky v pravidelných intervalech. `pyglet.clock.schedule_interval` je
123+
zde jasná volba.
124+
125+
## Ovládání pomocí klávesnice
126+
127+
Reagovat na stisknuté klávesy jsme se už taky učili. Teď to tedy využijeme,
128+
abychom dokázali změnit nastavený směr pohybu z předchozího bodu. Bude pro to
129+
samozřejmě potřeba funkce, kterou v pygletu zaregistrujeme pro spuštění po
130+
stisku klávesy.
131+
132+
Protože had už se nám v závislosti na směru pohybuje, měl by začít reagovat
133+
na jeho změnu.
134+
135+
V tuto chvíli:
136+
137+
* Směr se mění podle stisknuté klávesy.
138+
* Had se sám pohybuje podle zadaného směru.
139+
* Nová pozice hada se automaticky vykresluje jako zelené čtverečky.
140+
141+
Vida, máme hotový základ!
142+
143+
## Nenechme ho utéct
144+
145+
Had už se nám hýbe podle našich představ, ale stačí ho nechat chvíli bez dozoru
146+
a uteče nám z hrací plochy. Tomu není těžké zabránit, když víme, že žádná
147+
souřadnice hada nesmí být menší než nula a větší než je velikost hrací plochy.
148+
Kontrolovat je potřeba souřadnice jeho hlavy, která bude vždy všude jako první.
149+
150+
Reagovat na náraz do zdi se dá mnoha způsoby. Nejjednodušší by asi bylo
151+
ukončit hru, ale to by se pak hráč nemohl podívat na tu šlamastiku, do které
152+
se dostal. Proto bude lepší místo toho pouze zastavit časovač, který se stará
153+
o pohyb hada.
154+
155+
Stejným způsobem a na stejném místě v programu bude třeba vyřešit i situaci,
156+
kdy had narazí sám do sebe.
157+
158+
## Jen ať jí, hlavně že mu chutná
159+
160+
Jezdit s hadem po hrací ploše může být chvíli zábava, ale protože had neroste,
161+
není to žádná výzva. A aby mohl růst, potřebuje jíst.
162+
163+
K tomu budeš potřebovat další globálně dostupný seznam (nejlépe atribut
164+
existující třídy), který bude obsahovat informace (souřadnice) o existujícím
165+
jídle na hrací ploše. Navíc bude potřeba mít k dispozici metodu, která bude
166+
umět jídlo na hrací plochu přidat.
167+
168+
Záleží jen na tobě, zda se bude nové jídlo objevovat, když had jedno
169+
z existujících sní, nebo automaticky v pravidelných intervalech.
170+
171+
Jídlo vykreslíme stejným způsobem jako hada (ve stejné funkci/metodě) a jako
172+
obrázek použijeme třeba [jablko]({{ static('apple.png')}}).
173+
174+
První závan grafiky :-)
175+
176+
## Čtverečky ven, grafiku sem
177+
178+
Čtverečky jsou fajn, ale hra by měla lahodit oku a had by měl vypadat jako had.
179+
K tomu máme připravenou sadu obrázků - [ke stažení zde]({{ static('snake-tiles.zip') }}).
180+
Archiv si rozbal do adresáře s hrou tak, aby adresář `snake-tiles` byl na stejné
181+
úrovni jako soubor s programem.
182+
183+
{{ figure(
184+
img=static('snake-tiles.png'),
185+
alt="Kousky hada",
186+
) }}
187+
188+
### Načtení všech obrázků ze složky
189+
190+
Nejdříve si načteme všechny obrázky do hry, abychom je pak mohli bez potíží
191+
použít. Protože se nechceme opakovat (DRY), bude potřeba to udělat nějak
192+
poloautomaticky. Python obsahuje knihovnu [`pathlib`](https://docs.python.org/3/library/pathlib.html),
193+
která umí velmi přehledně pracovat s cestami k souborům a třeba nám dát
194+
i seznam všech souborů ve složce.
195+
196+
Nejdříve si z této knihovny naimportujeme třídu `Path`, která reprezentuje
197+
soubor či složku na disku a vytvoříme z ní instanci, která bude
198+
ukazovat do naší složky s obrázky.
199+
200+
```python
201+
from pathlib import Path
202+
203+
TILES_DIRECTORY = Path('snake-tiles')
204+
```
205+
206+
Třída `Path` má metodu `glob()`, která nám ze zadané cesty umí vrátit sekvenci
207+
s názvy souborů dle argumentem zadaných kritérií. My potřebujeme všechny soubory
208+
s příponou `.png` bez ohledu na jméno. Jakýkoli řetězec je v regulárních
209+
výrazech označen hvězdičkou (`*`), takže argument pro metodu `glob()` bude
210+
`*.png`, což označuje jakýkoli soubor s příponou `.png`. Jako výsledek
211+
dostaneme sekvenci cest k souborům s obrázky, kterou můžeme projít pomocí cyklu
212+
`for`, a každý obrázek si můžeme načíst do slovníku, kde hodnotou bude samotný obrázek
213+
`pyglet.image` a klíčem jeho název. Z názvu však potřebujeme jen samotný název souboru
214+
bez přípony a názvu složky – ten je uložen v atributu `stem`.
215+
216+
Výsledný slovník by měl vypadat takto:
217+
218+
```
219+
{'right-tongue': <ImageData 64x64>, 'top-tongue': <ImageData 64x64>,
220+
'right-top': <ImageData 64x64>, 'left-bottom': <ImageData 64x64>,
221+
'tail-left': <ImageData 64x64>, 'bottom-tongue': <ImageData 64x64>,
222+
'left-top': <ImageData 64x64>, 'bottom-bottom': <ImageData 64x64>,
223+
...
224+
```
225+
226+
Pokud je tohle pro tebe příliš mnoho nových věcí najednou a nedaří se ti to
227+
vyřešit, zkus to ještě jednou a pak se můžeš podívat na řešení.
228+
229+
{% filter solution %}
230+
```python
231+
from pathlib import Path
232+
233+
import pyglet
234+
235+
TILES_DIRECTORY = Path('snake-tiles')
236+
237+
snake_tiles = {}
238+
for path in TILES_DIRECTORY.glob('*.png'):
239+
snake_tiles[path.stem] = pyglet.image.load(path)
240+
241+
print(snake_tiles)
242+
```
243+
{% endfilter %}
244+
245+
### Housenka
246+
247+
Než se začneme zabývat různými obrázky, uděláme pokus k ověření, že nám vše stále funguje.
248+
Jako mezistupeň od hranatého hada k jeho věrné grafické podobě vytvoř housenku.
249+
Uděláš to jednoduše tak, že místo zeleného čtverce použiješ k vykreslení hada
250+
obrázek `tail-head.png`, který máš ve slovníku načten jako pod klíčem `tail-head`.
251+
252+
Funguje? No výborně! Před pokračováním si u jeho hraní na chvíli odpočiň.
253+
Začne to být náročnější.
254+
255+
{{ figure(
256+
img=static('screenshot-cat.png'),
257+
alt="Housenka",
258+
) }}
259+
260+
### Výběr správných obrázků
261+
262+
Jistě sis všiml{{a}}, že některé obrázky v naší sadě jsou téměř identické a liší
263+
se jen v otočení. V tuhle chvíli máme totiž dvě možnosti, jak vykreslit
264+
celého hada pomocí správných obrázků na správných pozicích:
265+
266+
1. Můžeme vzít jeden obrázek pro tělo, jeden pro ohyb a po jednom pro
267+
hlavu a ocas a ty otáčet tak, jak to bude pro konkrétní kousek hada potřeba.
268+
2. Můžeme využít všech dostupných (různé otočených) obrázků a použít ten
269+
správný obrázek na tom správném místě.
270+
271+
Bod č. 2 je v tuto chvíli snazší a tak budeme pokračovat tímto způsobem.
272+
273+
Jak vybrat správné obrázky na ta správná místa? Jména obrázků (klíče ve slovníku)
274+
obsahují informaci, odkud kam daný obrázek vede. Stačí se tedy při
275+
vykreslování každého kousku hada podívat na umístění jednoho před ním
276+
a jednoho za ním a podle toho vybrat ze slovníku ten správný obrázek.
277+
U každého kousku hada a kousku před i za ním tě budou zajímat jejich
278+
souřadnice, protože podle nich lze velmi snadno poznat, zda je zkoumaný kousek
279+
nalevo, napravo, nahoře, nebo dole.
280+
281+
Způsobů, jak toho docílit, je celá řada a i když se to může zdát jako složitější
282+
úkol, vše potřebné k jeho vyřešení znáš.
283+
284+
{{ figure(
285+
img=static('screenshot-final.png'),
286+
alt="Finální had",
287+
) }}
288+
289+
Odměnou za vyřešení ti bude kompletní grafická hra Had. Gratuluji!
290+
291+
## Optimalizace, úklid
292+
293+
Než se po dokončení základní hry vrhneš na její rozšiřování, měl by se celý kód
294+
uklidit a zpřehlednit, aby se v něm další úpravy dělaly snáze a s menším
295+
rizikem, že se něco pokazí.
296+
297+
Body k zamyšlení:
298+
299+
* Pokud se ti tam opakuje nějaký kousek kódu vícekrát, možná by se dal
300+
vložit do funkce nebo cyklu.
301+
* Mají všechny proměnné smysluplná jména?
302+
* Při vykreslování možná tvoříš pro každý kousek hada nový `Sprite` a ten
303+
je po vykreslení zapomenut. Optimálnější by možná bylo použít seznam
304+
a v něm všechny instance třídy `Sprite` uchovávat a používat znovu a znovu.
305+
`Sprite` přeci můžeme posunout na libovolné místo i změnit obrázek, který
306+
obsahuje.
307+
* Používáš globální proměnné? Nebylo by lepší mít jednu třídu pro stav hry
308+
a v ní všechny podstatné informace a metody?
309+
* Funguje ovládání dle tvých představ nebo by šlo nějak zlepšit?

lessons/projects/snake/info.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
title: Snake
2+
style: md
3+
attribution: Pro PyLadies Ostrava napsal Lumr Balhar, 2018.
4+
license: cc-by-sa-40

0 commit comments

Comments
 (0)