Většina videoher má celý herní svět uložený jako spoustu čísel, textů, seznamů a jiných datových objektů, které popisují všechno, co ve hře je. Tenhle stav se časem mění, ať už automaticky nebo podle akcí hráče. A docela často – většinou zhruba šedesátkrát za vteřinu – se stav hry převede na obrázek, který se hráčovi ukáže.
Abys mohla zobrazit hada, budeš napřed muset definovat stav hry – zadat to, co se má vykreslovat.
Zkus se zamyslet, co všechno bude ten stav obsahovat: co všechno si počítač musí o hře „pamatovat“, aby mohl aktuální stav zobrazit?
Bude potřebovat například aktuální polohu všech částí hada: kde má začátek? Kroutí se doprava nebo doleva? Jak je dlouhý? Naopak barvu hada ve stavu uložit nepotřebuješ – každý had v téhle hře bude stejný.
Napadne tě, jak polohu hada zapsat pomocí čísel, seznamů a dalších základních datových typů?
Asi nejjednodušší způsob, jak si v počítači „zapamatovat“ herního hada, je pomocí seznamu souřadnic.
Pamatuješ si ze školy na kartézské souřadnice? To je taková ta část matematiky, co možná vypadala že nemá praktické využití. Pro počítačovou grafiku jsou ale souřadnice to co pro češtinu písmenka. Pojďme si je osvěžit.
Každý bod v rovině (třeba na obrazovce!) je možné popsat dvěmi čísly: x-ovou a y-ovou souřadnicí. Ta x-ová říká, jak moc vlevo je bod od nějakého počátku, ta y-ová udává jak moc je nahoře. My za onen „počátek“ zvolíme roh okýnka, ve kterém se bude náš had plazit.
Na rozdíl od školní geometrie se had bude plazit po čtverečkové mřížce. Je to jako na šachovnici – když jde pěšec na D5, D značí, jak moc je to políčko vlevo od okraje a 5 jak moc „nahoře“.
Tady je had, který začíná na souřadnicích (1, 2) a hlavu má na (4, 5):
Možná si všimneš, že matematický zápis souřadnic – (1, 2) – odpovídá způsobu, jak se v Pythonu píšou n-tice. To není náhoda! Dvojice čísel je perfektní způsob, jak uložit souřadnice kousku hada.
Had má ale kousků víc, a jinak dlouzí hadi jich budou mít různý počet. Na to je dobré použít seznam. Seznam souřadnic. A protože souřadnice pro nás budou dvojice čísel, seznam souřadnic bude seznam dvojic čísel.
Had z obrázku výše bude v Pythonu vypadat takto:
snake = [(1, 2), (2, 2), (3, 2), (3, 3), (3, 4), (3, 5), (4, 5)]
Tohle je reprezentace hada – to, co je z hlediska hry potřeba o konkrétním hadovi vědět.
Počítačům (a programátorům?) to takhle stačí. My si to ale zkusme zobrazit barevně, ať hadovi rozumí i hráč naší budoucí hry.
U vykreslování hada musíme vyřešit jeden základní problém: převod logických souřadnic na souřadnice obrazovky.
Displeje počítačů fungují podobně jako naše souřadnicová „šachovnice“: jsou to čtvercové mřížky plné políček. Každému políčku – pixelu – lze nastavit barvu. Hlavní rozdíl proti šachovnici je v tom, že pixelů na obrazovce je mnohem, mnohem víc.
Kdyby byl každý „herní“ čtvereček 10×10 pixelů velký, tak hlava hada z ukázky, která má „herní“ souřadnice (4, 5), se na obrazovku bude vykreslovat na čtverečku, který začíná na (40, 50):
A ocas s „herními“ (logickými) souřadnicemi (1, 2) se vykreslí na čtvereček se souřadnicemi (10, 20).
Na to, abychom hada vykreslili, použijeme okýnko z Pygletu. Tady je základní kostra Pygletí aplikace, které už bys měla rozumět:
import pyglet
window = pyglet.window.Window()
@window.event
def on_draw():
window.clear()
pyglet.app.run()
V editoru si otevři nový soubor, ulož ho jako had.py
a kostru programu
do něj zkopíruj.
Budeme ji dál rozvíjet.
Stáhni si soubor green.png – zelený čtvereček – a dej ho do adresáře, kam píšeš kód.
Pod import pyglet
přidej řádek, který tento obrázek načte.
green_image = pyglet.image.load('green.png')
Potom zkus dovnitř do funkce on_draw
přidat vykreslení obrázku na souřadnice
(40, 50), velikosti 10.
green_image.blit(40, 50, width=10, height=10)
Program spusť (cd
do nového adresáře; python had.py
). Funguje?
(Je docela důležité, aby fungoval – nevidíš-li zelený čtvereček,
nečti dál a program radši oprav.)
Jak vidíš, čtvereček je docela malý. Budeme radši používat čtverečky větší, řekněme 64 pixelů.
To číslo je „střelené od boku“.
V programu ho použijeme několikrát, a možná ho později budeš chtít upravit.
Uložíme si ho proto do konstanty (proměnné, kterou nebudeme měnit).
Konstanty se tradičně pojmenovávají velkými písmeny a píšou se hned za řádek
import
(i když to není technicky nutné).
Přidej tedy za import
řádek:
TILE_SIZE = 64
… a ve volání green.blit
velikost čtverečku zohledni:
green_image.blit(4 * TILE_SIZE, 5 * TILE_SIZE,
width=TILE_SIZE, height=TILE_SIZE)
Povedlo se? Máš čtvereček? Jestli ne, zkus si program celý, řádek po řádce, projít a zkontrolovat. Nebo ho porovnej se vzorovým řešením (což je rychlejší varianta, ale míň se naučíš).
Zkus teď na začátek programu – těsně pod import
a konstantu – přidat
definici hada:
snake = [(1, 2), (2, 2), (3, 2), (3, 3), (3, 4), (3, 5), (4, 5)]
A ve funkci draw
vykresli všechny čtverečky hada.
Vzpomeň si, že seznam dvojic můžeš „projít“ pomocí cyklu for
a „rozbalení“
n-tice:
for x, y in snake:
...
Funguje to? Vidíš v tom – aspoň zhruba – hada poskládaného ze čtverečků?
Jestli ne, nezoufej, zkontroluj si to znovu, poptej se na radu. Ukázkové řešení využij až jako krajní možnost, jak pokračovat dál. Nebo pro kontrolu.
Aby bylo ve hře co dělat, budeme potřebovat pro hada krmení. Stáhni si do adresáře s projektem obrázek apple.png a zkus vykreslit jablíčka na následující souřadnice:
food = [(2, 0), (5, 1), (1, 4)]
Možná si všimneš, že obrázek má ve hře trošičku „zubaté“ hrany.
To je dáno způsobem, jakým v Pygletu vykreslujeme.
Úplné vysvětlení by se do tohoto návodu nevešlo, potřebuje trochu hlubší
znalosti počítačové grafiky.
Proto uvedu jen řešení.
Do funkce on_draw
, hned za clear
, dej následující tři řádky:
# Lepší vykreslování (pro nás zatím kouzelné zaříkadlo)
pyglet.gl.glEnable(pyglet.gl.GL_BLEND)
pyglet.gl.glBlendFunc(pyglet.gl.GL_SRC_ALPHA, pyglet.gl.GL_ONE_MINUS_SRC_ALPHA)
Teď, když umíš kreslit hada ze čtverců, zkusíme ho udělat hezčího.
Stáhni si archiv snake-tiles.zip
a rozbal si ho tak, aby adresář snake-tiles
s obrázky byl na stejné úrovni
jako program s hrou.
Struktura adresáře by měla vypadat takhle:
V archivuje spousta „kousků“ hada, které můžeme vykreslovat místo zelených čtverečků. Kousky vypadají následovně. Všimni si pojmenování – každý kousek hada buď spojuje dvě strany obrázku, nebo stranu obrázku s hlavou či ocasem. Obrázek se jmenuje odkud-kam.png.
Co jsou taková ta divná „hadí vajíčka”? To je pro případ, že by had byl jen jedno políčko dlouhý – a tedy měl hlavu i ocas na stejném políčku. V dodělané hře se do takového stavu nedostaneme (had bude začínat s délkou 2), ale než hru dokončíme, budou tyhle obrázky užitečné.
Pojďme si teď tyhle obrázky načíst. Šlo by to dělat postupně, třeba takhle:
bottom_left = pyglet.image.load('snake-tiles/bottom-left.png')
bottom_right = pyglet.image.load('snake-tiles/bottom-right.png')
bottom_top = pyglet.image.load('snake-tiles/bottom-top.png')
...
Ale obrázků je spousta, tímhle způsobem by to bylo zdlouhavé a nejspíš bys na některý zapomněla.
Proto Pythonu řekneme, aby nám dal všechny soubory s koncovkou .png
v daném
adresáři.
Na to se dá použít třída Path
z modulu pathlib
.
Zkus si do nového souboru, třeba experiment.py
, napsat následující kód
a spustit ho.
Dokážeš vysvětlit, co dělá?
from pathlib import Path
TILES_DIRECTORY = Path('snake-tiles')
for path in TILES_DIRECTORY.glob('*.png'):
print(path)
My z každého souboru potřebujeme nejlépe jméno, tedy místo
snake-tiles/right-end.png
jenom right-end
.
Na to naštěstí existuje atribut stem
(kořen, t.j. jméno bez přípony).
Místo print(path)
použij:
print(path.stem)
Funguje? Máš vypsané všechny možné kousky hada?
Teď budeme chtít načíst obrázky do slovníku. Klíče slovníku, podle kterých budeme vyhledávat, budou jména, která jsi právě vypsala. Hodnoty pak budou pygletí obrázky, které ve hře můžeš rovnou vykreslit.
Začni s prázdným slovníkem, {}
, a v cyklu for
do něj postupně přidávej
záznamy.
Pak celý slovník vypiš.
Až to budeš mít, měl by výpis vypadat asi takhle:
{'right-tongue': <ImageData 64x64>, 'top-tongue': <ImageData 64x64>,
'right-top': <ImageData 64x64>, 'left-bottom': <ImageData 64x64>,
'end-left': <ImageData 64x64>, 'bottom-tongue': <ImageData 64x64>,
'left-top': <ImageData 64x64>, 'bottom-bottom': <ImageData 64x64>,
...
A teď zkus načtení obrázků začlenit do programu s hadem!
Všechny importy patří nahoru, konstanty pod ně, a dál pak zbytek kódu.
Vypisovat načtený slovník ve hře nemusíš.
Zato ve vykreslovací funkci použij místo green_image
třeba snake_tiles['end-end']
.
Místo čtverečků se teď objeví kuličky – místo hada budeš mít „housenku“.
Místo toho, aby byl všude stejný kousek hada, ale budeme chtít vybrat vždycky ten správný.
Jak na to? Podle čeho ho vybrat?
Pojďme si to vyzkoušet vedle.
Vytvoř soubor smery.py
a napiš do něj:
snake = [(1, 2), (2, 2), (3, 2), (3, 3), (3, 4), (3, 5), (4, 5)]
for x, y in snake:
print(x, y)
Tenhle kód vypisuje souřadnice:
1 2
2 2
3 2
3 3
3 4
3 5
4 5
Zkus vymyslet, jak by se tenhle kód dal změnit, aby vypisoval ke každé souřadnici směr k předchozímu a následujícímu políčku – tedy odkud a kam každý kousek hada „vede“. Takhle:
1 2 end right
2 2 left right
3 2 left top
3 3 bottom top
3 4 bottom top
3 5 bottom right
4 5 left end
Toto je těžký úkol. Nepředpokládám, že ho zvládneš vyřešit hned, i když všechny potřebné informace a nástroje k tomu znáš. Zkus nad tím ale přemýšlet, nech si to rozležet v hlavě třeba přes noc, vrať se k materiálům k předchozím lekcím (hlavně k úvodu do Pythonu), zkoušej a objevuj… A časem na to přijdeš.
Až se to stane, zkus své řešení co nejvíc zjednodušit a pak ho zakomponovat
do vykreslovací funkce místo existujícího cyklu for x, y in snake
.
for ... in ...:
...
x = ...
y = ...
odkud = ...
kam = ...
...
snake_tiles[odkud + '-' + kam].blit(
x * TILE_SIZE, y * TILE_SIZE, width=TILE_SIZE, height=TILE_SIZE)
Soubor smery.py
po vyřešení nemaž, bude se ti pak hodit.
Odměnou za vyřešení tohoto úkolu ti bude had místo housenky.
Než na to přijdeš, zbytek programu ti neuteče. Housenka je úplně stejně hratelná jako had, jen jinak vypadá. Klidně přejdi na další část – logiku hry – s housenkou.