Hra typu Had

Dnes to všechno — třídy, grafiku, seznamy a tak dále – spojíme dohromady do závěrečného projektu. Doufám, že se ti bude líbit!

Naším cílem bude vytvořit klon známé hry Snake (neboli Had) jejíž princip je tu s námi od roku 1976. Největší popularity se Had dočkal díky mobilním telefonům Nokia, kde je jako základní hra dostupný od roku 1998 až dodnes.

Projekt není zase tak složitý, protože jeho základní principy už dobře znáš z domácích projektů a lekcí kurzu. Následující text je tedy spíše zadání než výukový materiál a v projektu jistě narazíš na něco, co jsme společně neprobírali. V takovém případě se neboj zeptat nebo si informace dohledat!

A ještě jedna věc: protože začátečnický kurz končí, začneme kód psát v angličtině, aby se pak dal sdílet s celým světem.

Procházíš-li si projekt doma, je možné, že narazíš na něco s čím si nebudeš vědět rady. Kdyby se to stalo, prosím, ozvi se nám! Rádi ti s projektem pomůžeme.

Logika hry a fáze projektu

Základní princip hry máš v malíčku, pokud jsi dokončila domácí projekt po lekci o seznamech. Pokud jej nemáš, doporučuji se k němu vrátit.

Práci s pygletem jsme dělali v lekci o grafice.

Teď nám nezbývá než princip tolik populární hry a znalosti z kurzu spojit dohromady. Doporučuji začít s čistým souborem v prázdné složce a do hotových programů se koukat jen v případě potřeby.

Jak postupovat, aby se projekt nezdál nedosažitelný už na začátku? Třeba takto:

  1. Promysli si, jak bude hra fungovat a jak přeneseme mřížku s hadem z příkazové řádky do grafického okna.
  2. Vykresli hada do grafického okna (ve formě barevných čtverců)
  3. Přidej funkci, která bude hadem hýbat.
  4. Umožni změnit směr hada pomocí klávesnice.
  5. Nenech hada utéct z herní plochy a nabourat do sebe sama.
  6. Přidej hadovi jídlo a zajisti, aby po jídle rostl.
  7. Vyměň barevné čtverečky za opravdovou grafiku.

Po těchto krocích budeš mít základní hru, ale tou to nekončí, právě naopak! Budeš mít vlastní hru, jejímuž fungování rozumíš jako nikdo jiný, a to je to pravé pro přidávání dalších možností. Fantazii se meze nekladou. Například:

  1. Ve hře mohou být dva nebo třeba tři hadi najednou – každý ovládaný jinými klávesami — navzájem soupeřící o jídlo.
  2. Kromě jídla se mohou na ploše objevovat i jiné objekty – překážky, do kterých nesmí had narazit, otrávené jídlo, které hada zkrátí atp.
  3. Hrací plocha může být nekonečná a když z ní had vyleze, objeví se na druhé straně.

Z příkazové řádky do grafické aplikace

V příkazové řádce měl had souřadnice označující řádek a sloupec. V grafické aplikaci to bude podobné, ale protože pixelů na obrazovce je mnohem více, budeme si muset vytvořit pomyslnou síť stejně velkých čtverců, které nám nahradí řádky a sloupce. Velikost takového čtverce bude konstanta, kterou se vyplatí mít po celou dobu hry k dispozici, aby se podle ní daly vypočítat souřadnice k vykreslení obrázků. Pro začátek řekněme, že ideální velikost takového čtverce bude 64 × 64 pixelů.

Z velikosti čtverce, kterou si můžeme v budoucnu libovolně změnit, a velikosti okna aplikace můžeme vypočítat, kolik se nám do okna takových čtverců vejde na šířku a na výšku a tím i zjistit, kolik pomyslných sloupců a řádků bude naše hrací plocha mít.

Vykreslení hada

Abychom mohli hada vykreslit, potřebujeme si pro začátek uložit jeho souřadnice. K tomu můžeš použít seznam dvojic – stejně jako v domácím projektu. Podobných informací, které se budou v průběhu hry dynamicky měnit, budeme mít už za malou chvíli více. Proto dává smysl si pro stav hry vytvořit třídu, která bude tyto informace obsahovat jako atributy a bude s nimi umět pracovat.

I když se může na začátku zdát vlastní třída jako zbytečná komplikace, později zjistíš, že ne všechno by se dalo snadno udržovat v globálních proměnných.

Had na „šachovnici“ se souřadnicemi

Když už je had definován, budeme potřebovat jednoduchou funkci, která na ta správná místa umístí obrázky. Pro začátek si vystačíme se zeleným čtvercem. Obrázek si stáhni zde a ulož do složky k programu.

Stejně jako na lekci i zde použijeme pro vykreslení Sprite, kterému už při vytvoření můžeme zadat obrázek pro vykreslení a vypočtené souřadnice. Pro jednoduchost stačí Sprite vytvořit, vykreslit a „zapomenout“. Není to ale optimální přístup a tak tohle může být jedním z adeptů pro pozdější vylepšení.

Rozpohybování hada

Aby se mohl had hýbat, potřebuje znát směr pohybu. V příkazové řádce jsme vždy počkali, až nám směr zadá uživatel, ale v opravdové hře se bude had pohybovat sám. Bude tedy potřeba nějaký atribut v naší třídě, kde bude směr neustále uložen a měnit se bude podle stisknutých kláves v dalším kroku.

Směr pohybu může být uložen v libovolné podobě – světové strany, slovní označení strany, nebo třeba dvojice s číselným označením pohybu ((0, 1) pro pohyb nahoru, (-1, 0) pro pohyb doleva atp.). Podle vybraného formátu pak bude třeba směr zpracovat.

Pro tuhle chvíli mu tedy bude stačit nastavit směr napevno a napsat funkci, nebo metodu, která hadem pohne. Pohyb bude probíhat naprosto stejně jako v příkazové řádce – přidáme do seznamu souřadnice, kde by měla být „nová hlava“ a umažeme poslední kousek hada.

Protože se pohyb má provádět pravidelně, bude potřeba tuto operaci provádět automaticky v pravidelných intervalech. pyglet.clock.schedule_interval je zde jasná volba.

Ovládání pomocí klávesnice

Reagovat na stisknuté klávesy jsme se už taky učili. Teď to tedy využijeme, abychom dokázali změnit nastavený směr pohybu z předchozího bodu. Bude pro to samozřejmě potřeba funkce, kterou v pygletu zaregistrujeme pro spuštění po stisku klávesy.

Protože had už se nám v závislosti na směru pohybuje, měl by začít reagovat na jeho změnu.

V tuto chvíli:

  • Směr se mění podle stisknuté klávesy.
  • Had se sám pohybuje podle zadaného směru.
  • Nová pozice hada se automaticky vykresluje jako zelené čtverečky.

