IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

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řídy Rect . Ten jde vytvořit jako pygame.Rect(x, y, sirka, vyska). x a y udává pozici levého horního rohu.
  • polygon: pygame.draw.polygon(povrch, barva, body), kde body 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:

Náhled okna pygame v Pythonu - Pygame - Tvorba her v Pythonu

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 hry
  • pygame.MOUSEBUTTONDOWN - při stisku tlačítka myši
  • pygame.MOUSEBUTTONUP - při uvolnění tlačítka myši
  • pygame.KEYDOWN - při stisknutí nějaké klávesy
  • pygage.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:

Náhled obrazovky - Pygame - Tvorba her v Pythonu

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.


 

Předchozí článek
Pygame - Úvod & instalace
Všechny články v sekci
Pygame - Tvorba her v Pythonu
Přeskočit článek
(nedoporučujeme)
Pygame - Pong - Příprava
Článek pro vás napsal Adam Hlaváček
Avatar
Uživatelské hodnocení:
34 hlasů
vývoji užitečných aplikací zjednodušujících každodenní život
Aktivity