ESP32-TTGO: Jednoduché hry – „Kdo si hraje, nezlobí…“
Dost bylo serióznosti a dost bylo jen neustálého „biflování“ nových informací a dovedností kolem modulu ESP32. Pojďme si chvíli hrát! A pochopitelně s čím jiným než s ESP32. A když už si hrát, tak opravdu hrát – třeba se skutečnými hrami.
To ovšem znamená jediné – budeme potřebovat displej a nějaký ovládací prvek. Abychom nemuseli opravdu nic zapojovat, sáhneme po modulu, který už něco z toho má na palubě. Ano, vracíme se k vývojovému kitu ESP32-TTGO (T-Display), který jsme si už představili v článku ESP32-TTGO: Postavme si hodinky a který nabízí dvě tlačítka a barevný TFT displej o rozlišení 135×240 px.
Myslíte, že nám tato výbava nebude stačit?
Tak pojďme na to!
Následující řádky představují tři různé hry spuštěné na modulu ESP32-TTGO T-Dislay – cílem ale není hraní, ale naopak pochopení, úpravy a práce s cizím kódem.
Dále bychom si měli zkusit na modulu ESP32-TTGO T-Display spustit následující hry:
ESP32-TTGO (T-Display)
Nejen pro ty, kteří s kitem ESP32-TTGO ještě neměli možnost pracovat, ale i pro ty, kteří ho po letech vytáhli ze šuplíku – stejně jako my – ani z něj pořádně neutřeli prach a teď se marně snaží rozpomenout, „co je vlastně co“, si tenhle populární bastlířský kousek krátce připomeneme.
TTGO (konkrétně varianta T-Display) je vývojový kit postavený přímo kolem čipu ESP32, tedy dvoujádrového mikrokontroléru s integrovanou Wi-Fi a Bluetooth. Jeho hlavní kouzlo spočívá v tom, že hned po vybalení nabízí barevný TFT displej a dvě tlačítka, takže odpadá bastlení první obrazovky na nepájivém poli. Připojíme USB-C kabel, nahrajeme ukázkový program – a hned „to něco kreslí“. Co víc si můžeme přát.
Velkým benefitem kitu je také podpora bateriového provozu. Kromě napájení z USB umožňuje připojení Li-Pol baterie přes JST konektor a má na palubě nabíjecí a napájecí management. Díky tomu se TTGO velmi dobře hodí pro přenosné projekty, jednoduché hry nebo různé kapesní indikátory. Jen je dobré mít na paměti, že hlavně podsvit TFT displeje dokáže baterii vyždímat překvapivě rychle, takže se vyplatí jeho jas řídit – ideálně pomocí PWM.
Základní přehled
- Procesor: ESP32 (Xtensa dual-core, až 240 MHz)
- Bezdrátově: Wi-Fi 2,4 GHz + Bluetooth (Classic i BLE)
- Napájení: přes USB-C nebo externí Li-Pol článek (JST konektor)
- Programování: Arduino IDE, ESP-IDF, MicroPython
- USB-UART převodník: CP2104 (u některých revizí CH9102)
- Velikost: malá destička, která se bez problémů vejde do kapsy
Board existuje v několika revizích (nejčastěji s 4 MB nebo 16 MB flash paměti), ale zapojení displeje i tlačítek je u varianty T-Display vesměs stejné.
GPIO a dostupná rozhraní
Ačkoliv je značná část pinů na TTGO T-Display obsazena vestavěným TFT displejem, zůstává k dispozici dostatek rozhraní pro běžné bastlířské projekty (viz obr. č. 1). ESP32 je v tomto směru velmi flexibilní a většinu periferií lze přiřadit na různé piny podle potřeby.
Obrázek č. 1 – Rozložení a funkce GPIO pinů kitu ESP32 TTGO T-Display.
Sběrnice I²C
Nejpohodlnější způsob, jak připojit senzory, RTC moduly nebo GPIO expandéry.
| sběrnice I²C | |
|---|---|
| SDA | GPIO 21 |
| SCL | GPIO 22 |
Díky I²C se dá jednoduše obejít omezený počet volných GPIO na desce.
Sběrnice SPI (externí zařízení)
Vedle interního SPI pro TFT displej lze připojit i další SPI zařízení, například SD kartu nebo jiný displej.
| sběrnice SPI | |
|---|---|
| SCLK | typicky GPIO 18 (pozor, sdíleno s TFT) |
| MOSI | GPIO 19 (sdíleno s TFT) |
| MISO | libovolný volný GPIO (např. GPIO 12 nebo 15) |
| CS | libovolný volný GPIO |
- Displej využívá pouze MOSI (GPIO 19) a SCLK (GPIO 18),
- MISO zůstává volné pro další periferie,
- zařízení se rozlišují pomocí samostatných CS pinů.
UART, ADC, další periférie
ESP32 nabízí i další rozhraní – UART pro sériovou komunikaci, analogové vstupy (např. GPIO 34) nebo kapacitní dotykové piny. Jejich využití už ale závisí na konkrétní aplikaci a dostupných pinech, takže se k nim dostaneme až podle potřeby.
- Poznámka:
- U některých revizí TTGO T-Display se GPIO 14 používá pro řízení části napájení související s měřením baterie. Nejde však o vlastnost samotného ESP32, ale o konstrukční detail konkrétní desky.
Vestavěná LED
Na desce není žádná vestavěná uživatelská LED, na kterou by se dalo „klasicky“ blikat. Pokud chceme optickou signalizaci mimo TFT displej, je potřeba si LED přidat externě.
Tlačítka
Na spodní hraně desky najdeme dvě tlačítka:
| Tlačítko | Připojený pin |
| Button 1 (levé) | GPIO 0 |
| Button 2 (pravé) | GPIO 35 |
GPIO 35 je pouze vstupní pin, což je pro tlačítko v pořádku. GPIO 0 je naopak bootovací pin – jeho stisk při resetu uvede ESP32 do režimu nahrávání firmware. To je velmi užitečné, ale zároveň to vysvětluje, proč se občas aplikace „nechce rozběhnout“, když máme prst zrovna tam, kde mít nemáme.
Absence třetího tlačítka je jedním z důvodů, proč se u některých projektů setkáme s tím, že se určité akce řeší kombinací obou tlačítek softwarově – jak si brzo ukážeme.
TFT displej
Dominantou kitu je 1,14″ barevný TFT displej s rozlišením 135×240 px a řadičem ST7789. Jde o IPS panel s dobrou čitelností a rychlou odezvou, ideální pro menu, jednoduchou grafiku i hry.
Displej je připojen přes SPI sběrnici, ale používá nestandardní zapojení pinů a nevyužívá linku MISO – komunikace probíhá jednostranně (ESP32 → displej). Zapojení je pevně dané:
| signál TFT displeje | GPIO pin – ESP32 | Poznámka |
|---|---|---|
| MOSI | GPIO 19 | Přenos dat z ESP32 na displej |
| SCLK | GPIO 18 | Hodinový signál – synchronizuje přenos dat |
| CS | GPIO 5 | Vybírá a zapíná displej |
| DC | GPIO 16 | Data/Příkaz – Rozlišuje mezi příkazy a daty |
| RESET | GPIO 23 | Hardwarový reset displeje |
| Podsvit | GPIO 4 | Slouží k nastavení jasu displeje (lze pomocí PWM stmívat) |
Tyto informace jsou důležité hlavně při použití knihovny TFT_eSPI, kde je nutné mít správně nastavenou konfiguraci. Odměnou je pak rychlé vykreslování a minimum dalšího nastavování.
Tolik tedy rychlé připomenutí hardwaru, se kterým budeme pracovat. Teď už víme, co máme v ruce, kde najdeme tlačítka, na čem se bude kreslit a jaké jsou limity i možnosti tohoto malého, ale šikovného bastlířského společníka. A to bohatě stačí. Teorie bylo akorát tolik, aby nás nepřibrzdila – a teď je nejvyšší čas ji přestat řešit a začít ji opravdu používat.
Je ale fér hned na začátku dodat jednu uklidňující poznámku: Žádnou z následujících her nebudeme psát úplně od nuly. Na internetu už dnes existuje spousta hotových projektů, ukázek a portů her pro ESP32, často právě pro TTGO nebo podobné kity. Byla by škoda je nevyužít – koneckonců bastlení není jen o psaní nového kódu, ale i o tom umět se zorientovat v tom stávajícím.
Jak ale brzy zjistíme, nebude to vždy fungovat stylem „stáhnu, nahraju, hraju“. Část těchto projektů vznikla před lety a byla původně napsána pro starší verze jádra Arduino ESP32 (řady 1.x nebo 2.x). Dnes už ale pracujeme s aktuálním jádrem verze 3, které přineslo poměrně výrazné změny v knihovnách, API i chování některých funkcí. Výsledek? Kód, který kdysi fungoval na první pokus, se dnes může zdráhat přeložit – nebo se tvářit, že všechno proběhlo v pořádku… a přitom se na displeji nic nehne.
A právě v tu chvíli se z pouhého „hraní“ stává zase trochu bastlení. Ale nebojte – přesně v tom je ta zábava.
Ale teď začneme zlehka. Odložíme tedy bloková schémata i datasheety a vrhneme se na to, kvůli čemu jsme TTGO vůbec vytáhli ze šuplíku, tedy pro hry. Uvidíme, že i se dvěma tlačítky, malým TFT displejem a trochou kódu se dá na ESP32 vyblbnout víc, než by se na první pohled zdálo.
A začneme rovnou klasikou, kterou není třeba dlouze představovat…
TETRIS
Tuhle herní pecku asi není třeba dlouze představovat. Tetris patří mezi nejčastěji zmiňované hry v nejrůznějších žebříčcích:
- je označován za nejúspěšnější puzzle hru historie,
- patří mezi nejprodávanější hry vůbec,
- dostal se do muzeí moderního umění (např. MoMA),
- má několik zápisů v Guinnessově knize rekordů (nejprodávanější puzzle videohra, jedna z nejvíce portovaných her historie, obrovské množství variant),
- bývá uváděn mezi nejvlivnějšími hrami všech dob.
Velmi slavná je především verze Tetris pro Nintendo Game Boy, která významně pomohla udělat z Game Boye celosvětový hit. Tak proč by nás teď nemohl potěšit i na našem ESP32 TTGO T-Display?
Použitá implementace
Pro naše „hraní“ využijeme hotovou implementaci Tetrisu pro modul ESP32 TTGO od vývojáře VolosR (GitHub profil VolosR).
Jeho skutečné jméno není veřejně známé, ale kvalita jeho práce mluví sama za sebe. VolosR se zaměřuje především na embedded a IoT projekty postavené kolem platformy ESP32, chytrých displejů a moderních GUI pro mikrokontroléry. Ve svých open-source repozitářích propojuje hardware hacking, vizualizaci dat i domácí automatizaci – od meteostanic a systémových dashboardů až po BLE senzory a specializované zobrazovače dat. Zkrátka autor, který dobře ví, co dělá – a tomu odpovídá i kvalita výsledků.
Lze očekávat, že jeho projekty budou nejen funkční, ale také inspirativní.
Stažení projektu a příprava prostředí
- Soubory projektu TTGOTetris stáhneme z adresy: https://
github.com/ VolosR/ TTGOTetris - Soubor ZIP s program si lze rovnou stáhnout i zde z našich stránek – TTgOTetris.zip
Projekt se skládá pouze ze dvou souborů:
TTGOTetris.ino– hlavní programový kódtet.h– data pro úvodní logo (JPG obrázek převedený do dat pomocí nástroje ImageConverter 565 )
Oba soubory uložíme do složky TTGOTetris a otevřeme soubor TTGOTetris.ino v prostředí Arduino IDE. Soubor tet.h, který je ve stejné složce, se otevře automaticky.
Na začátku kódu si můžeme všimnout, že aplikace využívá pouze knihovny SPI a TFT_eSPI, které zajišťují obsluhu TFT displeje modulu ESP32-TTGO. Pokud už máme knihovnu TFT_eSPI nainstalovanou a správně nakonfigurovanou pro Arduino ESP32 Core verze 3, je vše připraveno k překladu.
Pokud s modulem ESP32 TTGO nebo knihovnou TFT_eSPI teprve začínáme, doporučuji nahlédnout do článku: ESP32-TTGO: Postavme si hodinky. Ve druhé části je zde podrobně popsáno nastavení knihovny TFT_eSPI (od verze 2.5.43) pro Arduino ESP32 Core v3, aby korektně fungovalo nejen
přeložení programu, ale hlavně správné vykreslování grafiky na displeji.
Volba desky v Arduino IDE
Pokud jsme dosud používali „běžnou“ vývojovou desku s ESP32 (například DOIT ESP32 DEVKIT), máme ji pravděpodobně stále nastavenou v Arduino IDE. Nyní ale pracujeme s ESP32-TTGO T-Display.
Tato deska sice není v seznamu desek uvedena přímo, ale to vůbec nevadí. V nabídce zvolíme: „ESP32 Dev Module“ (viz obr. 2) – tato volba je pro ESP32-TTGO T-Display plně použitelná.
Obrázek č. 2 – Nastavení správného typy desky ESP32 TTGO T-Display v prostředí Arduino IDE.
Následný překlad a upload programu do desky by měl proběhnout bez problémů. Pokud vše dopadne dobře, objeví se na TFT displeji úvodní logo a po krátké chvíli se spustí klasický Tetris (viz obr. 3).
Obrázek č. 3 – Náhled na úvodní a herní obrazovku hry Tetris.
Ovládání hry
Pro úplnost doplňme ovládání:
- pohyb kostiček vlevo a vpravo se provádí vestavěnými tlačítky pod displejem,
- rotace je řešena buď:
- externím tlačítkem připojeným na GPIO 37 (při stisku se pin uzemní), nebo
- současným stiskem obou vestavěných tlačítek.
Druhá varianta není úplně ergonomická, ale po chvíli se na ni dá zvyknout – a hlavně je to lepší než vytahovat nepájivé pole a připojovat další tlačítko.
Úspora paměti – odstranění úvodního loga
Pokud bychom chtěli trochu ušetřit paměť, můžeme odstranit úvodní logo. Logo se vykresluje v proceduře setup() touto řádkou:
tft.pushImage(0, 0, 135, 240, tet);
Jenže pozor – samotné zakomentování této řádky moc paměti neušetří. Je potřeba odstranit i načtení externích dat:
#include "tet.h"
a také deklaraci externího objektu:
extern uint8_t tetris_img[];
Ideálně rovnou odstraníme i třísekundovou pauzu pro zobrazení loga:
tft.pushImage(0, 0, 135, 240, tet);
delay(3000);
Poté lze soubor tet.h ze složky projektu zcela smazat a celý program se skládá jen ze souboru TTGOTetris.ino.
Nevýhoda? Přijdeme o poměrně hezké úvodní logo.
Výhoda? Získáme pár kilobajtů víc volné Flash paměti.
Jak je hra řešena
Abychom si z článku neodnesli jen hotovou hru, pojďme se podívat, jak je Tetris vlastně implementován.
Kostičky (tetromina)
Základem je pole blocks, které obsahuje definice všech sedmi základních tetrisových dílků:
Block blocks[7] = {
{{{{-1,0},{0,0},{1,0},{2,0}},
{{0,-1},{0,0},{0,1},{0,2}},
{{0,0},{0,0},{0,0},{0,0}},
{{0,0},{0,0},{0,0},{0,0}}},2,1},
...
};
Tato struktura je v podstatě databáze všech tetromin. Každý dílek obsahuje:
- souřadnice čtyř čtverců určující padající „kostičkový objekt“,
- všechny své možné rotace kostičkového objektu,
- počet platných rotací objektu,
- barvu (index do barevné tabulky), kterou bude objekt vykreslen.
Základní prvek je jednoduchý bod {x, y}. Například {-1, 0} znamená posun o jeden blok doleva při stejné výšce.
Tyto body skládají konkrétní tvar. Například vodorovná „čára“ je definována takto:
{-1,0}, {0,0}, {1,0}, {2,0}
Protože každý dílek může mít více orientací, jsou rotace předdefinované. Například objekt vodorovné čáry může být vodorovný a svislý. Tedy jeho druhá varianta je v tomto případě popsatelná množinou bodů:
{{0,-1},{0,0},{0,1},{0,2}} // jeden dolů, jeden na místě a dva nad sebou.
Jiná objekty jsou však složitější, takže obecně mají až čtyři varianty otočení. Proto i vdorovná čára ná deklarované čtyři variantu, ale dvě poslední jsou nulové:
{
{{-1,0},{0,0},{1,0},{2,0}}, // rotace 1 (vodorovná varianta)
{{0,-1},{0,0},{0,1},{0,2}}, // rotace 2 (svislá varianta)
{{0,0},{0,0},{0,0},{0,0}}, // rotace 3 (nulová – tj. tato varianta pro těleso neexistuje)
{{0,0},{0,0},{0,0},{0,0}} // rotace 4 (nulová – tj. tato varianta pro těleso neexistuje)
}
Položky rotací jsou ale ještě zabaleny pole, které k těmto variantám rotací připojuje další dvě čísla – např. u rovné čáry …}, 2, 1} Tyto čísla určují, že celkový počet rotací je u tohoto kostičkového objektu 2 a že se bude daný objekt vykreslovat barvou 1 (dle tabulky barev).
Tohle řešení je ve své podstatě extrémně rychlé, pro ESP32 jednoduché a bez zbytečného zatížení dynamické paměti a hlavně bez matematických rotací – rotace nejsou počítané, jsou předdefinované v této „tabulce“.
Herní pole
Herní pole, ve kterém se kostičkové objekty pohybují, je v této implementaci řešeno naprosto jednoduše – jako obyčejná dvourozměrná matice celých čísel:
screen[10][20]
Každá buňka této mřížky obsahuje:
- hodnotu
0– prázdné pole - hodnotu
1až7– typ (a zároveň barvu) kostičky
Například obsah herního pole může vypadat takto:
0 0 0 0 0 0 0 0 0 0
0 0 0 2 2 0 0 0 0 0
0 0 0 2 2 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
Hodnota 2 zde značí čtvercový blok a současně určuje i jeho barvu – barva se vybírá z tabulky barev pod indexem 2.
Pohyb padající kostky
Zajímavé je, že aktuálně padající kostka je také zapsaná přímo v poli screen[][]. Nepoužívá se žádná zvláštní „dočasná struktura“. Princip je
jednoduchý, ale velmi elegantní:
- kostka se ze staré pozice smaže,
- vypočítá se nová pozice,
- kostka se znovu zapíše do pole.
Při tomto kroku se současně kontroluje:
- kolize se stěnami herního pole,
- kolize se dnem,
- kolize s již umístěnými bloky.
Pokud je nová pozice platná, zapíše se do pole hodnota odpovídající barvě aktuální rotace kostičkového objektu.
Jakmile už kostku nelze posunout dolů (například kvůli kolizi), zůstane v poli „zafixovaná“ a vytvoří se nový dílek, který začne padat shora.
Mazání zaplněných řádků
Po každém ustálení kostky program projede jednotlivé řádky herního pole a hledá, zda v nich existuje hodnota 0. Pokud řádek neobsahuje žádnou nulu, je plný – řádek se smaže, všechny řádky nad ním se posunou o jeden níže.
Technicky se jedná o prosté kopírování řádků shora dolů, čímž se elegantně vyřeší efekt „sesypání“ vyšších bloků.
V programu se však nachází drobná chyba – řádek s indexem 0 se po sesypání nevynuluje, takže nahoře mohou zůstat grafické artefakty.
Správné řešení by mělo obsahovat ještě například tento kód:
for (int i = 0; i < Width; i++) {
screen[i][0] = 0;
}
Jinak se může stát, že v horní části pole zůstanou zbytky starých dat.
Ale co – darovanému Tetrisu na řádky nekoukej! 😊
Proč to celé funguje tak dobře?
Ve skutečnosti je celá hra až geniálně jednoduchá. Ne proto, že by autor něco „odbyl“, ale právě naopak – proto, že je celý návrh postavený na co nejmenším množství jasných pravidel a datových struktur. Vše důležité se odehrává v jedné mřížce, jeden mechanismus řeší zároveň vykreslení, kolize i logiku hry a žádná část kódu nedělá nic navíc. Díky tomu je program rychlý, přehledný, snadno pochopitelný a ideálně se hodí i pro malé mikrokontroléry bez přebytku paměti a výkonu.
- 2D celočíselná „mřížka“
- aktivní blok je přímo součástí mřížky
- rotace = tabulka připravených tvarů
- kolize = kontrola hodnot v poli
- mazání řádků = kopírování řádků
- minimální nároky na RAM
- žádné objekty
- žádná zbytečná dynamická paměť
- a hlavně žádná „divoká fyzika“
Ve skutečnosti je totiž již samotná předloha hry až geniálně jednoduchá. A právě díky této jednoduchosti je Tetris dodnes považován za jednu z nejvýznamnějších her historie a drží řadu rekordů i ocenění.
ALIEN ATTACK
Další herní „peckou“ zlatých osmdesátek už téměř povinně musí být nějaká pořádná střílečka. A proč znovu nevycházet z hotového projektu od stejného autora? Takže sáhneme po hře Alien Attack opět od vývojáře VolosR.
Jde o klasickou arkádovou střílečku ve stylu starých automatů. Hráč ovládá raketu pohybující se ve spodní části obrazovky a snaží se sestřelovat přilétající ufonské nepřátele. Ovládání je jednoduché a přesně odpovídá duchu osmibitových her:
- tlačítka pod displejem slouží pro pohyb vlevo a vpravo,
- externí tlačítko (nebo současný stisk obou tlačítek) zajišťuje střelbu.
A právě v tom je kouzlo podobných projektů – žádná složitá pravidla, jen rychlá reakce a čistá radost z hraní.
Stažení projektu a příprava prostředí
- Projekt stáhneme z GitHubu autora: TTGOAlienAttack
- Případně lze využít i připravený ZIP archiv z našich stránek – TTGOAlienAttack.zip (kód je již upravený pro Arduino ESP32 Core v3)
Projekt už není tvořen jediným nebo dvěma soubory, ale skládá se hned z několika částí:
TTGOAlienAttack.ino– hlavní programani.h– data úvodní animaceback.h– obrázek pozadíenemy.h– grafika nepřátelgameover.h– obrazovka GAME OVERplayer.h– obrázek hráčovy rakety
Na první pohled je vidět zajímavá vlastnost podobných herních projektů. Samotná logika hry bývá poměrně jednoduchá, zatímco velkou část dat tvoří grafika uložená přímo ve zdrojových souborech. Kdybychom například nahradili obrázky vesmírných lodí tanky nebo auty, velmi rychle bychom získali úplně jinou hru.
Všechny soubory uložíme do složky TTGOAlienAttack a otevřeme soubor TTGOAlienAttack.ino v prostředí Arduino IDE.
Budeme-li předpokládat, že již máme:
- nainstalovanou podporu ESP32 prostřednictvím Arduino ESP32 Core verze 3.x,
- správně nastavenou knihovnu
TFT_eSPIpro modul ESP32-TTGO T-Display,
už nám nezbývá něž jen projekt přeložit. A hrát!
První komplikace: chyba při překladu
A právě zde narazíme na problém. Kompilace skončí chybou (viz obrázek č. 4) související s funkcí: ledcSetup()
Obrázek č. 4 – Selhání překladu původního staženého programu.
Důvod je poměrně jednoduchý. Projekt vznikl pro starší verze jádra Arduino ESP32 (2.x.x), zatímco v novějších verzích 3.x.x bylo původní PWM API zcela přepracováno a změněno.
Funkce ledcSetup() a ledcAttachPin() byly odstraněny a nahrazeny novějším způsobem konfigurace PWM.
Původní kód, který vypadá takto:
ledcSetup(pwmLedChannelTFT, pwmFreq, pwmResolution);
ledcAttachPin(TFT_BL, pwmLedChannelTFT);
ledcWrite(pwmLedChannelTFT, 100);
je třeba nahradit modernější podobou funkce: ledcAttachChannel(pin, freq, resolution, channel)
Pokud však nepotřebujeme pevně určovat konkrétní PWM kanál, což zde opravdu nepotřebujeme, vystačíme si s jednodušším řešením:
ledcAttach(TFT_BL, pwmFreq, pwmResolution);
ledcWrite(TFT_BL, 100);
Tato úprava je plně dostačující a po její aplikaci se projekt již bez problémů přeloží.
Obrázek č. 5 – Úspěšný překlad upraveného kódu.
Mimochodem – právě podobné situace jsou ideálním příkladem, kdy může pomoci umělá inteligence. Zadání názvu funkce vyvolávající chybu (nebo celé chybové hlášky) často velmi rychle napoví, co se mezi verzemi knihoven změnilo a můžeme rovnou dostat i tip na vyřešení problému.
Zahoďme svou boomerskou hrdost a pojďme této možnosti občas dát šanci!
Jak hra vypadá
Po spuštění se zobrazí úvodní obrazovka s efektní animací v pravém horním rohu. Následně se hráč ocitne uprostřed klasické vesmírné bitvy ve stylu arkádových automatů z osmdesátých let (viz obrázek č. 6).
Obrázek č. 6 – Náhled na úvodní a herní obrazovku hry Alien Attack.
Přestože jde o velmi jednoduchou hru, na malém displeji modulu ESP32-TTGO T-Display působí překvapivě dobře a krásně ukazuje, co všechno lze s ESP32 a několika tlačítky vytvořit.
Výměna obrázků? Proč ne!
Velkou výhodou projektu je oddělení grafických dat od samotného programu. To znamená, že změnou obrázků, jak již bylo naznačeno, lze velmi snadno vytvořit vlastní variantu hry. Místo vesmírných lodí mohou po obrazovce jezdit tanky, auta nebo třeba roboti. Fantazii se zde meze prakticky nekladou.
Můžete to zkusit jako takový nepovinný domácí úkol. 😊
Pro převod obrázků do formátu používaného knihovnou TFT_eSPI lze využít například online nástroj Image
Způsob převodu jpg obrázků na sekvenci dat programového kódu je ostatně zmíněn i přímo v zdrojových souborech projektu – soubory s příponou .h.
Kolik místa hra zabírá?
Po úspěšném překladu programu se v informačním okně projektu dozvíme, že program zabírá přibližně 900 kB FLASH paměti. Na první pohled to může působit překvapivě, protože samotný zdrojový kód není nijak rozsáhlý. Hlavním důvodem jsou pochopitelně grafická data uložená v souborech .h, zejména soubor ani.h, který obsahuje úvodní animaci.
Tato animace sice vypadá efektně, ale z hlediska samotného hraní není nijak zásadní. A právě zde se nabízí zajímavý prostor pro optimalizaci.
Odstranění úvodní animace
Úvodní animace sice vypadá efektně, ale zabírá značné množství FLASH paměti. Pokud nám na ní příliš nezáleží, můžeme ji poměrně snadno odstranit.
Animace běží v následující části programu:
while(digitalRead(0)==1) {
tft.pushImage(34, 0, 102, 84, ani[aniFrame]);
delay(40);
aniFrame++;
if(aniFrame==25)
aniFrame=0;
}
Data animace jsou uložena v souboru anim.h a připojena:
#include "ani.h"
Postup odstranění animace
Nejprve odstraníme nebo zakomentujeme řádek:
#include "ani.h"
Poté nahradíme animační smyčku jednoduchým čekáním na stisk tlačítka:
while(digitalRead(0)==1) {
delay(10);
}
Výsledná část programu bude vypadat takto:
if(fase==0) {
delay(500);
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE,TFT_BLACK);
tft.pushImage(0,0,135,240,back);
// čekání na stisk tlačítka bez animace
while(digitalRead(0)==1) {
delay(10);
}
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE,0x7820);
tft.fillRect(0,0,135,17,0x7820);
tft.drawString("SCORE:",2,1,2);
tft.drawString("TIME:",70,1,2);
tft.drawLine(0,17,135,17,0x6577);
tft.drawLine(0,18,0,240,0x6577);
tft.drawLine(134,18,134,240,0x6577);
fase=1;
}
Kolik paměti tím ušetříme?
Animace představuje největší datový blok celého projektu. Pole animace je deklarováno takto:
const unsigned short PROGMEM ani[][8568]
Každý snímek tedy obsahuje 8568 hodnot typu unsigned short, přičemž jedna hodnota zabírá 2 bajty. Velikost jednoho snímku tedy zabírá: 8568 × 2 = 17136 bajtů.
Animace obsahuje 25 snímků, jak vidíme v kódu:
if(aniFrame==25)
aniFrame=0;
Celková velikost animace je tedy: 17136 × 25 = 428400 bajtů, což odpovídá přibližně 428 kB Flash paměti. To už je u mikrokontroleru poměrně výrazná úspora.
- Poznámka:
- Je však důležité zmínit jednu podstatnou věc: odstraněním animace prakticky neušetříme žádnou RAM. Data uložená pomocí
PROGMEMse nacházejí ve Flash paměti programu, nikoliv v operační paměti mikrokontroleru. Úspora se tedy projeví hlavně ve velikosti výsledného firmware.
DINOSAURUS GAME
Hru Dinosaur Game, známou také jako Chrome Dino, T-Rex Runner nebo zkráceně Dino, není nutné dlouze představovat. Většina uživatelů se s ní setkala v internetovém prohlížeči Chrome ve chvíli, kdy nebylo dostupné internetové připojení. Z původně nenápadné doplňkové funkce se postupně stala jedna z nejznámějších jednoduchých her posledních let.
Princip hry je velmi jednoduchý. Pixelovaný Tyrannosaurus rex se neustále pohybuje vpřed a úkolem hráče je ve správný okamžik přeskakovat kaktusy nebo se vyhýbat pterodaktylům. Právě kombinace jednoduchého ovládání, plynulého tempa a postupně rostoucí obtížnosti způsobuje, že hra dokáže udržet pozornost překvapivě dlouho. Díky své grafické nenáročnosti se navíc velmi dobře hodí pro malé TFT displeje – konkrétně i pro ESP32-TTGO T-Display.
Stažení projektu a příprava prostředí
Zdrojový kód hry je dostupný několika způsoby:
- původní projekt lze stáhnout z GitHubu autora VolosR pod názvem TRexTTGOdisplay.
- alternativně je na těchto stránkách k dispozici ZIP archiv Dino.ZIP, který obsahuje původní, neupravenou verzi.
- pro účely tohoto článku je připraven také soubor Dino2.ZIP, tedy verze upravená podle zde popisovaných kroků.
V následujícím textu budeme do programu zasahovat poměrně výrazně, proto může být finální ZIP verze užitečná pro porovnání nebo kontrolu výsledného stavu.
Po otevření projektu je vidět, že je řešen poměrně přehledně a bez zbytečné složitosti. Celá hra se skládá pouze ze tří souborů:
Dino.ino– hlavní program hrynoInternet.h– datový soubor obsahující bitmapu úvodní obrazovkygameover.h– bitmapa závěrečné obrazovky GAME OVER
Soubor s příponou .ino představuje vlastní řídicí logiku hry. Hlavičkové soubory .h jsou použity výhradně jako úložiště obrazových dat, která se při běhu programu vykreslují na TFT displej. Tento přístup jsme viděli již u předchozích her a je vůbec u projektů pro malé displeje poměrně běžný a umožňuje udržet hlavní kód přehledný.
Všechny tři soubory uložíme do jedné složky s názvem Dino. Pokud pracujeme se ZIP archivem, stačí jej rozbalit – struktura projektu již odpovídá požadavkům vývojového prostředí Arduino IDE.
V Arduino IDE otevřeme soubor Dino.ino a v nastavení zvolíme:
- typ desky: „ESP32 Dev Module“
- odpovídající USB port
Poté spustíme překlad programu.
Chyba překladu nás nezastaví
Při překladu se objeví chyba, která není způsobena chybou v logice programu, ale změnami v API ESP32. Konkrétně se opět jedná o použití staršího způsobu nastavení PWM pro řízení podsvícení TFT displeje. Stejný problém se vyskytl již v předchozím projektu Alien Attack.
Obrázek č. 7 – Chyba při překladu programi Dino v prostředí Arduino IDE.
Původní kód:
ledcSetup(pwmLedChannelTFT, pwmFreq, pwmResolution);
ledcAttachPin(TFT_BL, pwmLedChannelTFT);
ledcWrite(pwmLedChannelTFT, brightnes[b]);
je nutné nahradit aktuálním zápisem:
ledcAttach(TFT_BL, pwmFreq, pwmResolution);
ledcWrite(TFT_BL, brightnes[b]);
Po této úpravě překlad proběhne bez chyb a program je připraven k nahrání do modulu ESP32-TTGO T-Display.
Spuštění hry
Po úspěšném překladu a nahrání programu do modulu ESP32-TTGO T-Display se na displeji objeví bílá obrazovka s typickou hláškou známou z internetového prohlížeče Chrome. Tato úvodní obrazovka je bitmapa uložená v souboru noInternet.h a jde o věrnou kopii originální „offline“ obrazovky.
Na rozdíl od předchozích her je zde drobná změna v ergonomii – modul je nutné držet na šířku, nikoliv na výšku. Samotné spuštění hry je jednoduché: stiskem vestavěného tlačítka Button 1.
Po spuštění se zobrazí vlastní hra. Jak je vidět na obrázku č. 8, jedná se o velmi přesnou implementaci původního Chrome Dina, a to včetně černobílého provedení. Barevný TFT displej tedy tentokrát není využit naplno, nicméně grafická jednoduchost k této hře neodmyslitelně patří.
Obrázek č. 8 – Hra Dino na modulu ESP32-TTGO T-Display.
Ve hře se objevují klasické překážky v podobě kaktusů, které je nutné pomocí jediného ovládacího tlačítka přeskakovat. Autor nezapomněl ani na létající pterodaktyly, kteří přicházejí později se zvyšující se obtížností. V pravém horním rohu displeje se průběžně zobrazuje skóre, které narůstá s časem, po který je dinosaurus naživu.
Game over – skutečný „over“?
Při kolizi dinosaura s některou z překážek hra končí a zobrazí se známá hláška GAME OVER, opět ve formě bitmapy tentokráte uložené ze souboru gameover.h. Zde však narážíme na drobný, ale poměrně zásadní problém.
V tomto okamžiku je totiž „game over“ skutečně definitivní. Hru nelze znovu spustit žádným ovládacím prvkem – jedinou možností je hardwarový reset modulu pomocí resetovacího tlačítka. To je sice funkční řešení, ale z pohledu použitelnosti i elegance nikoli ideální. A právě tady se otevírá prostor pro první zásahy do kódu.
Jakmile se rozhodneme do hry zasáhnout, dává smysl vyřešit více věcí najednou. Jednou z nich je i úvodní obrazovka imitující výpadek internetu. Ačkoliv je to pěkný odkaz na původní hru, v samostatné hardwarové verzi už ztrácí část svého smyslu.
Nabízejí se v zásadě dvě možnosti:
- navrhnout si vlastní úvodní obrazovku (této variantě se budeme věnovat vzápětí),
- nebo úvodní obrazovku zcela odstranit, podobně jako jsme to řešili u úvodní animace v předchozí hře Alien Attack.
V tuto chvíli se zaměříme především na pochopení toho, jak je úvodní obrazovka v programu řešena.
Analýza úvodní obrazovky
Data úvodního obrázku jsou do programu připojena pomocí souboru noInternet.h, který je zahrnut direktivou:
#include "noInternet.h"
Tento řádek lze samozřejmě zakomentovat, ale než to uděláme, je vhodné se podívat, jakým způsobem je obrázek vykreslován a co se v této části programu ještě děje.
Proto se přesuneme do funkce setup(), kde se úvodní obrazovka zobrazuje hned po startu programu. Klíčovým řádkem je následující příkaz:
tft.pushImage(0, 0, 217, 135, noInternet);
Ten vykreslí bitmapu přesně přes celý displej. Hned za tímto příkazem následuje čekací smyčka:
tft.pushImage(0, 0, 217, 135, noInternet);
while(digitalRead(0)!=0){
if(digitalRead(35)==0){
if(press2==0){
press2=1;
b++;
if(b>=6) b=0;
ledcWrite(pwmLedChannelTFT, brightnes[b]);
}
delay(200);
} else press2=0;
}
tft.fillScreen(TFT_WHITE);
Na první pohled je zřejmé, že program čeká na stisk tlačítka připojeného na GPIO0, kterým se hra spouští. Při podrobnějším pohledu si ale všimneme ještě jedné zajímavé věci – ve smyčce se testuje také druhé tlačítko, připojené na GPIO35.
Úvodní obrazovka tedy neplní pouze estetickou funkci. Pomocí druhého tlačítka lze v této fázi měnit jas podsvícení TFT displeje. Každý stisk zvýší hodnotu proměnné b, která slouží jako index do pole brightnes[]. Následný příkaz ledcWrite() nastaví odpovídající PWM hodnotu pro podsvícení. Jas se tak cyklicky přepíná mezi několika předdefinovanými úrovněmi. Z pohledu uživatele jde o jednoduché, ale praktické řešení, které má smysl právě při úvodní obrazovce – ještě před samotným startem hry.
A právě tato skutečnost zásadně ovlivní naše další rozhodování o tom, co s úvodní obrazovkou uděláme dál.
Měníme plán: místo odstranění výměna úvodního loga
Pokud bychom se rozhodli úvodní část kódu jednoduše zakomentovat a tím se zbavit úvodního obrázku, odstranili bychom tím zároveň i možnost nastavení jasu displeje, která je s touto obrazovkou přímo provázána. To by byla poměrně škoda – jednak z praktického hlediska, jednak proto, že bychom se tím nijak neposunuli v pochopení fungování programu. Namísto prostého odstranění úvodní obrazovky tedy měníme strategii. Úvodní logo zachováme, ale nahradíme jej vlastním obrázkem. Díky tomu zůstane zachována možnost regulace jasu a současně si projdeme celý proces práce s bitmapami pro TFT displej.
Vytvoření vlastního úvodního obrázku
Prvním krokem je vytvoření samotného obrázku. K tomu lze použít v podstatě libovolný bitmapový editor – od jednoduchých nástrojů typu klasického Malování až po pokročilejší grafické programy. Obrázek může být samozřejmě navržen i pomocí některého z nástrojů umělé inteligence, pokud to někomu usnadní práci. Hotový návrh je nutné připravit ve správném rozlišení, tedy 217×135 pixelů, což odpovídá rozměru původní bitmapy použité v projektu. Dále je vhodné převést obrázek do dvoubarevného provedení, aby odpovídal charakteru celé hry a původní grafice.
Pro úpravu rozměrů i barev se dobře hodí například bezplatný program XnView, jak je patrné z obrázku č. 9.
Obrázek č. 9 – Úprava obrázku pro nové logo v programu XnView.
Výsledný obrázek uložíme do některého z běžných bezztrátových formátů. Vzhledem k tomu, že se jedná o jednoduchou grafiku, je nejvhodnější volbou formát PNG.
Převod obrázku do formátu pro ESP32 (RGB565)
Aby bylo možné obrázek zobrazit na TFT displeji, je nutné jej převést do datové podoby, kterou lze přímo zahrnout do programu. Tento krok naštěstí není nutné řešit ručně. Velmi užitečným nástrojem je online konvertor Image to RGB565 Converter.
Postup je následující:
- Nahrajeme připravený PNG obrázek.
- Zvolíme název proměnné, do které se budou data ukládat – v našem případě ponecháme původní název
noInternet, aby nebylo nutné měnit kód programu. - Nastavíme rozměry výstupního obrázku (217×135 pixelů).
- Zvolíme barvu pozadí (bílá).
- Pořadí bitů ponecháme ve výchozím stavu (LSB).
Obrázek č. 10 – Konverze obrázku pomocí online nástroje Image to RGB565 Converter.
Po kliknutí na tlačítko  Convert to .h File  se vygeneruje a stáhne soubor s příponou .h, který obsahuje bitmapová data ve formátu RGB565.
Nahrazení dat v souboru noInternet.h
Vygenerovaný soubor otevřeme v textovém editoru. Na první pohled je patrné, že jeho struktura je velmi podobná původnímu souboru noInternet.h z projektu, i když se v detailech liší. Pro zachování maximální kompatibility nebudeme soubor nahrazovat celý, ale pouze jeho datovou část. Konkrétně se jedná o pole s bitmapovými daty:
const unsigned short noInternet[29295] PROGMEM= {
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
...
Tuto část z nově vygenerovaného souboru zkopírujeme a vložíme na stejné místo do původního noInternet.h, čímž původní obrázek nahradíme vlastním.
Po této úpravě projekt znovu přeložíme a nahrajeme do modulu. Pokud vše proběhlo správně, objeví se po restartu nová úvodní obrazovka, viz obrázek č. 11.
Obrázek č. 11 – Náš nový úvodní obrázek vložená do prigramu Dino.
Úvodní logo je tedy úspěšně vyměněno.
Malé zklamání na závěr
Při testování si však lze všimnout, že změna jasu displeje pomocí druhého tlačítka není – alespoň na našem modulu – příliš patrná. Podle kódu by se měl jas přepínat v šesti krocích, nicméně vizuální rozdíly jsou minimální. I když se tedy regulace jasu v praxi neprojevuje tak výrazně, jak bychom očekávali, alespoň jsme nepropadli možnosti vyřazení úvodního obrázku. Tím pádem jsme se pokusili vyměnit úvodní obrázek, což nám umožnilo pochopit práci s bitmapami, jejich konverzi a propojení grafických dat s kódem programu. Problematice nefungující regulace podsvícení se lze případně vrátit v některém z dalších našich experimentů.
Odstraňujeme bitmapový GAME OVER
U závěrečné obrazovky GAME OVER už není důvod ke kompromisům. Na rozdíl od úvodního loga zde bitmapa nepřináší žádnou funkční výhodu – jde pouze o statický nápis. Vzhledem k tomu, že knihovna TFT_eSPI umožňuje bez problémů vykreslovat text v různých velikostech a fontových stylů, je zbytečné zobrazovat hlášku o konci hry pomocí paměťově náročného obrázku.
Prvním krokem je tedy odstranění datového souboru s bitmapou. V úvodu programu zakomentujeme řádek:
#include "gameover.h"
Stejně tak odstraníme (nebo zakomentujeme) příkaz, který bitmapu vykresluje na displej. Najdeme jej téměř na konci funkce loop():
tft.drawXBitmap(10, 30, gameover, 223, 100, TFT_BLACK, TFT_WHITE); // bitmapovy GAMEOVER
Namísto bitmapy nyní přidáme vlastní kód, který vypíše text přímo na displej:
// vyčistí obrazovku
tft.fillScreen(TFT_WHITE);
tft.setTextSize(1);
tft.setTextFont(1);
// velký GAME OVER text
tft.setTextColor(TFT_BLACK, TFT_WHITE);
tft.setTextDatum(MC_DATUM);
// velké písmo (zabudované fonty TFT_eSPI)
tft.drawString("GAME OVER", 120, 50, 4);
tft.setTextSize(2);
tft.drawString("Score: " + String(score), 120, 85);
První část kódu vymaže displej a nastaví základní parametry pro práci s textem. Pomocí funkce tft.setTextDatum(MC_DATUM) zajistíme centrování textu vůči zadaným souřadnicím. Samotný nápis GAME OVER je vykreslen větším písmem pomocí vestavěného fontu knihovny TFT_eSPI. Pod ním se zobrazuje dosažené skóre, uložené v proměnné score.
Výsledek je patrný na obrázku č. 12.
Obrázek č. 11 – Nová závěrečná hláška GAMR OVER.
Z hlediska přehlednosti i paměťových nároků jde o výhodnější řešení než použití bitmapy. Navíc se tím otevřel prostor pro další rozšiřování závěrečné obrazovky – například přidání instrukce k opětovnému spuštění hry.
Spuštění nové hry bez resetu
Dalším zřejmým nedostatkem původního řešení je nutnost resetovat celý modul, pokud chceme hru spustit znovu. To sice funguje, ale patří spíše k nouzovým řešením než k běžné herní logice. Většina výchozích hodnot se v původním programu nastavuje ve funkci setup(). Abychom se vyhnuli rozsáhlým zásahům do kódu, vytvoříme si vlastní startovací funkci, která nastaví potřebné proměnné do výchozího stavu a umožní hru znovu spustit přímo z běžícího programu.
Novou funkci nazveme například resetGame() a přidáme ji před funkci setup():
void resetGame(){ // pridana procedura, aby se dala hra hrát znova po GAME OVER
gameRun = 1;
x = 30;
y = 58;
dir = -1.4;
pressed = 0;
frames = 0;
f = 0;
sped = 1;
cloudSpeed = 0.4;
score = 0;
t = 0;
clouds[0] = random(0,80);
clouds[1] = random(100,180);
eX[0] = random(240,310);
eX[1] = random(380,460);
for(int i=0;i<6;i++){
linesX[i]=random(i*40,(i+1)*40);
linesW[i]=random(1,14);
linesX2[i]=random(i*40,(i+1)*40);
linesW2[i]=random(1,14);
}
for(int n=0;n<2;n++){
bumps[n]=random(n*90,(n+1)*120);
bumpsF[n]=random(0,2);
}
tft.fillScreen(TFT_WHITE);
}
Tato funkce nastaví většinu důležitých proměnných do výchozího stavu pozice dinosaura, rychlosti pohybu, rozmístění překážek i skóre. Ve své podstatě jde o výňatek z původní funkce setup(). Do budoucna by bylo možné řešení ještě zpřehlednit tím, že by setup() tuto funkci pouze zavolala, a tím by se zabránilo duplicitnímu kódu. Pro účely této úpravy však ponecháme tuto optimalizaci otevřenou.
Zbývá ještě zajistit, aby se funkce resetGame() skutečně volala ve správný okamžik. Ve smyčce loop() hra běží pouze tehdy, pokud je proměnná gameRun rovna 1:
if (gameRun == 1) {
...
}
Pokud gameRun nabude jiné hodnoty, hra je ukončena – to je ideální místo pro obsluhu opakovaného startu. Za tuto podmínku tedy přidáme odpovídající větev else:
} else {
// restart jen po GAME OVER
if(digitalRead(0)==0){
delay(300);
while(digitalRead(0)==0);
resetGame();
}
}
Po stisku tlačítka na obrazovce GAME OVER se tak hra znovu inicializuje a okamžitě spustí, aniž by bylo nutné resetovat celý modul.
Máme hotovo?
Pokud jsme všechny předchozí úpravy provedli správně, měl by se program bez problémů přeložit, nahrát do modulu ESP32-TTGO T-Display a hra by měla fungovat podle očekávání. Po spuštění se zobrazí vlastní úvodní logo, po kolizi korektně naskočí textová obrazovka GAME OVER a stiskem tlačítka lze hru okamžitě znovu spustit – bez nutnosti resetovat celý modul. Na první pohled tedy vše funguje přesně tak, jak jsme chtěli. Přesto se při několika odehraných hrách objeví drobná nesrovnalost.
Možná si všimnete zvláštního jevu – s každou novou hrou se naše skóre stále zvyšuje. A to dokonce i v případě, že dinosaurus narazí hned do první překážky po startu. Zjevně zde něco ještě není v pořádku!
Přestože jsme ve funkci resetGame() hodnotu proměnné score vynulovali, samotný způsob výpočtu skóre tím ještě ovlivněn není. Pokud se podíváme do funkce drawS() (vykreslení scény), nalezneme následující řádek:
score=millis()/120;
Zde je jádro problému. Funkce millis() vrací počet milisekund, které uplynuly od posledního resetu mikrokontroléru. V původním řešení to nevadilo, protože každé nové spuštění hry bylo zároveň fyzickým resetem modulu. Jakmile ale umožníme opakované spouštění hry bez resetu zařízení, hodnota millis() neustále roste. Skóre je pak odvozeno nikoliv od začátku aktuální hry, ale od okamžiku, kdy byl modul zapnut. To vede k tomu, že se skóre s každou další hrou automaticky zvyšuje.
Řešení je poměrně jednoduché – uložíme si čas začátku hry a skóre budeme počítat jako relativní přírůstek vůči tomuto okamžiku.
Nejprve si zavedeme pomocnou proměnnou, například scoreOffset, která bude uchovávat hodnotu millis() v okamžiku startu hry:
unsigned long scoreOffset = 0; // kvuli novemu spusteni
Tuto deklaraci přidáme mezi ostatní globální proměnné v úvodu programu. Dále je nutné tuto proměnnou nastavit pokaždé, když se hra spouští znovu. Ideálním místem je funkce resetGame(). Hned na jejím začátku tedy doplníme:
scoreOffset = millis(); // aby se body opet pocitaly od nuly
Nakonec upravíme samotný výpočet skóre ve funkci drawS(), aby pracoval s rozdílem mezi aktuálním časem a uloženým počátkem hry:
score = (millis() - scoreOffset) / 120; // relativni prirustek od scoreOffset
Tím zajistíme, že skóre bude vždy počítáno od začátku aktuální hry – bez ohledu na to, kolikrát už byla hra spuštěna.
Hotovo. Tentokrát opravdu.
Po této poslední úpravě se chování hry definitivně srovná. Skóre se po každém restartu začne počítat od nuly, hra se spouští bez resetu modulu a celý program působí výrazně ucelenějším dojmem. A hlavně… zase jsme se něco naučili. Výsledkem je plně funkční, upravená verze hry Dinosaur Game pro ESP32-TTGO T-Display, která nejen věrně kopíruje původní předlohu, ale zároveň ukazuje, jak lze relativně jednoduchý projekt postupně vylepšovat, analyzovat a kultivovat.
Závěrem: Nejen hrát hry, ale hrát si s hrami
Na první pohled jsme si v tomto článku „jen hráli“. Spustili jsme Tetris, stříleli ufouny v Alien Attack a utíkali s dinosaurem přes kaktusy. Zdánlivě tedy tři samostatné hry, tři staré herní koncepty a jeden malý vývojový kit modulu ESP32 s TFT displejem. Při bližším pohledu je však zřejmé, že o pouhé hraní tady vůbec nešlo. Každá z her nám totiž ukázala jiný aspekt práce s cizím kódem.
- Tetris byl ukázkou čistoty návrhu. Jednoduché datové struktury, minimum pravidel a žádná zbytečná „omáčka“. Hra, která funguje skvěle právě proto, že je postavená na silném a jednoduchém základu. Ideální příklad toho, že dobře promyšlený algoritmus je často cennější než složité technické triky.
- Alien Attack nás naopak rychle vrátil nohama na zem. Starší funkční projekt přestal spolupracovat s novou verzí jádra ESP32, objevily se chyby při překladu a najednou bylo nutné chápat, proč něco nefunguje. Přidala se práce s pamětí, odhalení skutečné ceny grafických dat a první pokus o optimalizaci. Tady se z hraní začalo stávat pravé domácí bastlení v tom pravém slova smyslu.
- A Dinosaur Game? Ten se tak trochu stal naší tvůrčí laboratoří. Analýza cizího kódu, promyšlené zásahy do logiky hry, výměna bitmap za text, řešení restartu hry bez resetu, oprava nenápadné chyby se skóre a časem. Ukázka toho, že skutečně funkční program není jen o tom, že „to běží“, ale že se chová správně i v situacích, které původní autor možná vůbec neřešil.
Společným jmenovatelem všech tří projektů není konkrétní hra, autor kódu ani použitá knihovna. Je jím přístup. Přístup, který říká, že není ostuda vzít hotový projekt, rozebrat ho, pochopit a upravit podle vlastních potřeb. Naopak – právě to je jedna z nejcennějších dovedností bastlíře. Vývojový kit ESP32-TTGO T-Display se tak nakonec nestal herní konzolí – stal se naším hřištěm. A na takovém hřišti už nezáleží na tom, jestli právě běhá dinosaurus, padají kostičky nebo přilétají ufoni. Důležité je, že si pořád hrajeme a přitom se pořád něco nového učíme!
Hrát hry je příjemné. Ale hrát si s hrami je to, co nás posouvá dál!