Vida, máme hotový základ!

Nenechme ho utéct

Had už se nám hýbe podle našich představ, ale stačí ho nechat chvíli bez dozoru a uteče nám z hrací plochy. Tomu není těžké zabránit, když víme, že žádná souřadnice hada nesmí být menší než nula a větší než je velikost hrací plochy. Kontrolovat je potřeba souřadnice jeho hlavy, která bude vždy všude jako první.

Reagovat na náraz do zdi se dá mnoha způsoby. Nejjednodušší by asi bylo ukončit hru, ale to by se pak hráč nemohl podívat na tu šlamastiku, do které se dostal. Proto bude lepší místo toho pouze zastavit časovač, který se stará o pohyb hada.

Stejným způsobem a na stejném místě v programu bude třeba vyřešit i situaci, kdy had narazí sám do sebe.

Jen ať jí, hlavně že mu chutná

Jezdit s hadem po hrací ploše může být chvíli zábava, ale protože had neroste, není to žádná výzva. A aby mohl růst, potřebuje jíst.

K tomu budeš potřebovat další globálně dostupný seznam (nejlépe atribut existující třídy), který bude obsahovat informace (souřadnice) o existujícím jídle na hrací ploše. Navíc bude potřeba mít k dispozici metodu, která bude umět jídlo na hrací plochu přidat.

Záleží jen na tobě, zda se bude nové jídlo objevovat, když had jedno z existujících sní, nebo automaticky v pravidelných intervalech.

Jídlo vykreslíme stejným způsobem jako hada (ve stejné funkci/metodě) a jako obrázek použijeme třeba jablko.

První závan grafiky :-)

Čtverečky ven, grafiku sem

Čtverečky jsou fajn, ale hra by měla lahodit oku a had by měl vypadat jako had. K tomu máme připravenou sadu obrázků - ke stažení zde. Archiv si rozbal do adresáře s hrou tak, aby adresář snake-tiles byl na stejné úrovni jako soubor s programem.

Kousky hada

Načtení všech obrázků ze složky

Nejdříve si načteme všechny obrázky do hry, abychom je pak mohli bez potíží použít. Protože se nechceme opakovat (DRY), bude potřeba to udělat nějak poloautomaticky. Python obsahuje knihovnu pathlib, která umí velmi přehledně pracovat s cestami k souborům a třeba nám dát i seznam všech souborů ve složce.

Nejdříve si z této knihovny naimportujeme třídu Path, která reprezentuje soubor či složku na disku a vytvoříme z ní instanci, která bude ukazovat do naší složky s obrázky.

from pathlib import Path

TILES_DIRECTORY = Path('snake-tiles')

Třída Path má metodu glob(), která nám ze zadané cesty umí vrátit sekvenci s názvy souborů dle argumentem zadaných kritérií. My potřebujeme všechny soubory s příponou .png bez ohledu na jméno. Jakýkoli řetězec je v regulárních výrazech označen hvězdičkou (*), takže argument pro metodu glob() bude *.png, což označuje jakýkoli soubor s příponou .png. Jako výsledek dostaneme sekvenci cest k souborům s obrázky, kterou můžeme projít pomocí cyklu for, a každý obrázek si můžeme načíst do slovníku, kde hodnotou bude samotný obrázek pyglet.image a klíčem jeho název. Z názvu však potřebujeme jen samotný název souboru bez přípony a názvu složky – ten je uložen v atributu stem.

Výsledný slovník by měl vypadat takto:

