Lekce 2 - Pygame - Kreslení a pohyb
V minulé lekci, Pygame - Úvod & instalace, jsme se seznámili s Pygame a tento herní framework jsme si nainstalovali.
Ještě než se pustíme do vykreslování tvarů na obrazovku, ošetříme kompatibilitu různých verzí Pygame.
Zpětná kompatibilita
Abychom zachovali zpětnou kompatibilitu se všemi verzemi Pygame, budeme importovat následujícím způsobem:
try: # noinspection PyUnresolvedReferences import pygame_sdl2 pygame_sdl2.import_as_pygame() except ImportError: pass import pygame
Tento postup nám zaručí, že ať již máme nainstalovanou jakoukoliv
verzi Pygame, budeme se na ni moci odkazovat pouze pomocí
pygame
.
Otevíráme naše první okno
Když chceme pygame
používat, musíme jej nejdříve
inicializovat. K tomu slouží funkce pygame.init()
.
Jakmile máme inicializaci dokončenou, vytvoříme si okno hry. Okno se
vytváří pomocí funkce pygame.display.set_mode()
. Funkce bere
jako svůj první parametr tuple s rozměry vytvořeného okna:
pygame.init() screen = pygame.display.set_mode((800, 600)) # vytvoří okno o velikosti 800x600
Pokud chceme okno maximalizovat přes celou obrazovku, jako hodnoty
použijeme 0
. Zároveň, pokud bychom chtěli odstranit všechna
okna, jako druhý parametr použijeme pygame.FULLSCREEN
.
pygame.init() screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN) # vytvoří fullscreen okno
Upozornění: Vytváření okna ve fullscreen bez předchozí
implementace ukončení pygame
způsobí, že se objeví černé
okno přes celou obrazovku, které zachytává všechny klávesy. Dostat se z
takového okna je vysoce obtížné.
Díky tomuto máme v proměnné screen
uložený objekt typu
Surface
, což je v pygame
reprezentace jakékoliv
grafiky.
Když nás potom naše okno přestane bavit, použijeme funkci
pygame.quit()
, která ukončí celé pygame
.
Kreslíme tvary
Pokud chceme v pygame
malovat různé tvary, nabízí nám
jednoduchou možnost skrze svůj podmodul pygame.draw
.
Ukážeme si nejčastější příklady:
- kruh:
pygame.draw.circle(povrch, barva, (x, y), poloměr)
-(x, y)
udává střed - obdélník:
pygame.draw.rect(povrch, barva, obdélník)
, kde obdélník je objekt třídyRect
. Ten jde vytvořit jakopygame.Rect(x, y, sirka, vyska)
.x
ay
udává pozici levého horního rohu. - polygon:
pygame.draw.polygon(povrch, barva, body)
, kdebody
je seznam tuplů ve formátu(x, y)
- čára:
pygame.draw.line(povrch, barva, start_pozice, konec_pozice)
Barva je tuple ve formátu
(červená, modrá, zelená, [průhlednost])
, kde všechny hodnoty
jsou int
z rozsahu 0
-255
.
Všechny tyto funkce mají také volitelný parametr width
. U
čáry určuje její tloušťku, u ostatních tvarů určuje tloušťku jejich
výplně. Pokud je width
nastaven na 0
, což je
výchozí hodnota, tvar je plně vyplněn.
Povrch je libovolný objekt třídy Surface
, takže jím může
být jakýkoliv obrázek, nebo dokonce i naše okno.
Kruh
Pojďme si naše nově nabyté vlastnosti hned vyzkoušet a namalujme si modrý kruh!
pygame.init() screen = pygame.display.set_mode((800, 600)) pygame.draw.circle(screen, (0, 0, 255), (400, 300), 150)
Zkuste si tento kód spustit. Co se stane?
Herní cyklus
V předchozím příkladu se nám okno otevřelo pouze na chvíli a hned se zavřelo. A ani ten kruh nebyl vidět. Co se stalo špatně?
Odpověď je jednoduchá: Interpreter došel na konec kódu a ukončil tedy jeho provádění. Zároveň jsme neviděli ani na chvíli náš kruh, protože jsme nepoužili funkci na vykreslení nového obsahu do okna. Pojďme to tedy hned napravit.
Definujme si nový pojem - krok. Krok je všechen kód,
který chceme, aby se periodicky prováděl uvnitř naší hry. V našem
dosavadním kódu máme pouze jeden krok, po kterém se provádění celé
naší hry ukončí. Tomu zamezíme tím, že všechen kód, který se má
provádět, uzavřeme do smyčky while True
. Na konec smyčky pak
ještě nesmíme zapomenout přidat pygame.display.flip()
, což
způsobí aktualizaci obrazovky:
pygame.init() while True: screen = pygame.display.set_mode((800, 600)) pygame.draw.circle(screen, (0, 0, 255), (400, 300), 150) pygame.display.flip()
Výsledek:

