Krušné časy zažívá spousta lidí, kteří jsou nováčky v programování a v grafice, když chtějí pohnout s obrázkem na obrazovce. Bez porozumění celkovému konceptu to může být dost neprůhledné. Nejste první člověk, který se zde zastavil, vynasnažím se co možná nejlépe podat věci krok za krokem. Na konci si dokonce zkusíme metody, které zefektivní naši animaci.
Nebudeme učit programovat v Pythonu, jen představíme něco ze základů pygame.
V pygame je display Plocha (Surface). To je v základě obrázek, který je viditelný na
obrazovce. Obrázek je sestaven z pixelů. Hlavním způsobem, jak změnit tyto pixely, je volání
funkce blit(). Ta kopíruje pixely z jednoho obrázku na druhý.
To je první na pochopení. Když vykreslíte (blit) něco na obrazovku, jednoduše měníte barvy pixelů na obrazovce. Pixely se nepřidávají ani neposouvají, jen měníme barvu těch pixelů, které jsou na obrazovce. Vykreslené obrázky na obrazovce jsou také v pygame Plochy, ale nejsou žádným způsobem propojeny s Plochou displeje. Když se vykreslují na obrazovce, kopírují se na display, ale vy pořád máte jedinečnou kopii originálu.
S tímto stručným popisem snad již porozumíte, co je potřeba k "posunu" obrázku. Ve skutečnosti s ničím neposunujeme. Jednoduše vykreslíme obrázek v nové pozici. Předtím, než to uděláme, musíme "smazat" ten starý. Jinak bude obrázek vidět na obrazovce na dvou místech. Iluzi pohybu dosahujeme rychlým výmazem obrázku a jeho překreslením na nové místo.
Ve zbytku tohoto tutoriálu rozdělíme tento proces na jednodušší kroky. Dokonce si ozřejmíme nejlepší způsoby jak dosáhnout pohybu několika obrázků po obrazovce. Asi máte již spousty otázek. Jako třeba: jak "vymazat" obrázek předtím, než ho namalujeme v nové pozici? Možná jste stále totálně mimo? Doufejme, že zbytek tohoto tutoriálu vám věci přiblíží více.
Je koncept pixelů a obrázků vám stále trochu cizí? Mám pro vás dobrou zprávu, několik příštích odstavců se budeme zabývat kódem, který bude dělat vše co potřebujeme, jen nebude používat pixely. Vytvoříme malý pythonýrský seznam ze 6 čísel a budeme si představovat, že znázorňují nějakou fantastickou grafiku na obrazovce. Asi bude pro dosti překvapující, jak přesně to bude znázorňovat, co budeme dělat později se skutečnou grafikou.
Začněme vytvořením našeho seznamu obrazovky a vyplňme ho úžasnou krajinou jedniček a dvojek.Tím jsme si vytvořili pozadí. Nebude moc vzrušující, dokud nenakreslíme na obrazovku také hráče. Vytvoříme mocného hrdinu, který bude vypadat jako číslo 8. Strčme ho někam doprostřed mapy a podívejme se, jak to bude vypadat.>>> obrazovka = [1, 1, 2, 2, 2, 1]
>>> print obrazovka
[1, 1, 2, 2, 2, 1]
Teď jsme tedy tak daleko, jako kdybychom skočili přímo na nějaké grafické programování s pygame. Máte na obrazovce vcelku slušný materiál, ale nelze s ním nikam hýbat. Možná teď, když nám obrazovku představuje seznam čísel, to bude jednodušší pochopit?>>> obrazovka[3] = 8
>>> print obrazovka
[1, 1, 2, 8, 2, 1]
Nyní bude snadné ho posunout na nové místo. Jednoduše změníme hodnotu>>> poziceHrdiny = 3
>>> obrazovka[poziceHrdiny] = 8
>>> print obrazovka
[1, 1, 2, 8, 2, 1]
poziceHrdiny a vykreslíme ho znovu na obrazovku.Ooops. Teď máme hrdiny dva. Jednoho na staré pozici a jednoho na nové. To je přesně ten důvod, proč je třeba nejdříve vymazat hrdinu z jeho staré pozice. K tomu potřebujeme znát, co na této pozici bylo předtím, než jsme ho tam vykreslili. To znamená, že si musíme ještě pamatovat hodnoty obrazovky před vykreslením hrdiny. Je několik cest, jak toho dosáhnout, ale snad nejednodušší cesta je schovat si odděleně kopii pozadí obrazovky. To ale znamená, že musíme v naší malé hře udělat malé úpravy.>>> poziceHrdiny = poziceHrdiny - 1
>>> obrazovka[poziceHrdiny] = 8
>>> print obrazovka
[1, 1, 8, 8, 2, 1]
Možná to vypadá na spoustu nadbytečné práce a přitom nejsme dále než jsme byli předtím, když jsme se ho snažili posouvat. Tentokrát ale máme extra informaci, kterou potřebujeme na řádný pohyb.>>> pozadi = [1, 1, 2, 2, 2, 1]
>>> obrazovka = [0]*6 # nová prázdná obrazovka
>>> for i in range(6):
... obrazovka[i] = pozadi[i]
>>> print obrazovka
[1, 1, 2, 2, 2, 1]
>>> poziceHrdiny = 3
>>> obrazovka[poziceHrdiny] = 8
>>>> print obrazovka
[1, 1, 2, 8, 2, 1]
A je to. Hrdina se posunul o jedno místo doleva. Tento kód můžeme opakovaně použít při posunu doleva.>>> print obrazovka
[1, 1, 2, 8, 2, 1]
>>> obrazovka[poziceHrdiny] = pozadi[poziceHrdiny]
>>> poziceHrdiny = poziceHrdiny - 1
>>> obrazovka[poziceHrdiny] = 8
>>> print obrazovka
[1, 1, 8, 2, 2, 1]
Výborně! Není to, jak byste asi rádi, hladká animace, ale s několika malými změnami to bude fungovat stejně s grafikou na obrazovce.>>> obrazovka[poziceHrdiny] = pozadi[poziceHrdiny]
>>> poziceHrdiny = poziceHrdiny - 1
>>> obrazovka[poziceHrdiny] = 8
>>> print obrazovka
[1, 8, 2, 2, 2, 1]
Hmm, kód by měl mít známou strukturu a což je důležitější, tento kód by měl dávat trochu smysl. Snad moje přirovnání s nastavením hodnot v seznamu ukazuje podobnost s nastavením pixelů na obrazovce (pomocí>>> pozadi = [teren1, teren1, teren2, teren2, teren2, teren1]
>>> obrazovka = vytvorObrazovku()
>>> for i in range(6):
... obrazovka.blit(pozadi[i], (i*10, 0)) # aby byly umístěny vedle sebe a nepřekrývaly se
>>> poziceHrdiny = 3
>>> obrazovka.blit(obrazekHrdiny, (poziceHrdiny*10, 0))
blit). Jediná část, která vypadá skutečně jinak a
která zatím není vysvětlena dostatečně, je pozice hrdiny na souřadnicích obrazovky. Zatím nám
musí stačit nevysvětlené (poziceHrdiny*10,0), ale brzy to napravíme. Nyní pohněme
s hrdinou v prostoru. Tento kód by neměl být překvapením.A je to. S tímto kouskem kódu dokážeme zobrazit jednoduché pozadí s hrdinou. Pak řádně posuneme hrdinu doleva o jedno místo. Kam se vydáme odsud? Pro někoho je stále tento kód trochu hloupý. Budeme se snažit zaprvé najít čistější způsob znázorňování pozadí a pozice hráče. Pak snad trochu hladší, skutečnější animace.>>> obrazovka.blit(pozadi[poziceHrdiny], (poziceHrdiny*10, 0))
>>> poziceHrdiny = poziceHrdiny - 1
>>> obrazovka.blit(obrazekHrdiny, (poziceHrdiny*10, 0))
blit(), kam
ten obrázek dát. V pygame vždy předáváme pozici jako souřadnice (x,y). To představuje počet
pixelů doprava a počet pixelů dolů a na tomto místě bude umístěn obrázek. Levý horní roh Plochy
má souřadnici (0,0) . Posunutím trochu směrem doprava se dostaneme na (10,0) a dalším posunutím
směrem dolů jsme na souřadnici (10,10). Když vykreslujeme pomocí blit, tyto
souřadnice představují, kde by měl být umístěn levý horní roh obrázku.Rect (Obdélník). Rect v zásadě představuje obdélníkovou oblast na těchto souřadnicích. Má levý horní roh a
velikost. Rect přichází s mnoha pohodlnými metodami, které pomáhají při jejich posunu a
umísťování. V našem dalším příkladu budeme používat místo souřadnic našich objektů
Obdélníky.blit() umí
přijmout jako argument Obdélník - jednoduše použije levý horní roh jako skutečnou pozici..pygame.display.update(), které nám následně ukáže vše na skutečné obrazovce.Tak asi tak. Je to celý kód, který potřebujeme, když chceme hladce posouvat objekt po obrazovce. Další výhoda tohoto způsobu vytvoření pozadí je, že obrázek hrdiny může mít průhlednost nebo vystřižené oblasti a přesto se bude na obrazovce vykreslovat správně (bonus zdarma).obrazovka = vytvorObrazovku()
hrdina = nahrajObrazekHrdiny()
pozadi = nahrajObrazekPozadi()
obrazovka.blit(pozadi, (0, 0)) #vykresli pozadí
pozice = hrdina.get_rect()
obrazovka.blit(hrdina, pozice) #vykresli hrdinu
pygame.display.update() #vše to ukaž
for x in range(100): #animace 100 snímků
obrazovka.blit(pozadi, pozice, pozice) #vymazat
pozice = pozice.move(2, 0) #posuň hrdinu
obrazovka.blit(hrdina, pozice) #vykresli nového hrdinu
pygame.display.update() #vše to ukaž
pygame.time.delay(100) #zastav program na 1/10 sekundy
pygame.time.delay(). To trochu zpomalí
náš program, jinak by mohl být také tak rychlý, že bychom nic neviděli.nahrajObrazekHrdiny()?
Také by se nám hodil jednoduchý vstup od uživatele a smyčka s více než 100 snímky. Použijeme
příklad, který zde máme a přeložíme ho do objektově orientovaného výtvoru, aby na nás mohla být
maminka pyšná.pygame.image má funkci load(),která umí co chceme. Řádky, kde
se nahrávají obrázky budou vypadat takto.
hrdina = pygame.image.load('hrdina.jpg').convert()
pozadi = pygame.image.load('pozadi.jpg').convert()
Vidíme, že je to celkem snadné, funkce prostě bere jméno souboru a vrací novou
Plochu s nahraným obrázkem. Po nahrátí voláme metodu Plochy convert(). Ta vrací
opět novou Plochu z obrázku, nyní však konvertovanou do stejného pixelového formátu jako je dáš
display. Naše vykreslování bude tudíž velmi rychlé, protože máme stejné formáty displeje i
obrázků. Kdybychom nezkonvertovali, funkce blit() by byla pomalejší, protože při
každém volání by musela konvertovat z jednoho typu pixelů do jiného.load() tak convert() vrací nové
Plochy. To znamená, že ve skutečnosti na každém řádku vytváříme dvě Plochy. V jiných
programovacích jazycích to má za následek paměťovou díru (to není dobrá věc). Python je
dostatečně bystrý, že to ošetří a pygame řádně uklidí Plochu, kterou jsme nepoužili.vytvorObrazovku(). Pygame
umožňuje snadno vytvořit nové okno pro grafiku. Kód na vytvoření plochy 640x480 je níže. Bez
argumentů pygame prostě vybere nejlepší barevnou hloubku a pixelový formát sama.obrazovka = pygame.display.set_mode((640, 480))
Ve smyčce budeme procházet navěky a kontrolovat, jestli nastala nějaká událost od uživatele.Opustíme program, pokud uživatel stiskl něco na klávesnici nebo zavřel okno. Po kontrole všech událostí přesuneme naše objekty. Nezapomeneme je předtím smazat. Slova QUIT a KEYDOWN jsou konstanty obsažené v moduluwhile 1:
for udalost in pygame.event.get():
if udalost.type in (QUIT, KEYDOWN):
sys.exit()
posun_a_vykresli_vsechny_objekty()
pygame.locals, který nesmíme zapomenou do
našeho programu naimportovat.V naší třídě máme dvě funkce. Funkce init vytváří náš objekt. Nastavuje jeho polohu a rychlost. Funkce posun pohne s objektem o jeden krok. Pokud se dostane příliš daleko, vrátí ho zpět doleva.class ObjektHry:
def __init__(self, obrazek, vyska, rychlost):
self.rychlost = rychlost
self.obrazek = obrazek
self.pozice = obrazek.get_rect().move(0, vyska)
def posun(self):
self.pozice = self.pozice.move(self.rychlost, 0)
if self.pozice.right > 640:
self.pozice.left = 0
To je vše. Tímto kódem animujeme 10 objektů na obrazovce. Jediný bod, který snad ještě potřebuje vysvětlení je, proč máme dvě smyčky, kde mažeme a kreslíme všechny objekty. Před vykreslením všech objektů je třeba tyto nejdříve smazat. V našem příkladě by snad nevadilo, kdybychom mazání i kreslení dali do smyčky jediné, ale když se objekty překrývají, jsou dvě smyčky důležité.import pygame, sys
from pygame.locals import *
pygame.init()
class ObjektHry:
def __init__(self, obrazek, vyska, rychlost):
self.rychlost = rychlost
self.obrazek = obrazek
self.pozice = obrazek.get_rect().move(0, vyska)
def posun(self):
self.pozice = self.pozice.move(self.rychlost, 0)
if self.pozice.right > 640:
self.pozice.left = 0
obrazovka = pygame.display.set_mode((640, 480))
hrdina = pygame.image.load('hrdina.jpg').convert()
pozadi = pygame.image.load('pozadi.jpg').convert()
obrazovka.blit(pozadi, (0, 0))
objekty = []
for x in range(10):
o = ObjektHry(hrdina, x*40, x)
objekty.append(o)
while 1:
for udalost in pygame.event.get():
if udalost.type in (QUIT, KEYDOWN):
sys.exit()
for o in objekty:
obrazovka.blit(pozadi, o.pozice, o.pozice)
for o in objekty:
o.posun()
obrazovka.blit(o.obrazek, o.pozice)
pygame.display.update()
pygame.time.delay(100)
Takže co bude další na vaší cestě k učení? Nejdříve si pohrajte trochu s tímto příkladem. Přepište kód do editoru a zkuste měnit nejrůznější věci. Hrajte si s ním, pouštějte ho a učte se.
Snad budete chtít pracovat na tom, abyste měli více než jeden typ objektu. Abyste nalezli
způsob, jak čistě "smazat" objekt, když už ho nebudete vůbec potřebovat. Také změna volání
display.update(), abyste mu mohli předávat seznam oblastí na obrazovce, které se
změnily.
Jsou i další tutoriály v pygame, které zahrnují tuto tématiku. Takže když jste ready, pokračujte ve čtení. :-)
Nakonec vás pozývám do pygame mailing listu nebo irc s jakoukoliv otázkou k tomuto tématu. Vždycky tam někdo je, kdo vám podá pomocnou ruku.
A nakonec - hodně zábavy, od toho přeci hry jsou!