Obsah
1. Animace založené na tweeningu
2. Animace v nástroji Pygame Zero
3. Automatické spuštění animace po inicializaci programu
4. Kostra aplikace demonstrující animační schopnosti Pygame Zero
5. Nastavení rychlosti animace
6. Zrychlování a/nebo zpomalování animace
7. Rozkmitání objektu na začátku a/nebo na konci animace
8. Odraz objektu na začátku a/nebo na konci animace
9. Složitější animace složená ze série na sebe navazujících animací
10. Animace většího množství objektů ve scéně
12. Ukázka základu hry typu Asteroids
13. Zvuky a hudba v Pygame Zero
14. Podpora pro přehrávání zvuků
15. Demonstrační příklad: přehrání zvuku po stisku klávesy
16. Podpora pro přehrávání hudby
17. Demonstrační příklad: přehrání hudby inicializované po spuštění
18. Relevantní funkce z knihovny Pygame
19. Repositář s demonstračními příklady
1. Animace založené na tweeningu
V první části dnešního článku o nástroji Pygame Zero si ukážeme tvorbu jednodušších a následně i poměrně komplikovaných animací. Ty jsou – minimálně v Pygame Zero – založeny na takzvaném tweeningu, což znamená, že je nějakým způsobem popsán začátek animace a současně i její konec. Jednotlivé snímky animace (resp. přesněji řečeno mezisnímky) mezi známým začátkem a koncem jsou dopočteny automaticky, typicky s využitím lineární či nelineární interpolace mezi klíčovými snímky. A právě tento výpočet mezisnímků se nazývá tweening, což je slovo získané zkrácením slova inbetweening.
O tweeningu jsme se již na stránkách Roota minimálně jednou zmínili, a to konkrétně při popisu dnes již historického Autodesk Animatoru, za jehož vývojem stál Jim Kent, jenž ještě před Autodesk Animatorem pracoval i na vývoji dalších animačních programů, zejména Aegis Animatoru a Cyber Paintu. Rozšířený tweening nalezneme i v moderních animačních programech – viz například stránky společnosti Adobe.
Jim Kent v prakticky všech svých animačních programech experimentoval s animační technikou zvanou tweening zmíněnou v úvodním odstavci. Jedná se o označení, které původně odkazovalo na profesi tweenera, tedy člověka, který ručně kreslil přechody mezi klíčovými snímky vytvořené animátorem. A prakticky stejný způsob nalezneme například již v Aegis Animatoru – explicitně je nutné vytvořit pouze klíčové snímky, přičemž mezisnímky automaticky dopočítá software. Příkladem může být změna pozice nějakého spritu, změna tvaru mnohoúhelníku, vyplňování barvou, aplikace jednoduchého filtru atd. Mezisnímek vypočtený Animatorem obsahoval rozdíly mezi dvojicí snímků, přičemž tyto rozdíly byly zobrazeny první barvou palety, což je většinou (alespoň v dobovém animačním software) barva modrá. Tyto rozdíly, popř. i další pomocné prvky (guides) bylo možné smazat jediným příkazem (clear guides) a vytvořit tak z pomocného mezisnímku skutečnou součást výsledné animace.
Obrázek 1: Animace vytvořená v Cyber Paintu přímo Jimem Kentem.
2. Animace v nástroji Pygame Zero
Připomeňme si ve stručnosti, jakým způsobem se animace založená na tweeningu specifikuje v nástroji Pygame Zero. Víme již, že samotná animace se vytvoří funkcí pojmenovanou animate, které se předávají minimálně dva parametry:
- Objekt, jehož atributy se mají v rámci animace měnit
- Atribut(y) a jejich konečné parametry po dokončení animace
Podívejme se nyní na jednoduchý demonstrační příklad, v němž budeme postupně měnit pozici spritu, který plynule přejede ze své původní polohy na místo označené kurzorem myši (ten určí nový střed spritu). Animovaným objektem tedy bude sprite, atributem pak pos a koncovou hodnotou tohoto atributu je pozice kurzoru myši ve chvíli, kdy uživatel stiskne její (libovolné) tlačítko. Animace se deklaruje následovně:
def on_mouse_down(pos, button): animate(sprite, pos=pos)
Obrázek 2: Výchozí pozice spritu ve scéně ihned po spuštění aplikace.
Obrázek 3: Nová pozice spritu ve scéně po stisku libovolného tlačítka myši.
Úplný zdrojový kód příkladu, v němž se po stisku myši sprite plynule přesune na místo kurzoru myši, vypadá následovně:
WIDTH = 480 HEIGHT = 480 BACKGROUND_COLOR = (0, 0x80, 0x80) sprite = Actor("sprite1.png") sprite.pos = (240, 240) def draw(): screen.fill(BACKGROUND_COLOR) sprite.draw() def on_key_down(key, mod, unicode): if key == keys.ESCAPE: exit() def on_mouse_down(pos, button): animate(sprite, pos=pos)
3. Automatické spuštění animace po inicializaci programu
V některých aplikacích budeme vyžadovat spuštění animace ihned po inicializaci programu. To lze ve skutečnosti provést jednoduše, protože kód, který není umístěn ve funkcích, je systémem Pygame Zero automaticky spuštěn (neexistuje zde koncept funkce main atd.). V následujícím programu je tedy sprite ihned po jeho spuštění postupně přesouván z levého okraje (x=0) až na pravý okraj (x=WIDTH). Animace je ve výpisu zvýrazněna podtržením:
WIDTH = 480 HEIGHT = 480 BACKGROUND_COLOR = (0, 0x80, 0x80) sprite = Actor("sprite1.png") sprite.pos = (0, 240) animation = animate(sprite, x=WIDTH) def draw(): screen.fill(BACKGROUND_COLOR) sprite.draw() def on_key_down(key, mod, unicode): if key == keys.ESCAPE: exit()
4. Kostra aplikace demonstrující animační schopnosti Pygame Zero
Nyní si ukažme zdrojový kód aplikace, na které budeme postupně demonstrovat základní animační schopnosti nástroje Pygame Zero. Tato aplikace po svém spuštění vypíše nápovědu s popisem kláves, kterými se může ovládat:
MESSAGES = ( "F - fast animation", "R - reset animation", "", "Esc - exit" ) def draw(): screen.fill(BACKGROUND_COLOR) y = 10 for message in MESSAGES: screen.draw.text(message, (TEXT_LEFT, y), color=TEXT_COLOR) y += TEXT_HEIGHT
Současně se do scény vykreslí i sprite, jehož původní pozice je nastavena na levý okraj okna aplikace:
sprite = Actor("sprite1.png") sprite.pos = (0, 240) def draw(): sprite.draw()
Obrázek 4: Kostra aplikace pro demonstraci animací je ve skutečnosti plně funkčním programem – výchozí situace.
Prozatím je v aplikaci implementována jediná animace, a to konkrétně plynulý přesun spritu z levého okraje okna směrem k okraji pravému:
if key == keys.R: sprite.x = 0 if key == keys.F: animate(sprite, x=WIDTH)
Obrázek 5: Kostra aplikace pro demonstraci animací je ve skutečnosti plně funkčním programem – situace po dokončení animace.
Úplný (a současně i plně funkční) zdrojový kód kostry aplikace může vypadat následovně:
WIDTH = 480 HEIGHT = 480 BACKGROUND_COLOR = (0, 0x80, 0x80) TEXT_COLOR = "Orange" TEXT_LEFT = 10 TEXT_HEIGHT = 16 MESSAGES = ( "F - fast animation", "R - reset animation", "", "Esc - exit" ) sprite = Actor("sprite1.png") sprite.pos = (0, 240) def draw(): screen.fill(BACKGROUND_COLOR) sprite.draw() y = 10 for message in MESSAGES: screen.draw.text(message, (TEXT_LEFT, y), color=TEXT_COLOR) y += TEXT_HEIGHT def on_key_down(key, mod, unicode): if key == keys.ESCAPE: exit() if key == keys.R: sprite.x = 0 if key == keys.F: animate(sprite, x=WIDTH)
5. Nastavení rychlosti animace
Celá animace je při výchozím nastavení provedena za jednu sekundu (interně se výpočty provádí s přesností jedné šedesátiny sekundy). Ovšem pochopitelně budeme chtít, aby se některé animace provedly rychleji a jiné zase pomaleji. K tomuto účelu slouží nepovinný parametr duration:
animate(sprite, x=WIDTH, duration=1)
Dobu trvání animace můžeme prodloužit, například na pět sekund:
animate(sprite, x=WIDTH, duration=5)
V upraveném demonstračním příkladu je možné animaci spustit jak rychle (tlačítkem F), tak i pomalu (tlačítkem S):
Obrázek 6: Nová podoba testovací aplikace.
Opět si uvedeme úplný kód našeho upraveného demonstračního příkladu:
WIDTH = 480 HEIGHT = 480 BACKGROUND_COLOR = (0, 0x80, 0x80) TEXT_COLOR = "Orange" TEXT_LEFT = 10 TEXT_HEIGHT = 16 MESSAGES = ( "F - fast animation", "S - slow animation", "R - reset animation", "", "Esc - exit" ) sprite = Actor("sprite1.png") sprite.pos = (0, 240) def draw(): screen.fill(BACKGROUND_COLOR) sprite.draw() y = 10 for message in MESSAGES: screen.draw.text(message, (TEXT_LEFT, y), color=TEXT_COLOR) y += TEXT_HEIGHT def on_key_down(key, mod, unicode): if key == keys.ESCAPE: exit() if key == keys.R: sprite.x = 0 if key == keys.F: animate(sprite, x=WIDTH, duration=1) if key == keys.S: animate(sprite, x=WIDTH, duration=5)
6. Zrychlování a/nebo zpomalování animace
Prozatím animace probíhala konstantní rychlostí, přesněji řečeno se atribut či atributy objektu měnily v čase lineárně. Mnohdy je ovšem vhodné animaci na začátku urychlit, popř. na konci zpomalit. I toto chování je možné v nástroji Pygame Zero řídit, a to konkrétně nepovinným parametrem duration.
Zrychlení animace na začátku (z nulové rychlosti):
animate(sprite, x=WIDTH, duration=1, tween="accelerate")
Zpomalení animace na konci (na nulovou rychlost):
animate(sprite, x=WIDTH, duration=1, tween="decelerate")
Zrychlení animace na začátku a současně zpomalení na konci:
animate(sprite, x=WIDTH, duration=1, tween="accel_decel")
Obrázek 7: Podoba upraveného demonstračního příkladu po jeho spuštění.
Následuje výpis zdrojového kódu takto upraveného demonstračního příkladu:
WIDTH = 480 HEIGHT = 480 BACKGROUND_COLOR = (0, 0x80, 0x80) TEXT_COLOR = "Orange" TEXT_LEFT = 10 TEXT_HEIGHT = 16 MESSAGES = ( "F - fast animation", "S - slow animation", "R - reset animation", "", "A - fast animation, accelerated tween", "D - fast animation, decelerated tween", "B - fast animation, both accelerated and decelerated tween", "", "Esc - exit" ) sprite = Actor("sprite1.png") sprite.pos = (0, 240) def draw(): screen.fill(BACKGROUND_COLOR) sprite.draw() y = 10 for message in MESSAGES: screen.draw.text(message, (TEXT_LEFT, y), color=TEXT_COLOR) y += TEXT_HEIGHT def on_key_down(key, mod, unicode): if key == keys.ESCAPE: exit() if key == keys.R: sprite.x = 0 if key == keys.F: animate(sprite, x=WIDTH, duration=1) if key == keys.S: animate(sprite, x=WIDTH, duration=5) if key == keys.A: animate(sprite, x=WIDTH, duration=1, tween="accelerate") if key == keys.D: animate(sprite, x=WIDTH, duration=1, tween="decelerate") if key == keys.B: animate(sprite, x=WIDTH, duration=1, tween="accel_decel")
7. Rozkmitání objektu na začátku a/nebo na konci animace
Existuje ještě další varianta, jak může animace začít, popř. skončit. Tato varianta spočívá v rozkmitání objektu ještě předtím, než se začne pohybovat (to pochopitelně platí pouze za předpokladu, že animujeme pohyb objektu ve scéně – můžeme totiž „animovat“ prakticky jakýkoli atribut). I tato modifikace animace se zadává pomocí nepovinného parametru tween, ovšem s odlišnými hodnotami.
Rozkmitání objektu před vlastním pohybem:
animate(sprite, x=WIDTH, duration=2, tween="in_elastic")
Zakmitání objektu po zastavení:
animate(sprite, x=WIDTH, duration=2, tween="out_elastic")
Kombinace obou předchozích možností:
animate(sprite, x=WIDTH, duration=2, tween="in_out_elastic")
Obrázek 8: Další podoba upraveného demonstračního příkladu po jeho spuštění.
Opět si ukažme, jak vypadá úplný zdrojový kód takto upraveného příkladu:
WIDTH = 480 HEIGHT = 480 BACKGROUND_COLOR = (0, 0x80, 0x80) TEXT_COLOR = "Orange" TEXT_LEFT = 10 TEXT_HEIGHT = 16 MESSAGES = ( "F - fast animation", "S - slow animation", "R - reset animation", "", "I - fast animation, elastic @ in tween", "O - fast animation, elastic @ out tween", "B - fast animation, elastic @ both sides tween", "", "Esc - exit" ) sprite = Actor("sprite1.png") sprite.pos = (0, 240) def draw(): screen.fill(BACKGROUND_COLOR) sprite.draw() y = 10 for message in MESSAGES: screen.draw.text(message, (TEXT_LEFT, y), color=TEXT_COLOR) y += TEXT_HEIGHT def on_key_down(key, mod, unicode): if key == keys.ESCAPE: exit() if key == keys.R: sprite.x = 0 if key == keys.F: animate(sprite, x=WIDTH, duration=1) if key == keys.S: animate(sprite, x=WIDTH, duration=5) if key == keys.I: animate(sprite, x=WIDTH, duration=2, tween="in_elastic") if key == keys.O: animate(sprite, x=WIDTH, duration=2, tween="out_elastic") if key == keys.B: animate(sprite, x=WIDTH, duration=2, tween="in_out_elastic")
8. Odraz objektu na začátku a/nebo na konci animace
Konečně se dostáváme k poslední modifikaci animace podporované nástrojem Pygame Zero. Tato modifikace spočívá v tom, že se objekt na začátku či na konci animace několikrát „odrazí“ od počáteční nebo koncové hodnoty. V naprosté většině případů má smysl použít odrazy na konci, ovšem pro úplnost si opět uvedeme všechny podporované možnosti (asi není velkým překvapením, že se opět využije parametr tween).
Odrazy na začátku animace:
animate(sprite, x=WIDTH-BORDER, duration=2, tween="bounce_start")
Odrazy na konci animace:
animate(sprite, x=WIDTH-BORDER, duration=2, tween="bounce_end")
Odrazy na začátku a současně i na konci animace:
animate(sprite, x=WIDTH-BORDER, duration=2, tween="bounce_start_end")
Obrázek 9: Upravená varianta demonstračního příkladu.
Poslední varianta demonstračního příkladu s animací řízenou uživatelem pomocí kláves vypadá takto:
WIDTH = 480 HEIGHT = 480 BACKGROUND_COLOR = (0, 0x80, 0x80) TEXT_COLOR = "Orange" TEXT_LEFT = 10 TEXT_HEIGHT = 16 MESSAGES = ( "F - fast animation", "S - slow animation", "R - reset animation", "", "T - fast animation, bounce @ start tween", "E - fast animation, bounce @ end tween", "B - fast animation, bounce @ both sides tween", "", "Esc - exit" ) sprite = Actor("sprite1.png") BORDER = sprite.width/2 - 5 sprite.pos = (BORDER, 240) def draw(): screen.fill(BACKGROUND_COLOR) sprite.draw() y = 10 for message in MESSAGES: screen.draw.text(message, (TEXT_LEFT, y), color=TEXT_COLOR) y += TEXT_HEIGHT def on_key_down(key, mod, unicode): if key == keys.ESCAPE: exit() if key == keys.R: sprite.x = BORDER if key == keys.F: animate(sprite, x=WIDTH-BORDER, duration=1) if key == keys.S: animate(sprite, x=WIDTH-BORDER, duration=5) if key == keys.T: animate(sprite, x=WIDTH-BORDER, duration=2, tween="bounce_start") if key == keys.E: animate(sprite, x=WIDTH-BORDER, duration=2, tween="bounce_end") if key == keys.B: animate(sprite, x=WIDTH-BORDER, duration=2, tween="bounce_start_end")
9. Složitější animace složená ze série na sebe navazujících animací
Nástroj Pygame Zero umožňuje specifikaci funkce, která se má zavolat po dokončení animace. A právě tato vlastnost nám umožňuje vytváření složitějších animací postavených na konceptu takzvaných klíčových snímků (keyframe), mezi kterými jsou dopočítány mezisnímky (inbetween → tween). Podívejme se nyní na způsob zadání takové animace, při které objekt (sprite) objede po všech okrajích okna. Celá animace je složena ze série jednodušších lineárních posunů, každý reprezentovaný samostatnou funkcí:
def a1(): animate(sprite, x=WIDTH-BORDER, on_finished=a2) def a2(): animate(sprite, y=HEIGHT-BORDER, on_finished=a3) def a3(): animate(sprite, x=BORDER, on_finished=a4) def a4(): animate(sprite, y=BORDER)
Celá animace je spuštěna klávesou W:
def on_key_down(key, mod, unicode): if key == keys.W: animate(sprite, x=BORDER, y=BORDER, on_finished=a1)
Úplný zdrojový kód demonstračního příkladu, který provádí tuto již poněkud složitější animaci, vypadá následovně:
WIDTH = 480 HEIGHT = 480 BACKGROUND_COLOR = (0, 0x80, 0x80) TEXT_COLOR = "Orange" TEXT_LEFT = 10 TEXT_HEIGHT = 16 MESSAGES = ( "F - fast animation", "R - reset animation", "", "W - walk around screen", "Esc - exit" ) sprite = Actor("sprite1.png") BORDER = sprite.width/2 - 5 sprite.pos = (BORDER, HEIGHT/2) def draw(): screen.fill(BACKGROUND_COLOR) sprite.draw() y = 10 for message in MESSAGES: screen.draw.text(message, (TEXT_LEFT, y), color=TEXT_COLOR) y += TEXT_HEIGHT def on_key_down(key, mod, unicode): if key == keys.ESCAPE: exit() if key == keys.R: sprite.x = BORDER sprite.y = HEIGHT/2 if key == keys.F: animate(sprite, x=WIDTH-BORDER) if key == keys.W: animate(sprite, x=BORDER, y=BORDER, on_finished=a1) def a1(): animate(sprite, x=WIDTH-BORDER, on_finished=a2) def a2(): animate(sprite, y=HEIGHT-BORDER, on_finished=a3) def a3(): animate(sprite, x=BORDER, on_finished=a4) def a4(): animate(sprite, y=BORDER)
10. Animace většího množství objektů ve scéně
Nic nám nebrání v tom, aby se v jeden okamžik spustilo několik animací. Ty budou prováděny současně. Nejdříve si opět připravme kostru příkladu, která prozatím bude provádět animaci jediného spritu (částice). Tato částice bude padat z horního okraje obrazovky směrem za její spodní okraj. Přesné souřadnice začátku a konce jsou zvoleny pseudonáhodně a částice je urychlována (jakoby skutečně padala v gravitačním poli):
from random import randrange WIDTH = 480 HEIGHT = 480 BACKGROUND_COLOR = (0, 0x80, 0x80) X_SPREAD = 50 sprite = Actor("particle.png") sprite.pos = (240, 0) def a1(): sprite.x = WIDTH/2 + randrange(-X_SPREAD, X_SPREAD) sprite.y = -10 animate(sprite, x=WIDTH/2 + randrange(-X_SPREAD, X_SPREAD), y=HEIGHT, on_finished=a1, duration=5, tween="accelerate") def draw(): screen.fill(BACKGROUND_COLOR) sprite.draw() def on_key_down(key, mod, unicode): if key == keys.ESCAPE: exit() a1()
Obrázek 10: Jediná padající částice.
Rozšíření na větší počet částic (v našem konkrétním případě je jejich počet řízen pomocí SPRITES_COUNT) je již snadné. Vytvoříme seznam sprites se všemi částicemi a posléze animaci spustíme s tím, že se po dopadu částice animace zopakuje:
from random import randrange WIDTH = 480 HEIGHT = 480 BACKGROUND_COLOR = (0, 0x80, 0x80) X_SPREAD = 100 Y_SPREAD = 200 SPRITES_COUNT = 100 sprites = None def init_sprites(): global sprites sprites = [Actor("particle.png") for i in range(SPRITES_COUNT)] for sprite in sprites: sprite.pos = (240, 0) a1(sprite) def a1(sprite): sprite.x = WIDTH/2 + randrange(-X_SPREAD, X_SPREAD) sprite.y = 0 - randrange(Y_SPREAD) animate(sprite, x=WIDTH/2 + randrange(-X_SPREAD, X_SPREAD), y=HEIGHT + randrange(Y_SPREAD), on_finished=lambda: a1(sprite), duration=randrange(2, 7), tween="accelerate") def draw(): screen.fill(BACKGROUND_COLOR) for sprite in sprites: sprite.draw() def on_key_down(key, mod, unicode): if key == keys.ESCAPE: exit() init_sprites()
Obrázek 11: Větší množství animovaných spritů ve scéně.
animate(sprite, x=WIDTH/2 + randrange(-X_SPREAD, X_SPREAD), y=HEIGHT + randrange(Y_SPREAD), on_finished=lambda: a1(sprite), duration=randrange(2, 7), tween="accelerate")
11. Rotace spritů
Sprity je možné otáčet, a to konkrétně změnou atributu angle. V následujícím demonstračním příkladu je otáčení prováděno kontinuálně, takže nepoužijeme klasickou animaci, ale budeme úhel měnit ve funkci update, která je volaná šedesátkrát za sekundu:
def update(): sprite.left += dx sprite.top += dy sprite.angle += 1
Obrázek 12: Otáčení spritu ve scéně.
Opět si ukážeme celý zdrojový kód tohoto demonstračního příkladu:
WIDTH = 480 HEIGHT = 480 BACKGROUND_COLOR = (0, 0x80, 0x80) sprite = Actor("sprite1.png") sprite.pos = (240, 240) dx = 0 dy = 0 def draw(): screen.fill(BACKGROUND_COLOR) sprite.draw() def update(): sprite.left += dx sprite.top += dy sprite.angle += 1 def on_key_down(key, mod, unicode): global dx, dy if key == keys.ESCAPE: exit() if key == keys.UP: dy = -1 if key == keys.DOWN: dy = 1 if key == keys.LEFT: dx = -1 if key == keys.RIGHT: dx = 1 def on_key_up(key, mod): global dx, dy if key == keys.ESCAPE: exit() if key == keys.UP or key == keys.DOWN: dy = 0 if key == keys.LEFT or key == keys.RIGHT: dx = 0
Upravit můžeme i předchozí příklad s animovanými částicemi tak, aby se částice při pádu náhodně otáčely:
from random import randrange WIDTH = 480 HEIGHT = 480 BACKGROUND_COLOR = (0, 0x80, 0x80) X_SPREAD = 100 Y_SPREAD = 200 SPRITES_COUNT = 100 sprites = None def init_sprites(): global sprites sprites = [Actor("particle.png") for i in range(SPRITES_COUNT)] for sprite in sprites: sprite.pos = (240, 0) a1(sprite) def a1(sprite): sprite.x = WIDTH/2 + randrange(-X_SPREAD, X_SPREAD) sprite.y = 0 - randrange(Y_SPREAD) animate(sprite, x=WIDTH/2 + randrange(-X_SPREAD, X_SPREAD), y=HEIGHT + randrange(Y_SPREAD), angle=randrange(360), on_finished=lambda: a1(sprite), duration=randrange(2, 7), tween="accelerate") def draw(): screen.fill(BACKGROUND_COLOR) for sprite in sprites: sprite.draw() def on_key_down(key, mod, unicode): if key == keys.ESCAPE: exit() init_sprites()
12. Ukázka základu hry typu Asteroids
Nyní již máme k dispozici všechny potřebné znalosti k tomu, abychom vytvořili základ jednoduché hry typu Asteroids. Prozatím budeme implementovat jen pohyb rakety s otáčením a dynamickou změnou rychlosti kurzorovými tlačítky (raketa navíc sama zpomaluje, což je sice ve vesmíru nesmysl, ale alespoň si můžeme odzkoušet implementaci zpomalování):
Obrázek 13: Snímek ze „hry“.
from math import sin, cos, radians WIDTH = 480 HEIGHT = 480 BACKGROUND_COLOR = (0, 0x80, 0x80) sprite = Actor("spaceship.png") sprite.pos = (240, 240) speed_delta = 0 angle_delta = 0 speed = 0 angle = 0 def draw(): screen.fill(BACKGROUND_COLOR) sprite.draw() def update(): global speed, angle, speed_delta angle += angle_delta sprite.top -= speed*cos(radians(angle)) sprite.left -= speed*sin(radians(angle)) sprite.angle = angle speed += speed_delta speed *= 0.95 def on_key_down(key, mod, unicode): global speed_delta, angle_delta, speed if key == keys.ESCAPE: exit() if key == keys.R: speed_delta = 0 speed = 0 sprite.x = WIDTH/2 sprite.y = HEIGHT/2 if key == keys.UP: speed_delta = 1.5 if key == keys.DOWN: speed_delta = -1.5 if key == keys.LEFT: angle_delta = 1 if key == keys.RIGHT: angle_delta = -1 def on_key_up(key, mod): global speed_delta, angle_delta if key == keys.ESCAPE: exit() if key == keys.LEFT or key == keys.RIGHT: angle_delta = 0 if key == keys.UP or key == keys.DOWN: speed_delta = 0
13. Zvuky a hudba v Pygame Zero
V navazujících kapitolách si řekneme základní informace o podpoře přehrávání zvuků a hudby v nástroji Pygame Zero. Tato funkcionalita je opět postavena přímo na možnostech poskytovaných knihovnou Pygame, i když prozatím některé možnosti nejsou přímo přístupné – například mixování zvuků atd.
14. Podpora pro přehrávání zvuků
Nástroj Pygame Zero podporuje přehrávání zvuků, kterých může v jeden okamžik znít větší množství. Mixáž je v tomto případě prováděna buď programově, nebo přímo na zvukové kartě. Zvuky uložené typicky ve formátu WAV (typicky nekomprimovaný), MP3 nebo Ogg/Vorbis jsou automaticky vyhledány v podadresáři sounds, odkud jsou načteny a jsou z nich vytvořeny příslušné objekty nabízené jako atributy objektu sounds (viz navazující kapitolu s demonstračním příkladem):
. ├── 00_intro.py ... ... ... ├── 50_play_sound.py ├── 51_play_music.py ├── fonts │ ├── AUTHORS │ ├── COPYING │ ├── CREDITS │ ├── freesans.ttf │ └── README ├── images │ ├── LICENSE │ ├── particle.png │ ├── plasma.png │ ├── spaceship.png │ └── sprite1.png ├── music │ └── test.ogg └── sounds ├── login.wav └── README.md
15. Demonstrační příklad: přehrání zvuku po stisku klávesy
V dnešním předposledním demonstračním příkladu je ukázáno, jakým způsobem je možné zajistit přehrání zvuku po stisku klávesy. Konkrétně se jedná o klávesu Space, po jejímž stisku se přehraje obsah souboru „login.wav“ uloženého v podadresáři „sounds“ (viz též předchozí kapitolu se stručným popisem práce se zvuky):
WIDTH = 480 HEIGHT = 480 BACKGROUND_COLOR = (0, 0x80, 0x80) def draw(): screen.fill(BACKGROUND_COLOR) def on_key_down(key, mod, unicode): if key == keys.ESCAPE: exit() if key == keys.SPACE: sounds.login.play()
16. Podpora pro přehrávání hudby
Na hudbu klademe poněkud odlišné nároky, než na zvuky. Většinou totiž není vyžadováno, aby bylo spuštění hudby přísně navázáno na nějaký časový okamžik (pokud se nejedná o intro atd.), ovšem naproti tomu bývají hudební soubory rozsáhlejší a je nutné je přehrávat bez zasekávání. Z tohoto důvodu může být výhodné při přehrávání používat delší buffery, které sice (obecně) zdrží začátek přehrávání, ovšem do značné míry zamezí zasekávání přehrávání. Hudební soubory jsou typicky uloženy ve formátech Ogg/Vorbis a MP3; tyto soubory jsou automaticky hledány v podadresáři „music“:
. ├── 00_intro.py ... ... ... ├── 50_play_sound.py ├── 51_play_music.py ├── fonts │ ├── AUTHORS │ ├── COPYING │ ├── CREDITS │ ├── freesans.ttf │ └── README ├── images │ ├── LICENSE │ ├── particle.png │ ├── plasma.png │ ├── spaceship.png │ └── sprite1.png ├── music │ └── test.ogg └── sounds ├── login.wav └── README.md
17. Demonstrační příklad: přehrání hudby inicializované po spuštění
Konečně se dostáváme k dnešnímu poslednímu demonstračnímu příkladu. Po spuštění tohoto příkladu se načte a prakticky ihned se začne přehrávat obsah zvukového/hudebního souboru nazvaného „test.ogg“, jenž je uložen v podadresáři „music“. Kdykoli poté je možné spustit zvuk „login“, a to naprosto stejným způsobem, jako tomu bylo v předchozím demonstračním příkladu, jenž byl popsán v patnácté kapitole. Jak zvuk tak i hudba jsou v tomto případě korektně zmixovány (prozatím však nedokážeme vyvážit hlasitosti ani jinak ovlivnit mixáž):
WIDTH = 480 HEIGHT = 480 BACKGROUND_COLOR = (0, 0x80, 0x80) def draw(): screen.fill(BACKGROUND_COLOR) def on_key_down(key, mod, unicode): if key == keys.ESCAPE: exit() if key == keys.SPACE: sounds.login.play() music.play("test")
18. Relevantní funkce z knihovny Pygame
V této kapitole jsou uvedeny odkazy na dokumentaci k funkcím knihovny Pygame, které jsou interně používány i projektem Pygame Zero. Tyto funkce jsou vidět v některých chybových hlášeních, protože i přes snahy autorů Pygame Zero není odstínění od knihovny Pygame úplné (což je možná jeden z největších současných nedostatků tohoto projektu):
- pygame.init()
http://www.pygame.org/docs/ref/pygame.html#pygame.init - pygame.quit()
http://www.pygame.org/docs/ref/pygame.html#pygame.quit - pygame.display.set_mode()
http://www.pygame.org/docs/ref/display.html#pygame.display.set_mode - pygame.display.set_caption()
http://www.pygame.org/docs/ref/display.html#pygame.display.set_caption - pygame.display.update()
http://www.pygame.org/docs/ref/display.html#pygame.display.update - pygame.event.get()
http://www.pygame.org/docs/ref/event.html#pygame.event.get - pygame.time.wait()
http://www.pygame.org/docs/ref/time.html#pygame.time.wait - pygame.time.Clock.tick()
http://www.pygame.org/docs/ref/time.html#pygame.time.Clock.tick - pygame.draw.line()
http://www.pygame.org/docs/ref/draw.html#pygame.draw.line - pygame.draw.circle()
http://www.pygame.org/docs/ref/draw.html#pygame.draw.circle - pygame.draw.rect()
http://www.pygame.org/docs/ref/draw.html#pygame.draw.rect - pygame.draw.ellipse()
http://www.pygame.org/docs/ref/draw.html#pygame.draw.ellipse - pygame.key.get_pressed
https://www.pygame.org/docs/ref/key.html#pygame.key.get_pressed - pygame.key.get_mods
https://www.pygame.org/docs/ref/key.html#pygame.key.get_mods - pygame.mouse.get_pos
https://www.pygame.org/docs/ref/mouse.html#pygame.mouse.get_pos - pygame.mouse.get_rel
https://www.pygame.org/docs/ref/mouse.html#pygame.mouse.get_rel - pygame.mouse.get_pressed
https://www.pygame.org/docs/ref/mouse.html#pygame.mouse.get_pressed - pygame.mixer
https://www.pygame.org/docs/ref/mixer.html - pygame.mixer.music
https://www.pygame.org/docs/ref/music.html - pygame.mixer.Sound
https://www.pygame.org/docs/ref/mixer.html#pygame.mixer.Sound - pygame.mixer.Channel
https://www.pygame.org/docs/ref/mixer.html#pygame.mixer.Channel
19. Repositář s demonstračními příklady
Zdrojové kódy všech popsaných demonstračních příkladů určených pro Python 3 a současně i pro nástroj Pygame Zero byly uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/most-popular-python-libs. V případě, že nebudete chtít klonovat celý repositář (ten je ovšem stále velmi malý, dnes má velikost zhruba několik desítek kilobajtů), můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující čtveřici tabulek (ovšem vzhledem k tomu, že se datové soubory hledají v konkrétních podadresářích, je výhodnější provést klon celého repositáře):
Rastrové obrázky použité v demonstračních příkladech jsou uloženy na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/pygame_zero/images.
Zvuky použité v demonstračních příkladech jsou uloženy na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/pygame_zero/sounds.
A konečně hudební soubory (resp. přesněji řečeno prozatím pouze jediný soubor) jsou uloženy na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/pygame_zero/music
20. Odkazy na Internetu
- Welcome to Pygame Zero
https://pygame-zero.readthedocs.io/en/stable/index.html - Other libraries like Pygame Zero
https://pygame-zero.readthedocs.io/en/stable/other-libs.html - Principles of Pygame Zero
https://pygame-zero.readthedocs.io/en/stable/principles.html - Built-in Objects (in Pygame Zero)
https://pygame-zero.readthedocs.io/en/stable/builtins.html - Pygame
https://www.pygame.org/news - Kniha: Coding Games With Pygame Zero & Python: Student workbook
https://bookerystore.com/downloads/coding-games-with-pygame-zero-python-student-workbook/ - Projekty založené na Pygame
https://www.pygame.org/tags/all - Domovská stránka projektu LÖVE
https://love2d.org/ - PyWeek, a bi-annual game jam to write games in Python
https://pyweek.org/ - Teaching a kid to code with Pygame Zero
https://www.mattlayman.com/blog/2019/teach-kid-code-pygame-zero/ - Games with PyGame Zero
https://codewith.mu/en/tutorials/1.0/pgzero - Coding Games With Pygame Zero & Python: Student workbook (2nd edition)
https://electronstudio.github.io/pygame-zero-book/ - Historie vývoje počítačových her (116. část – vývoj her v současnosti: od assembleru k PyGame)
https://www.root.cz/clanky/historie-vyvoje-pocitacovych-her-116-cast-vyvoj-her-v-soucasnosti-od-assembleru-k-pygame/ - Lua + LÖVE: vytvořte si vlastní hru
https://www.root.cz/clanky/lua-love-vytvorte-si-vlastni-hru/ - Hrátky se systémem LÖVE
https://www.root.cz/clanky/hratky-se-systemem-love/ - Vytváříme hru v systému LÖVE
https://www.root.cz/clanky/vytvarime-hru-v-systemu-love/ - Hrátky se systémem LÖVE – částicové systémy
https://www.root.cz/clanky/hratky-se-systemem-love-casticove-systemy/ - Hrátky se systémem LÖVE – kolize a odrazy těles
https://www.root.cz/clanky/hratky-se-systemem-love-ndash-kolize-a-odrazy-teles/ - Hrátky se systémem LÖVE – kolize a odrazy těles II
https://www.root.cz/clanky/hratky-se-systemem-love-kolize-a-odrazy-teles-ii/ - Hrátky se systémem LÖVE – pružné vazby mezi tělesy
https://www.root.cz/clanky/hratky-se-systemem-love-pruzne-vazby-mezi-telesy/ - Hrátky se systémem LÖVE – dokončení
https://www.root.cz/clanky/hratky-se-systemem-love-dokonceni/ - Seriál Letní škola programovacího jazyka Logo
http://www.root.cz/serialy/letni-skola-programovaciho-jazyka-logo/ - Scratch: oficiální stránka projektu
http://scratch.mit.edu/ - Scratch: galerie projektů vytvořených ve Scratchi
http://scratch.mit.edu/galleries/browse/newest - Scratch: nápověda
file:///usr/share/scratch/Help/en/index.html - Scratch: obrazovky nápovědy
file:///usr/share/scratch/Help/en/allscreens.html - Scratch (Wikipedie CZ)
http://cs.wikipedia.org/wiki/Scratch - Scratch (programming language)
http://en.wikipedia.org/wiki/Scratch_(programming_language) - Scratch Modification
http://wiki.scratch.mit.edu/wiki/Scratch_Modification - Scratch Lowers Resistance to Programming
http://www.wired.com/gadgetlab/2009/03/scratch-lowers/ - Snap!
http://snap.berkeley.edu/ - Prostředí Snap!
http://snap.berkeley.edu/snapsource/snap.html - Alternatives to Scratch
http://wiki.scratch.mit.edu/wiki/Alternatives_to_Scratch - Snap! (programming language)
https://en.wikipedia.org/wiki/Snap!_(programming_language) - Kniha o Basicu-256
http://www.basicbook.org/files/syw2l2p_b256.pdf/ - Basic-256 home page
http://www.basic256.org/index_en - Basic-256 Language Documentation
http://doc.basic256.org/doku.php - Basic-256 Art Gallery
http://www.basic256.org/artgallery - Basic-256 Tutorial
http://www.basic256.org/tutorials - Why BASIC?
http://www.basic256.org/whybasic - A book to teach ANYBODY how to program a computer (using BASIC)
http://www.basicbook.org/ - Sprite ve Scratchi
https://en.scratch-wiki.info/wiki/Sprite - Scratch Modification
https://en.scratch-wiki.info/wiki/Scratch_Modification - 3D Programming in Python – Part 1
https://greendalecs.wordpress.com/2012/04/21/3d-programming-in-python-part-1/ - A very basic Pyglet tutorial
http://www.natan.termitnjak.net/tutorials/pyglet_basic.html - Alpha blending
https://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending - Pygame Colors
https://pygame-zero.readthedocs.io/en/latest/colors_ref.html - Python Color Constants Module
https://www.webucator.com/blog/2015/03/python-color-constants-module/ - Inbetweening
https://en.wikipedia.org/wiki/Inbetweening - MP3 (Wikipedia)
https://en.wikipedia.org/wiki/MP3 - Ogg (Wikipedia)
https://en.wikipedia.org/wiki/Ogg - Vorbis audio compression
https://xiph.org/vorbis/ - Vorbis (Wikipedia)
https://en.wikipedia.org/wiki/Vorbis - Tweens
https://helpx.adobe.com/animate/using/Tweens.html