{'right-tongue': <ImageData 64x64>, 'top-tongue': <ImageData 64x64>,
 'right-top': <ImageData 64x64>, 'left-bottom': <ImageData 64x64>,
 'tail-left': <ImageData 64x64>, 'bottom-tongue': <ImageData 64x64>,
 'left-top': <ImageData 64x64>, 'bottom-bottom': <ImageData 64x64>,
 ...

Pokud je tohle pro tebe příliš mnoho nových věcí najednou a nedaří se ti to vyřešit, zkus to ještě jednou a pak se můžeš podívat na řešení.

Řešení

Housenka

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. Jako mezistupeň od hranatého hada k jeho věrné grafické podobě vytvoř housenku. Uděláš to jednoduše tak, že místo zeleného čtverce použiješ k vykreslení hada obrázek tail-head.png, který máš ve slovníku načten jako pod klíčem tail-head.

Funguje? No výborně! Před pokračováním si u jeho hraní na chvíli odpočiň. Začne to být náročnější.

Housenka

Výběr správných obrázků

Jistě sis všimla, že některé obrázky v naší sadě jsou téměř identické a liší se jen v otočení. V tuhle chvíli máme totiž dvě možnosti, jak vykreslit celého hada pomocí správných obrázků na správných pozicích:

  1. Můžeme vzít jeden obrázek pro tělo, jeden pro ohyb a po jednom pro hlavu a ocas a ty otáčet tak, jak to bude pro konkrétní kousek hada potřeba.
  2. Můžeme využít všech dostupných (různé otočených) obrázků a použít ten správný obrázek na tom správném místě.

Bod č. 2 je v tuto chvíli snazší a tak budeme pokračovat tímto způsobem.

Jak vybrat správné obrázky na ta správná místa? Jména obrázků (klíče ve slovníku) obsahují informaci, odkud kam daný obrázek vede. Stačí se tedy při vykreslování každého kousku hada podívat na umístění jednoho před ním a jednoho za ním a podle toho vybrat ze slovníku ten správný obrázek. U každého kousku hada a kousku před i za ním tě budou zajímat jejich souřadnice, protože podle nich lze velmi snadno poznat, zda je zkoumaný kousek nalevo, napravo, nahoře, nebo dole.

Způsobů, jak toho docílit, je celá řada a i když se to může zdát jako složitější úkol, vše potřebné k jeho vyřešení znáš.

Finální had

Odměnou za vyřešení ti bude kompletní grafická hra Had. Gratuluji!

Optimalizace, úklid

Než se po dokončení základní hry vrhneš na její rozšiřování, měl by se celý kód uklidit a zpřehlednit, aby se v něm další úpravy dělaly snáze a s menším rizikem, že se něco pokazí.

Body k zamyšlení:

  • Pokud se ti tam opakuje nějaký kousek kódu vícekrát, možná by se dal vložit do funkce nebo cyklu.
  • Mají všechny proměnné smysluplná jména?
  • Při vykreslování možná tvoříš pro každý kousek hada nový Sprite a ten je po vykreslení zapomenut. Optimálnější by možná bylo použít seznam a v něm všechny instance třídy Sprite uchovávat a používat znovu a znovu. Sprite přeci můžeme posunout na libovolné místo i změnit obrázek, který obsahuje.
  • Používáš globální proměnné? Nebylo by lepší mít jednu třídu pro stav hry a v ní všechny podstatné informace a metody?
  • Funguje ovládání dle tvých představ nebo by šlo nějak zlepšit?
{
  "data": {
    "sessionMaterial": {
      "id": "session-material:2019/pyladies-ostrava-jaro:asteroids:1",
      "title": "Snake",
      "html": "\n          \n    \n\n    <h1>Hra typu Had</h1>\n<p>Dnes to v&#x161;echno &#x2014; t&#x159;&#xED;dy, grafiku, seznamy a&#xA0;tak d&#xE1;le &#x2013;\nspoj&#xED;me dohromady do z&#xE1;v&#x11B;re&#x10D;n&#xE9;ho projektu.\nDouf&#xE1;m, &#x17E;e se ti bude l&#xED;bit!</p>\n<p>Na&#x161;&#xED;m c&#xED;lem bude vytvo&#x159;it klon zn&#xE1;m&#xE9; hry <a href=\"https://en.wikipedia.org/wiki/Snake_(video_game_genre)\">Snake (neboli Had)</a>\njej&#xED;&#x17E; princip je tu s&#xA0;n&#xE1;mi od roku 1976. Nejv&#x11B;t&#x161;&#xED; popularity se Had do&#x10D;kal\nd&#xED;ky mobiln&#xED;m telefon&#x16F;m Nokia, kde je jako z&#xE1;kladn&#xED; hra dostupn&#xFD; od roku 1998\na&#x17E; dodnes.</p>\n<p>Projekt nen&#xED; zase tak slo&#x17E;it&#xFD;, proto&#x17E;e jeho z&#xE1;kladn&#xED; principy u&#x17E; dob&#x159;e zn&#xE1;&#x161;\nz&#xA0;dom&#xE1;c&#xED;ch projekt&#x16F; a&#xA0;lekc&#xED; kurzu. N&#xE1;sleduj&#xED;c&#xED; text je tedy sp&#xED;&#x161;e\nzad&#xE1;n&#xED; ne&#x17E; v&#xFD;ukov&#xFD; materi&#xE1;l a&#xA0;v&#xA0;projektu jist&#x11B; naraz&#xED;&#x161; na n&#x11B;co, co jsme\nspole&#x10D;n&#x11B; neprob&#xED;rali. V&#xA0;takov&#xE9;m p&#x159;&#xED;pad&#x11B; se neboj zeptat nebo si informace\ndohledat!</p>\n<p>A&#xA0;je&#x161;t&#x11B; jedna v&#x11B;c: proto&#x17E;e za&#x10D;&#xE1;te&#x10D;nick&#xFD; kurz kon&#x10D;&#xED;,\nza&#x10D;neme k&#xF3;d ps&#xE1;t v&#xA0;angli&#x10D;tin&#x11B;, aby se pak dal sd&#xED;let s&#xA0;cel&#xFD;m sv&#x11B;tem.</p>\n<div class=\"admonition note\"><p>Proch&#xE1;z&#xED;&#x161;-li si projekt doma, je mo&#x17E;n&#xE9;, &#x17E;e naraz&#xED;&#x161; na\nn&#x11B;co s&#xA0;&#x10D;&#xED;m si nebude&#x161; v&#x11B;d&#x11B;t rady.\nKdyby se to stalo, pros&#xED;m, ozvi se n&#xE1;m!\nR&#xE1;di ti s&#xA0;projektem pom&#x16F;&#x17E;eme.</p>\n</div><h2>Logika hry a&#xA0;f&#xE1;ze projektu</h2>\n<p>Z&#xE1;kladn&#xED; princip hry m&#xE1;&#x161; v&#xA0;mal&#xED;&#x10D;ku, pokud jsi dokon&#x10D;ila dom&#xE1;c&#xED; projekt\npo <a href=\"/2019/pyladies-ostrava-jaro/beginners/list/\">lekci o&#xA0;seznamech</a>. Pokud jej nem&#xE1;&#x161;, doporu&#x10D;uji\nse k n&#x11B;mu vr&#xE1;tit.</p>\n<p>Pr&#xE1;ci s&#xA0;pygletem jsme d&#x11B;lali v <a href=\"/2019/pyladies-ostrava-jaro/intro/pyglet/\">lekci o&#xA0;grafice</a>.</p>\n<p>Te&#x10F; n&#xE1;m nezb&#xFD;v&#xE1; ne&#x17E; princip tolik popul&#xE1;rn&#xED; hry a&#xA0;znalosti z&#xA0;kurzu spojit\ndohromady. Doporu&#x10D;uji za&#x10D;&#xED;t s&#xA0;&#x10D;ist&#xFD;m souborem v&#xA0;pr&#xE1;zdn&#xE9; slo&#x17E;ce a&#xA0;do hotov&#xFD;ch\nprogram&#x16F; se koukat jen v p&#x159;&#xED;pad&#x11B; pot&#x159;eby.</p>\n<p>Jak postupovat, aby se projekt nezd&#xE1;l nedosa&#x17E;iteln&#xFD; u&#x17E; na za&#x10D;&#xE1;tku? T&#x159;eba takto:</p>\n<ol>\n<li>Promysli si, jak bude hra fungovat a&#xA0;jak p&#x159;eneseme m&#x159;&#xED;&#x17E;ku s&#xA0;hadem\nz&#xA0;p&#x159;&#xED;kazov&#xE9; &#x159;&#xE1;dky do grafick&#xE9;ho okna.</li>\n<li>Vykresli hada do grafick&#xE9;ho okna (ve form&#x11B; barevn&#xFD;ch &#x10D;tverc&#x16F;)</li>\n<li>P&#x159;idej funkci, kter&#xE1; bude hadem h&#xFD;bat.</li>\n<li>Umo&#x17E;ni zm&#x11B;nit sm&#x11B;r hada pomoc&#xED; kl&#xE1;vesnice.</li>\n<li>Nenech hada ut&#xE9;ct z&#xA0;hern&#xED; plochy a&#xA0;nabourat do sebe sama.</li>\n<li>P&#x159;idej hadovi j&#xED;dlo a&#xA0;zajisti, aby po j&#xED;dle rostl.</li>\n<li>Vym&#x11B;&#x148; barevn&#xE9; &#x10D;tvere&#x10D;ky za opravdovou grafiku.</li>\n</ol>\n<p>Po t&#x11B;chto kroc&#xED;ch bude&#x161; m&#xED;t z&#xE1;kladn&#xED; hru, ale tou to nekon&#x10D;&#xED;, pr&#xE1;v&#x11B; naopak!\nBude&#x161; m&#xED;t vlastn&#xED; hru, jej&#xED;mu&#x17E; fungov&#xE1;n&#xED; rozum&#xED;&#x161; jako nikdo jin&#xFD;, a&#xA0;to je to prav&#xE9; pro\np&#x159;id&#xE1;v&#xE1;n&#xED; dal&#x161;&#xED;ch mo&#x17E;nost&#xED;. Fantazii se meze nekladou. Nap&#x159;&#xED;klad:</p>\n<ol>\n<li>Ve h&#x159;e mohou b&#xFD;t dva nebo t&#x159;eba t&#x159;i hadi najednou &#x2013; ka&#x17E;d&#xFD; ovl&#xE1;dan&#xFD;\njin&#xFD;mi kl&#xE1;vesami &#x2014; navz&#xE1;jem soupe&#x159;&#xED;c&#xED; o&#xA0;j&#xED;dlo.</li>\n<li>Krom&#x11B; j&#xED;dla se mohou na plo&#x161;e objevovat i&#xA0;jin&#xE9; objekty &#x2013; p&#x159;ek&#xE1;&#x17E;ky,\ndo kter&#xFD;ch nesm&#xED; had narazit, otr&#xE1;ven&#xE9; j&#xED;dlo, kter&#xE9; hada zkr&#xE1;t&#xED; atp.</li>\n<li>Hrac&#xED; plocha m&#x16F;&#x17E;e b&#xFD;t nekone&#x10D;n&#xE1; a&#xA0;kdy&#x17E; z&#xA0;n&#xED; had vyleze, objev&#xED; se\nna druh&#xE9; stran&#x11B;.</li>\n</ol>\n<h2>Z&#xA0;p&#x159;&#xED;kazov&#xE9; &#x159;&#xE1;dky do grafick&#xE9; aplikace</h2>\n<p>V&#xA0;p&#x159;&#xED;kazov&#xE9; &#x159;&#xE1;dce m&#x11B;l had sou&#x159;adnice ozna&#x10D;uj&#xED;c&#xED; &#x159;&#xE1;dek a&#xA0;sloupec. V&#xA0;grafick&#xE9;\naplikaci to bude podobn&#xE9;, ale proto&#x17E;e pixel&#x16F; na obrazovce je mnohem v&#xED;ce, budeme\nsi muset vytvo&#x159;it pomyslnou s&#xED;&#x165; stejn&#x11B; velk&#xFD;ch &#x10D;tverc&#x16F;, kter&#xE9; n&#xE1;m nahrad&#xED;\n&#x159;&#xE1;dky a&#xA0;sloupce. Velikost takov&#xE9;ho &#x10D;tverce bude konstanta, kterou se vyplat&#xED;\nm&#xED;t po celou dobu hry k&#xA0;dispozici, aby se podle n&#xED; daly vypo&#x10D;&#xED;tat\nsou&#x159;adnice k&#xA0;vykreslen&#xED; obr&#xE1;zk&#x16F;. Pro za&#x10D;&#xE1;tek &#x159;ekn&#x11B;me, &#x17E;e ide&#xE1;ln&#xED; velikost\ntakov&#xE9;ho &#x10D;tverce bude 64&#xA0;&#xD7;&#xA0;64 pixel&#x16F;.</p>\n<p>Z&#xA0;velikosti &#x10D;tverce, kterou si m&#x16F;&#x17E;eme v&#xA0;budoucnu libovoln&#x11B; zm&#x11B;nit,\na&#xA0;velikosti okna aplikace m&#x16F;&#x17E;eme vypo&#x10D;&#xED;tat, kolik se n&#xE1;m do okna takov&#xFD;ch\n&#x10D;tverc&#x16F; vejde na &#x161;&#xED;&#x159;ku a&#xA0;na v&#xFD;&#x161;ku a&#xA0;t&#xED;m i&#xA0;zjistit, kolik pomysln&#xFD;ch\nsloupc&#x16F; a&#xA0;&#x159;&#xE1;dk&#x16F; bude na&#x161;e hrac&#xED; plocha m&#xED;t.</p>\n<h2>Vykreslen&#xED; hada</h2>\n<p>Abychom mohli hada vykreslit, pot&#x159;ebujeme si pro za&#x10D;&#xE1;tek ulo&#x17E;it jeho sou&#x159;adnice.\nK&#xA0;tomu m&#x16F;&#x17E;e&#x161; pou&#x17E;&#xED;t seznam dvojic &#x2013; stejn&#x11B; jako v&#xA0;dom&#xE1;c&#xED;m projektu. Podobn&#xFD;ch\ninformac&#xED;, kter&#xE9; se budou v&#xA0;pr&#x16F;b&#x11B;hu hry dynamicky m&#x11B;nit, budeme m&#xED;t u&#x17E;\nza malou chv&#xED;li v&#xED;ce. Proto d&#xE1;v&#xE1; smysl si pro stav hry vytvo&#x159;it t&#x159;&#xED;du, kter&#xE1;\nbude tyto informace obsahovat jako atributy a&#xA0;bude s&#xA0;nimi um&#x11B;t pracovat.</p>\n<div class=\"admonition note\"><p>I&#xA0;kdy&#x17E; se m&#x16F;&#x17E;e na za&#x10D;&#xE1;tku zd&#xE1;t vlastn&#xED; t&#x159;&#xED;da jako zbyte&#x10D;n&#xE1;\nkomplikace, pozd&#x11B;ji zjist&#xED;&#x161;, &#x17E;e ne v&#x161;echno by se dalo snadno udr&#x17E;ovat v&#xA0;glob&#xE1;ln&#xED;ch\nprom&#x11B;nn&#xFD;ch.</p>\n</div><p><span class=\"figure\"><a href=\"/2019/pyladies-ostrava-jaro/projects/snake/static/coords.svg\"><img src=\"/2019/pyladies-ostrava-jaro/projects/snake/static/coords.svg\" alt=\"Had na &#x201E;&#x161;achovnici&#x201C; se sou&#x159;adnicemi\"></a></span></p>\n<p>Kdy&#x17E; u&#x17E; je had definov&#xE1;n, budeme pot&#x159;ebovat jednoduchou funkci, kter&#xE1;\nna ta spr&#xE1;vn&#xE1; m&#xED;sta um&#xED;st&#xED; obr&#xE1;zky. Pro za&#x10D;&#xE1;tek si vysta&#x10D;&#xED;me se zelen&#xFD;m\n&#x10D;tvercem. Obr&#xE1;zek si <a href=\"/2019/pyladies-ostrava-jaro/projects/snake/static/green.png\">st&#xE1;hni zde</a> a&#xA0;ulo&#x17E;\ndo slo&#x17E;ky k&#xA0;programu.</p>\n<p>Stejn&#x11B; jako na lekci i&#xA0;zde pou&#x17E;ijeme pro vykreslen&#xED; <code>Sprite</code>, kter&#xE9;mu u&#x17E; p&#x159;i\nvytvo&#x159;en&#xED; m&#x16F;&#x17E;eme zadat obr&#xE1;zek pro vykreslen&#xED; a&#xA0;vypo&#x10D;ten&#xE9; sou&#x159;adnice.\nPro jednoduchost sta&#x10D;&#xED; <code>Sprite</code> vytvo&#x159;it, vykreslit a&#xA0;&#x201E;zapomenout&#x201C;. Nen&#xED; to ale\noptim&#xE1;ln&#xED; p&#x159;&#xED;stup a&#xA0;tak tohle m&#x16F;&#x17E;e b&#xFD;t jedn&#xED;m z&#xA0;adept&#x16F; pro pozd&#x11B;j&#x161;&#xED; vylep&#x161;en&#xED;.</p>\n<h2>Rozpohybov&#xE1;n&#xED; hada</h2>\n<p>Aby se mohl had h&#xFD;bat, pot&#x159;ebuje zn&#xE1;t sm&#x11B;r pohybu. V&#xA0;p&#x159;&#xED;kazov&#xE9; &#x159;&#xE1;dce jsme\nv&#x17E;dy po&#x10D;kali, a&#x17E; n&#xE1;m sm&#x11B;r zad&#xE1; u&#x17E;ivatel, ale v&#xA0;opravdov&#xE9; h&#x159;e se bude had\npohybovat s&#xE1;m. Bude tedy pot&#x159;eba n&#x11B;jak&#xFD; atribut v&#xA0;na&#x161;&#xED; t&#x159;&#xED;d&#x11B;, kde bude sm&#x11B;r\nneust&#xE1;le ulo&#x17E;en a&#xA0;m&#x11B;nit se bude podle stisknut&#xFD;ch kl&#xE1;ves v&#xA0;dal&#x161;&#xED;m kroku.</p>\n<p>Sm&#x11B;r pohybu m&#x16F;&#x17E;e b&#xFD;t ulo&#x17E;en v&#xA0;libovoln&#xE9; podob&#x11B; &#x2013; sv&#x11B;tov&#xE9; strany, slovn&#xED;\nozna&#x10D;en&#xED; strany, nebo t&#x159;eba dvojice s &#x10D;&#xED;seln&#xFD;m ozna&#x10D;en&#xED;m pohybu\n(<code>(0, 1)</code> pro pohyb nahoru, <code>(-1, 0)</code> pro pohyb doleva atp.). Podle vybran&#xE9;ho\nform&#xE1;tu pak bude t&#x159;eba sm&#x11B;r zpracovat.</p>\n<p>Pro tuhle chv&#xED;li mu tedy bude sta&#x10D;it nastavit sm&#x11B;r napevno a&#xA0;napsat funkci,\nnebo metodu, kter&#xE1; hadem pohne. Pohyb bude prob&#xED;hat\nnaprosto stejn&#x11B; jako v&#xA0;p&#x159;&#xED;kazov&#xE9; &#x159;&#xE1;dce &#x2013; p&#x159;id&#xE1;me do seznamu sou&#x159;adnice,\nkde by m&#x11B;la b&#xFD;t &#x201E;nov&#xE1; hlava&#x201C; a&#xA0;uma&#x17E;eme posledn&#xED; kousek hada.</p>\n<p>Proto&#x17E;e se pohyb m&#xE1; prov&#xE1;d&#x11B;t pravideln&#x11B;, bude pot&#x159;eba tuto operaci prov&#xE1;d&#x11B;t\nautomaticky v&#xA0;pravideln&#xFD;ch intervalech. <code>pyglet.clock.schedule_interval</code> je\nzde jasn&#xE1; volba.</p>\n<h2>Ovl&#xE1;d&#xE1;n&#xED; pomoc&#xED; kl&#xE1;vesnice</h2>\n<p>Reagovat na stisknut&#xE9; kl&#xE1;vesy jsme se u&#x17E; taky u&#x10D;ili. Te&#x10F; to tedy vyu&#x17E;ijeme,\nabychom dok&#xE1;zali zm&#x11B;nit nastaven&#xFD; sm&#x11B;r pohybu z&#xA0;p&#x159;edchoz&#xED;ho bodu. Bude pro to\nsamoz&#x159;ejm&#x11B; pot&#x159;eba funkce, kterou v&#xA0;pygletu zaregistrujeme pro spu&#x161;t&#x11B;n&#xED; po\nstisku kl&#xE1;vesy.</p>\n<p>Proto&#x17E;e had u&#x17E; se n&#xE1;m v&#xA0;z&#xE1;vislosti na sm&#x11B;ru pohybuje, m&#x11B;l by za&#x10D;&#xED;t reagovat\nna jeho zm&#x11B;nu.</p>\n<p>V tuto chv&#xED;li:</p>\n<ul>\n<li>Sm&#x11B;r se m&#x11B;n&#xED; podle stisknut&#xE9; kl&#xE1;vesy.</li>\n<li>Had se s&#xE1;m pohybuje podle zadan&#xE9;ho sm&#x11B;ru.</li>\n<li>Nov&#xE1; pozice hada se automaticky vykresluje jako zelen&#xE9; &#x10D;tvere&#x10D;ky.</li>\n</ul>\n<p>Vida, m&#xE1;me hotov&#xFD; z&#xE1;klad!</p>\n<h2>Nenechme ho ut&#xE9;ct</h2>\n<p>Had u&#x17E; se n&#xE1;m h&#xFD;be podle na&#x161;ich p&#x159;edstav, ale sta&#x10D;&#xED; ho nechat chv&#xED;li bez dozoru\na&#xA0;ute&#x10D;e n&#xE1;m z&#xA0;hrac&#xED; plochy. Tomu nen&#xED; t&#x11B;&#x17E;k&#xE9; zabr&#xE1;nit, kdy&#x17E; v&#xED;me, &#x17E;e &#x17E;&#xE1;dn&#xE1;\nsou&#x159;adnice hada nesm&#xED; b&#xFD;t men&#x161;&#xED; ne&#x17E; nula a&#xA0;v&#x11B;t&#x161;&#xED; ne&#x17E; je velikost hrac&#xED; plochy.\nKontrolovat je pot&#x159;eba sou&#x159;adnice jeho hlavy, kter&#xE1; bude v&#x17E;dy v&#x161;ude jako prvn&#xED;.</p>\n<p>Reagovat na n&#xE1;raz do zdi se d&#xE1; mnoha zp&#x16F;soby. Nejjednodu&#x161;&#x161;&#xED; by asi bylo\nukon&#x10D;it hru, ale to by se pak hr&#xE1;&#x10D; nemohl pod&#xED;vat na tu &#x161;lamastiku, do kter&#xE9;\nse dostal. Proto bude lep&#x161;&#xED; m&#xED;sto toho pouze zastavit &#x10D;asova&#x10D;, kter&#xFD; se star&#xE1;\no&#xA0;pohyb hada.</p>\n<p>Stejn&#xFD;m zp&#x16F;sobem a&#xA0;na stejn&#xE9;m m&#xED;st&#x11B; v&#xA0;programu bude t&#x159;eba vy&#x159;e&#x161;it i&#xA0;situaci,\nkdy had naraz&#xED; s&#xE1;m do sebe.</p>\n<h2>Jen a&#x165; j&#xED;, hlavn&#x11B; &#x17E;e mu chutn&#xE1;</h2>\n<p>Jezdit s hadem po hrac&#xED; plo&#x161;e m&#x16F;&#x17E;e b&#xFD;t chv&#xED;li z&#xE1;bava, ale proto&#x17E;e had neroste,\nnen&#xED; to &#x17E;&#xE1;dn&#xE1; v&#xFD;zva. A&#xA0;aby mohl r&#x16F;st, pot&#x159;ebuje j&#xED;st.</p>\n<p>K&#xA0;tomu bude&#x161; pot&#x159;ebovat dal&#x161;&#xED; glob&#xE1;ln&#x11B; dostupn&#xFD; seznam (nejl&#xE9;pe atribut\nexistuj&#xED;c&#xED; t&#x159;&#xED;dy), kter&#xFD; bude obsahovat informace (sou&#x159;adnice) o&#xA0;existuj&#xED;c&#xED;m\nj&#xED;dle na hrac&#xED; plo&#x161;e. Nav&#xED;c bude pot&#x159;eba m&#xED;t k&#xA0;dispozici metodu, kter&#xE1; bude\num&#x11B;t j&#xED;dlo na hrac&#xED; plochu p&#x159;idat.</p>\n<p>Z&#xE1;le&#x17E;&#xED; jen na tob&#x11B;, zda se bude nov&#xE9; j&#xED;dlo objevovat, kdy&#x17E; had jedno\nz&#xA0;existuj&#xED;c&#xED;ch sn&#xED;, nebo automaticky v&#xA0;pravideln&#xFD;ch intervalech.</p>\n<p>J&#xED;dlo vykresl&#xED;me stejn&#xFD;m zp&#x16F;sobem jako hada (ve stejn&#xE9; funkci/metod&#x11B;) a&#xA0;jako\nobr&#xE1;zek pou&#x17E;ijeme t&#x159;eba <a href=\"/2019/pyladies-ostrava-jaro/projects/snake/static/apple.png\">jablko</a>.</p>\n<p>Prvn&#xED; z&#xE1;van grafiky :-)</p>\n<h2>&#x10C;tvere&#x10D;ky ven, grafiku sem</h2>\n<p>&#x10C;tvere&#x10D;ky jsou fajn, ale hra by m&#x11B;la lahodit oku a&#xA0;had by m&#x11B;l vypadat jako had.\nK&#xA0;tomu m&#xE1;me p&#x159;ipravenou sadu obr&#xE1;zk&#x16F; - <a href=\"/2019/pyladies-ostrava-jaro/projects/snake/static/snake-tiles.zip\">ke sta&#x17E;en&#xED; zde</a>.\nArchiv si rozbal do adres&#xE1;&#x159;e s&#xA0;hrou tak, aby adres&#xE1;&#x159; <code>snake-tiles</code> byl na stejn&#xE9;\n&#xFA;rovni jako soubor s programem.</p>\n<p><span class=\"figure\"><a href=\"/2019/pyladies-ostrava-jaro/projects/snake/static/snake-tiles.png\"><img src=\"/2019/pyladies-ostrava-jaro/projects/snake/static/snake-tiles.png\" alt=\"Kousky hada\"></a></span></p>\n<h3>Na&#x10D;ten&#xED; v&#x161;ech obr&#xE1;zk&#x16F; ze slo&#x17E;ky</h3>\n<p>Nejd&#x159;&#xED;ve si na&#x10D;teme v&#x161;echny obr&#xE1;zky do hry, abychom je pak mohli bez pot&#xED;&#x17E;&#xED;\npou&#x17E;&#xED;t. Proto&#x17E;e se nechceme opakovat (DRY), bude pot&#x159;eba to ud&#x11B;lat n&#x11B;jak\npoloautomaticky. Python obsahuje knihovnu <a href=\"https://docs.python.org/3/library/pathlib.html\"><code>pathlib</code></a>,\nkter&#xE1; um&#xED; velmi p&#x159;ehledn&#x11B; pracovat s&#xA0;cestami k&#xA0;soubor&#x16F;m a&#xA0;t&#x159;eba n&#xE1;m d&#xE1;t\ni&#xA0;seznam v&#x161;ech soubor&#x16F; ve slo&#x17E;ce.</p>\n<p>Nejd&#x159;&#xED;ve si z&#xA0;t&#xE9;to knihovny naimportujeme t&#x159;&#xED;du <code>Path</code>, kter&#xE1; reprezentuje\nsoubor &#x10D;i slo&#x17E;ku na disku a&#xA0;vytvo&#x159;&#xED;me z&#xA0;n&#xED; instanci, kter&#xE1; bude\nukazovat do na&#x161;&#xED; slo&#x17E;ky s&#xA0;obr&#xE1;zky.</p>\n<div class=\"highlight\"><pre><span></span><span class=\"kn\">from</span> <span class=\"nn\">pathlib</span> <span class=\"kn\">import</span> <span class=\"n\">Path</span>\n\n<span class=\"n\">TILES_DIRECTORY</span> <span class=\"o\">=</span> <span class=\"n\">Path</span><span class=\"p\">(</span><span class=\"s1\">&apos;snake-tiles&apos;</span><span class=\"p\">)</span>\n</pre></div><p>T&#x159;&#xED;da <code>Path</code> m&#xE1; metodu <code>glob()</code>, kter&#xE1; n&#xE1;m ze zadan&#xE9; cesty um&#xED; vr&#xE1;tit sekvenci\ns&#xA0;n&#xE1;zvy soubor&#x16F; dle argumentem zadan&#xFD;ch krit&#xE9;ri&#xED;. My pot&#x159;ebujeme v&#x161;echny soubory\ns&#xA0;p&#x159;&#xED;ponou <code>.png</code> bez ohledu na jm&#xE9;no. Jak&#xFD;koli &#x159;et&#x11B;zec je v&#xA0;regul&#xE1;rn&#xED;ch\nv&#xFD;razech ozna&#x10D;en hv&#x11B;zdi&#x10D;kou (<code>*</code>), tak&#x17E;e argument pro metodu <code>glob()</code> bude\n<code>*.png</code>, co&#x17E; ozna&#x10D;uje jak&#xFD;koli soubor s&#xA0;p&#x159;&#xED;ponou <code>.png</code>. Jako v&#xFD;sledek\ndostaneme sekvenci cest k&#xA0;soubor&#x16F;m s&#xA0;obr&#xE1;zky, kterou m&#x16F;&#x17E;eme proj&#xED;t pomoc&#xED; cyklu\n<code>for</code>, a&#xA0;ka&#x17E;d&#xFD; obr&#xE1;zek si m&#x16F;&#x17E;eme na&#x10D;&#xED;st do slovn&#xED;ku, kde hodnotou bude samotn&#xFD; obr&#xE1;zek\n<code>pyglet.image</code> a&#xA0;kl&#xED;&#x10D;em jeho n&#xE1;zev. Z&#xA0;n&#xE1;zvu v&#x161;ak pot&#x159;ebujeme jen samotn&#xFD; n&#xE1;zev souboru\nbez p&#x159;&#xED;pony a&#xA0;n&#xE1;zvu slo&#x17E;ky &#x2013; ten je ulo&#x17E;en v&#xA0;atributu <code>stem</code>.</p>\n<p>V&#xFD;sledn&#xFD; slovn&#xED;k by m&#x11B;l vypadat takto:</p>\n<div class=\"highlight\"><pre><code>{&apos;right-tongue&apos;: &lt;ImageData 64x64&gt;, &apos;top-tongue&apos;: &lt;ImageData 64x64&gt;,\n &apos;right-top&apos;: &lt;ImageData 64x64&gt;, &apos;left-bottom&apos;: &lt;ImageData 64x64&gt;,\n &apos;tail-left&apos;: &lt;ImageData 64x64&gt;, &apos;bottom-tongue&apos;: &lt;ImageData 64x64&gt;,\n &apos;left-top&apos;: &lt;ImageData 64x64&gt;, &apos;bottom-bottom&apos;: &lt;ImageData 64x64&gt;,\n ...</code></pre></div><p>Pokud je tohle pro tebe p&#x159;&#xED;li&#x161; mnoho nov&#xFD;ch v&#x11B;c&#xED; najednou a&#xA0;neda&#x159;&#xED; se ti to\nvy&#x159;e&#x161;it, zkus to je&#x161;t&#x11B; jednou a&#xA0;pak se m&#x16F;&#x17E;e&#x161; pod&#xED;vat na &#x159;e&#x161;en&#xED;.</p>\n<div class=\"solution\" id=\"solution-0\">\n    <h3>&#x158;e&#x161;en&#xED;</h3>\n    <div class=\"solution-cover\">\n        <a href=\"/2019/pyladies-ostrava-jaro/projects/snake/index/solutions/0/\"><span class=\"link-text\">Uk&#xE1;zat &#x159;e&#x161;en&#xED;</span></a>\n    </div>\n    <div class=\"solution-body\" aria-hidden=\"true\">\n        <div class=\"highlight\"><pre><span></span><span class=\"kn\">from</span> <span class=\"nn\">pathlib</span> <span class=\"kn\">import</span> <span class=\"n\">Path</span>\n\n<span class=\"kn\">import</span> <span class=\"nn\">pyglet</span>\n\n<span class=\"n\">TILES_DIRECTORY</span> <span class=\"o\">=</span> <span class=\"n\">Path</span><span class=\"p\">(</span><span class=\"s1\">&apos;snake-tiles&apos;</span><span class=\"p\">)</span>\n\n<span class=\"n\">snake_tiles</span> <span class=\"o\">=</span> <span class=\"p\">{}</span>\n<span class=\"k\">for</span> <span class=\"n\">path</span> <span class=\"ow\">in</span> <span class=\"n\">TILES_DIRECTORY</span><span class=\"o\">.</span><span class=\"n\">glob</span><span class=\"p\">(</span><span class=\"s1\">&apos;*.png&apos;</span><span class=\"p\">):</span>\n    <span class=\"n\">snake_tiles</span><span class=\"p\">[</span><span class=\"n\">path</span><span class=\"o\">.</span><span class=\"n\">stem</span><span class=\"p\">]</span> <span class=\"o\">=</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">image</span><span class=\"o\">.</span><span class=\"n\">load</span><span class=\"p\">(</span><span class=\"n\">path</span><span class=\"p\">)</span>\n\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">snake_tiles</span><span class=\"p\">)</span>\n</pre></div>\n    </div>\n</div><h3>Housenka</h3>\n<p>Ne&#x17E; se za&#x10D;neme zab&#xFD;vat r&#x16F;zn&#xFD;mi obr&#xE1;zky, ud&#x11B;l&#xE1;me pokus k&#xA0;ov&#x11B;&#x159;en&#xED;, &#x17E;e n&#xE1;m v&#x161;e st&#xE1;le funguje.\nJako mezistupe&#x148; od hranat&#xE9;ho hada k&#xA0;jeho v&#x11B;rn&#xE9; grafick&#xE9; podob&#x11B; vytvo&#x159; housenku.\nUd&#x11B;l&#xE1;&#x161; to jednodu&#x161;e tak, &#x17E;e m&#xED;sto zelen&#xE9;ho &#x10D;tverce pou&#x17E;ije&#x161; k vykreslen&#xED; hada\nobr&#xE1;zek <code>tail-head.png</code>, kter&#xFD; m&#xE1;&#x161; ve slovn&#xED;ku na&#x10D;ten jako pod kl&#xED;&#x10D;em <code>tail-head</code>.</p>\n<p>Funguje? No v&#xFD;born&#x11B;! P&#x159;ed pokra&#x10D;ov&#xE1;n&#xED;m si u&#xA0;jeho hran&#xED; na chv&#xED;li odpo&#x10D;i&#x148;.\nZa&#x10D;ne to b&#xFD;t n&#xE1;ro&#x10D;n&#x11B;j&#x161;&#xED;.</p>\n<p><span class=\"figure\"><a href=\"/2019/pyladies-ostrava-jaro/projects/snake/static/screenshot-cat.png\"><img src=\"/2019/pyladies-ostrava-jaro/projects/snake/static/screenshot-cat.png\" alt=\"Housenka\"></a></span></p>\n<h3>V&#xFD;b&#x11B;r spr&#xE1;vn&#xFD;ch obr&#xE1;zk&#x16F;</h3>\n<p>Jist&#x11B; sis v&#x161;imla, &#x17E;e n&#x11B;kter&#xE9; obr&#xE1;zky v&#xA0;na&#x161;&#xED; sad&#x11B; jsou t&#xE9;m&#x11B;&#x159; identick&#xE9; a&#xA0;li&#x161;&#xED;\nse jen v&#xA0;oto&#x10D;en&#xED;. V&#xA0;tuhle chv&#xED;li m&#xE1;me toti&#x17E; dv&#x11B; mo&#x17E;nosti, jak vykreslit\ncel&#xE9;ho hada pomoc&#xED; spr&#xE1;vn&#xFD;ch obr&#xE1;zk&#x16F; na spr&#xE1;vn&#xFD;ch pozic&#xED;ch:</p>\n<ol>\n<li>M&#x16F;&#x17E;eme vz&#xED;t jeden obr&#xE1;zek pro t&#x11B;lo, jeden pro ohyb a&#xA0;po jednom pro\nhlavu a&#xA0;ocas a&#xA0;ty ot&#xE1;&#x10D;et tak, jak to bude pro konkr&#xE9;tn&#xED; kousek hada pot&#x159;eba.</li>\n<li>M&#x16F;&#x17E;eme vyu&#x17E;&#xED;t v&#x161;ech dostupn&#xFD;ch (r&#x16F;zn&#xE9; oto&#x10D;en&#xFD;ch) obr&#xE1;zk&#x16F; a&#xA0;pou&#x17E;&#xED;t ten\nspr&#xE1;vn&#xFD; obr&#xE1;zek na tom spr&#xE1;vn&#xE9;m m&#xED;st&#x11B;.</li>\n</ol>\n<p>Bod &#x10D;. 2 je v tuto chv&#xED;li snaz&#x161;&#xED; a&#xA0;tak budeme pokra&#x10D;ovat t&#xED;mto zp&#x16F;sobem.</p>\n<p>Jak vybrat spr&#xE1;vn&#xE9; obr&#xE1;zky na ta spr&#xE1;vn&#xE1; m&#xED;sta? Jm&#xE9;na obr&#xE1;zk&#x16F; (kl&#xED;&#x10D;e ve slovn&#xED;ku)\nobsahuj&#xED; informaci, odkud kam dan&#xFD; obr&#xE1;zek vede. Sta&#x10D;&#xED; se tedy p&#x159;i\nvykreslov&#xE1;n&#xED; ka&#x17E;d&#xE9;ho kousku hada pod&#xED;vat na um&#xED;st&#x11B;n&#xED; jednoho p&#x159;ed n&#xED;m\na&#xA0;jednoho za n&#xED;m a&#xA0;podle toho vybrat ze slovn&#xED;ku ten spr&#xE1;vn&#xFD; obr&#xE1;zek.\nU&#xA0;ka&#x17E;d&#xE9;ho kousku hada a&#xA0;kousku p&#x159;ed i&#xA0;za n&#xED;m t&#x11B; budou zaj&#xED;mat jejich\nsou&#x159;adnice, proto&#x17E;e podle nich lze velmi snadno poznat, zda je zkouman&#xFD; kousek\nnalevo, napravo, naho&#x159;e, nebo dole.</p>\n<p>Zp&#x16F;sob&#x16F;, jak toho doc&#xED;lit, je cel&#xE1; &#x159;ada a&#xA0;i&#xA0;kdy&#x17E; se to m&#x16F;&#x17E;e zd&#xE1;t jako slo&#x17E;it&#x11B;j&#x161;&#xED;\n&#xFA;kol, v&#x161;e pot&#x159;ebn&#xE9; k&#xA0;jeho vy&#x159;e&#x161;en&#xED; zn&#xE1;&#x161;.</p>\n<p><span class=\"figure\"><a href=\"/2019/pyladies-ostrava-jaro/projects/snake/static/screenshot-final.png\"><img src=\"/2019/pyladies-ostrava-jaro/projects/snake/static/screenshot-final.png\" alt=\"Fin&#xE1;ln&#xED; had\"></a></span></p>\n<p>Odm&#x11B;nou za vy&#x159;e&#x161;en&#xED; ti bude kompletn&#xED; grafick&#xE1; hra Had. Gratuluji!</p>\n<h2>Optimalizace, &#xFA;klid</h2>\n<p>Ne&#x17E; se po dokon&#x10D;en&#xED; z&#xE1;kladn&#xED; hry vrhne&#x161; na jej&#xED; roz&#x161;i&#x159;ov&#xE1;n&#xED;, m&#x11B;l by se cel&#xFD; k&#xF3;d\nuklidit a&#xA0;zp&#x159;ehlednit, aby se v&#xA0;n&#x11B;m dal&#x161;&#xED; &#xFA;pravy d&#x11B;laly sn&#xE1;ze a&#xA0;s&#xA0;men&#x161;&#xED;m\nrizikem, &#x17E;e se n&#x11B;co pokaz&#xED;.</p>\n<p>Body k&#xA0;zamy&#x161;len&#xED;:</p>\n<ul>\n<li>Pokud se ti tam opakuje n&#x11B;jak&#xFD; kousek k&#xF3;du v&#xED;cekr&#xE1;t, mo&#x17E;n&#xE1; by se dal\nvlo&#x17E;it do funkce nebo cyklu.</li>\n<li>Maj&#xED; v&#x161;echny prom&#x11B;nn&#xE9; smyslupln&#xE1; jm&#xE9;na?</li>\n<li>P&#x159;i vykreslov&#xE1;n&#xED; mo&#x17E;n&#xE1; tvo&#x159;&#xED;&#x161; pro ka&#x17E;d&#xFD; kousek hada nov&#xFD; <code>Sprite</code> a&#xA0;ten\nje po vykreslen&#xED; zapomenut. Optim&#xE1;ln&#x11B;j&#x161;&#xED; by mo&#x17E;n&#xE1; bylo pou&#x17E;&#xED;t seznam\na&#xA0;v n&#x11B;m v&#x161;echny instance t&#x159;&#xED;dy <code>Sprite</code> uchov&#xE1;vat a&#xA0;pou&#x17E;&#xED;vat znovu a&#xA0;znovu.\n<code>Sprite</code> p&#x159;eci m&#x16F;&#x17E;eme posunout na libovoln&#xE9; m&#xED;sto i&#xA0;zm&#x11B;nit obr&#xE1;zek, kter&#xFD;\nobsahuje.</li>\n<li>Pou&#x17E;&#xED;v&#xE1;&#x161; glob&#xE1;ln&#xED; prom&#x11B;nn&#xE9;? Nebylo by lep&#x161;&#xED; m&#xED;t jednu t&#x159;&#xED;du pro stav hry\na&#xA0;v n&#xED; v&#x161;echny podstatn&#xE9; informace a&#xA0;metody?</li>\n<li>Funguje ovl&#xE1;d&#xE1;n&#xED; dle tv&#xFD;ch p&#x159;edstav nebo by &#x161;lo n&#x11B;jak zlep&#x161;it?</li>\n</ul>\n\n\n        "
    }
  }
}