Používáme události
Možná jste si všimli, že okno pygame
nešlo zavřít jinak,
než za použití Ctrl + C v terminálu. To je způsobeno
tím, že nijak neodchytáváme událost žádosti o zavření okna. Takové
chování není příliš intuitivní a tak by byl dobrý nápad jej
napravit.
Seznam všech událostí získáme pomocí zavolání funkce
pygame.event.get()
, přeš jejíž výsledek můžeme iterovat a
porovnávat jej. Mezi často používané typy událostí patří:
pygame.QUIT
- je zavolána, když se někdo pokusí ukončit okno hrypygame.MOUSEBUTTONDOWN
- při stisku tlačítka myšipygame.MOUSEBUTTONUP
- při uvolnění tlačítka myšipygame.KEYDOWN
- při stisknutí nějaké klávesypygage.KEYUP
- při uvolnění nějaké klávesy
Malá poznámka: V Pygame_SDL2 se může stát, že vám nebude
fungovat rozpoznávání stisků kláves šipek. Proto se ve všech kurzech
šipkám raději vyvarujeme a budeme místo nich používat známé
WASD. Zároveň jsem také narazil
na problém s pygame.KEYDOWN
ve verzi 1.9.6., takže místo něj
budeme prozatím raději používat pygame.KEYUP
.
Řekněme, že budeme chtít ukončit hru ve dvou případech: Je o
ukončení požádáno nebo byla stisknuta klávesa Esc. Jelikož
nám nyní kód běží uvnitř cyklu while True
, nedá se z něj
tak snadno vyskočit. Proto si definujme raději proměnnou
running = True
a budeme kontrolovat její stav. Pak nám jen
stačí přidat krátký for
cyklus, ve kterém otestujeme, jestli
náhodou není některá z aktivních událostí ta, která nás zajímá.
Po těchto úpravách bude náš kód vypadat zhruba následovně:
pygame.init() running = True while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False elif event.type == pygame.KEYUP and event.key == pygame.K_ESCAPE: running = False screen = pygame.display.set_mode((800, 600)) pygame.draw.circle(screen, (0, 0, 255), (400, 300), 150) pygame.display.flip()
Nyní by již naše okno mělo jít zavřít jak pomocí křížku, tak pomocí stisknutí (resp. uvolnění) klávesy Esc.
Kreslíme obdélník
Obdélník, resp. pygame.Rect
, je vysoce užitečná třída
uvnitř pygame
, jelikož je možné s její pomocí vyřešit
například kolize. Abychom si to v rychlosti ukázali, nakreslíme si ke kruhu
ještě i zelený obdélník:
pygame.init() running = True while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False elif event.type == pygame.KEYUP and event.key == pygame.K_ESCAPE: running = False screen = pygame.display.set_mode((800, 600)) pygame.draw.circle(screen, (0, 0, 255), (400, 300), 150) pygame.draw.rect(screen, (0, 255, 0), pygame.Rect(10, 10, 100, 200)) pygame.display.flip()
Výsledek:

Pohybujeme se
Závěrem této lekce se naučíme, jak se sebou můžeme hýbat pomocí
stisku kláves. Mohli bychom si zaznamenávat pomocí událostí, které
klávesy uživatel stiskl a které poté pustil. Naštěstí má ale
pygame
přímo již zabudovanou funkci
pygame.key.get_pressed()
, která vrátí n-tici všech právě
stisknutých kláves. Indexy v této n-tici odpovídají hodnotám konstant
pygame.K_
.
Řekněme si tedy, že chceme hýbat naším kruhem pomocí kláves WASD. K tomu si potřebujeme ukládat pozici kruhu, ke které budeme přidávat hodnoty podle toho, které klávesy jsou zrovna stisknuté:
player_x = 400 player_y = 300 while running: # ... pressed = pygame.key.get_pressed() if pressed[pygame.K_w]: # Nahoru player_y -= 5 if pressed[pygame.K_s]: # Dolů player_y += 5 if pressed[pygame.K_a]: # Doleva player_x -= 5 if pressed[pygame.K_d]: # Doprava player_x += 5
Nyní by nám měla fungovat změna souřadnic, jak si přejeme. Ještě ale musíme upravit vykreslování kruhu:
while running: # ... pygame.draw.circle(screen, (0, 0, 255), (player_x, player_y), 150)
Teď máme kruh, který můžeme ovládat pomocí kláves. Zkuste si kód spustit a chvíli si s ním hrát. Možná se vám zdá, že modrý kruh běhá trochu rychleji, než jste si představovali. A ano, máte pravdu. To je proto, že jsme nedali žádné omezení maximální rychlosti programu, takže se všechny kroky provádějí tak rychle, jak jen mohou. To není naprosto žádoucí chování, jelikož by pak každému uživateli běhala hra jinak rychle a také by velice záleželo na vytížení procesoru.
Proto má pygame
snadnou cestu, jak omezit maximální rychlost
programu na určitý počet FPS (= frames per second = snímků za
sekundu). Běžná hodnota FPS bývá 30, 60 nebo 120, což jsou všechno
hodnoty, při kterých si již lidské oko nevšimne žádného trhání.
Pro omezení maximálního FPS na 30 tedy použijeme instanci třídy
pygame.time.Clock
:
clock = pygame.time.Clock() # vytvoříme nové pygame hodiny while True: # ... clock.tick(30) # umístíme na konec cyklu
Máme hotovo!
Výborná práce, nyní máme hotové všechny základy, které potřebujeme na vytvoření naší první hry. Tu si společně vytvoříme příště.
Ještě pro zopakování si ale ukážeme kompletní kód (bez importu), který bychom měli mít po konci této lekce:
pygame.init() running = True clock = pygame.time.Clock() player_x = 400 player_y = 300 while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False elif event.type == pygame.KEYUP and event.key == pygame.K_ESCAPE: running = False pressed = pygame.key.get_pressed() if pressed[pygame.K_w]: # Nahoru player_y -= 5 if pressed[pygame.K_s]: # Dolů player_y += 5 if pressed[pygame.K_a]: # Doleva player_x -= 5 if pressed[pygame.K_d]: # Doprava player_x += 5 screen = pygame.display.set_mode((800, 600)) pygame.draw.circle(screen, (0, 0, 255), (player_x, player_y), 150) pygame.draw.rect(screen, (0, 255, 0), pygame.Rect(10, 10, 100, 200)) pygame.display.flip() clock.tick(30)
V příští lekci, Pygame - Pong - Příprava, si začneme vytvářet naši první hru Pong. Budeme pracovat s textem a povíme si něco o kolizi.