V dnešní části seriálu o programovacích jazycích i o knihovnách a frameworcích vhodných pro výuku programování popř. pro výuku základů počítačové grafiky, si ukážeme, jakým způsobem se v knihovně Pygame může detekovat kolize dvou či více spritů. To je velmi důležitá operace, s níž se setkáme v prakticky každé 2D hře.

Obsah

1. Programovací jazyky a knihovny určené pro výuku základů počítačové grafiky: detekce kolize spritů

2. Použití funkce pygame.sprite.spritecollide pro detekci kolize spritů

3. Zdrojový kód demonstračního příkladu pygame20: základní detekce kolize spritů

4. Vylepšení předchozího kódu: detekce hráče se sebou samým je ignorována

5. Zdrojový kód demonstračního příkladu pygame21

6. Zvýraznění spritu, s nímž došlo ke kolizi

7. Zdrojový kód demonstračního příkladu pygame22: vysvícení spritů

8. Kolize se sprity, které nemají čtvercový či obdélníkový tvar

9. Zdrojový kód demonstračního příkladu pygame23: špatně vypočtená kolize kruhových spritů

10. Zdrojový kód demonstračního příkladu pygame24: použití korektní detekce kolize

11. Obsah následující části seriálu

12. Repositář se zdrojovými kódy všech dnešních demonstračních příkladů

13. Funkce použité v dnešních demonstračních příkladech

14. Odkazy na Internetu

1. Programovací jazyky a knihovny určené pro výuku základů počítačové grafiky: detekce kolize spritů

V předchozí části seriálu o systémech, knihovnách a jazycích vhodných pro výuku programování počítačové grafiky, jsme se seznámili s tím, jakým způsobem lze v knihovně Pygame pracovat s takzvanými sprity, tj. s rastrovými obrázky, s nimiž je možné pohybovat po celé dvourozměrné scéně. Sprity samozřejmě mohou úplně či částečně překreslit pozadí vytvářené scény, mohou se v případě potřeby navzájem překrývat, při překrytí lze aplikovat již dříve popsanou průhlednost apod.

01

Obrázek 1: Sprity použité ve známé herní sérii Sonic the Hedgehog.

Ovšem zbývá nám vyřešit ještě jeden relativně závažný problém úzce související se sprity – v mnoha aplikacích (především v počítačových hrách) je nutné nějakým způsobem zareagovat ve chvíli, kdy dojde ke kolizi či k překryvu dvou spritů. Představme si například hru Pac-Man, která musí adekvátně reagovat při srážce hlavního hrdiny s duchem, zareagovat na sežrání bodu (což je taktéž srážka spritu/spritů) apod. I tyto případy, které byly v minulosti řešeny už na úrovni příslušného hardware (video čipu), jsou samozřejmě v knihovně Pygame relativně jednoduše vyřešitelné, což ostatně uvidíme v navazujících kapitolách.

02

Obrázek 2: Starší varianta hry Pac-Man, zde pro osmibitovou herní konzoli Atari 2600.

2. Použití funkce pygame.sprite.spritecollide pro detekci kolize spritů

Připomeňme si, že každý sprite je v knihovně Pygame charakterizován svým rastrovým obrázkem, dále pak rozměry (šířka×výška) a taktéž pozicí v dvourozměrné scéně. Obě poslední hodnoty, tj. jak rozměry, tak i pozice spritu ve scéně, jsou uloženy v jediné datové struktuře typu rect. Test, zda došlo ke kolizi dvou spritů, lze tedy v nejjednodušším případě realizovat testem na protnutí dvou obdélníků charakterizovaných již zmíněnou strukturou rect. Aby nebylo nutné kód pro detekci kolizí spritů psát neustále znovu, lze využít funkci pygame.sprite.spritecollide(). Tato funkce akceptuje tři nebo čtyři parametry (čtvrtý parametr je totiž nepovinný):

  1. sprite – sprite, pro nějž se zjišťuje potenciální kolize s ostatními sprity.
  2. group – skupina spritů, s nimiž může první sprite kolidovat (kolize mezi jednotlivými sprity ve skupině se však nezjišťuje).
  3. dokill – pokud je tento parametr nastavený na pravdivostní hodnotu True, je kolidující sprite ze skupiny odstraněn. V opačném případě nedojde k žádné změně v předané skupině.
  4. collided – v případě, že je tento parametr specifikován, musí se jednat o funkci, které se předají dva sprity a funkce vrátí hodnotu True, pokud dojde k jejich kolizi.

Funkce pygame.sprite.spritecollide() vrátí seznam obsahující ty sprity ze skupiny group, které kolidují s prvním spritem předaným v parametru sprite. Sprity samozřejmě nejsou do tohoto seznamu kopírovány, ale je použita jen reference na ně (což nám umožňuje provádět operace nad původními objekty).

03

Obrázek 3: Povšimněte si počitadla kolizí zobrazeného v titulku okna tohoto demonstračního příkladu (jednička zde znamená, že hráč koliduje sám se sebou, což je jeden z nedostatků dále popsaného příkladu).

3. Zdrojový kód demonstračního příkladu pygame20: základní detekce kolize spritů

V dnešním prvním demonstračním příkladu (v celkovém pořadí se však jedná už o příklad dvacátý) je ukázáno použití výše zmíněné funkce pygame.sprite.spritecollide(). Celý program je založen na kódu, s nímž jsme se již seznámili minule, ovšem došlo k přidání několika nepohyblivých spritů a taktéž funkce check_collisions(). V této periodicky volané funkci se zjistí, zda došlo ke kolizi hráče (resp. přesněji řečeno spritu reprezentujícího hráče) s některým dalším spritem. Následně se do titulkového pruhu okna vypíše celkový počet kolizí, který se velmi snadno vypočítá z délky seznamu, který funkce pygame.sprite.spritecollide() vrátí. Počet kolizí bude o jedničku vyšší, a to z toho důvodu, že samotný hráč je taktéž přidán do skupiny sprite_group (a každý sprite samozřejmě koliduje sám se sebou). Podívejme se nyní na výpis celého zdrojového kódu tohoto demonstračního příkladu:

#!/usr/bin/python
# vim: set fileencoding=utf-8

# Demonstrační příklady využívající knihovnu Pygame

# Příklad číslo 20: použití spritů, pohyblivý sprite


import pygame, sys, os, math

# Nutno importovat kvůli konstantám QUIT atd.
from pygame.locals import *

# Velikost okna aplikace
WIDTH = 320
HEIGHT = 240



# Třída představující sprite zobrazený jako jednobarevný čtverec.
class BlockySprite(pygame.sprite.Sprite):
    # Konstruktor
    def __init__(self, color, size, x, y):
        # Nejprve je nutné zavolat konstruktor předka,
        # tj. konstruktor třídy pygame.sprite.Sprite:
        pygame.sprite.Sprite.__init__(self)

        # Vytvoření obrázku představujícího vizuální obraz spritu:
        self.image = pygame.Surface([size,size])
        self.image.fill(color)

        # Vytvoření obalového obdélníku
        # (velikost se získá z rozměru obrázku)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y

        # Počáteční rychlost spritu
        self.speed_x = 0
        self.speed_y = 0



# Inicializace knihovny Pygame
pygame.init()

clock = pygame.time.Clock()

# Vytvoření okna pro vykreslování
display = pygame.display.set_mode([WIDTH, HEIGHT])

# Nastavení titulku okna
pygame.display.set_caption('Pygame test #20')

# Konstanty s n-ticemi představujícími základní barvy
BLACK   = (  0,   0,   0)
RED     = (255,   0,   0)
GRAY    = (128, 128, 128)

# Objekt sdružující všechny sprity
all_sprites = pygame.sprite.Group()

# Vytvoření několika typů spritů
#                     barva  x   y velikost
wall1  = BlockySprite(GRAY, 50, 10, 10)
wall2  = BlockySprite(GRAY, 15, 100, 100)
wall3  = BlockySprite(GRAY, 15, 100, 150)
wall4  = BlockySprite(GRAY, 15, 200, 100)
wall5  = BlockySprite(GRAY, 15, 200, 150)
player = BlockySprite(RED,  25, WIDTH/2-12, HEIGHT/2-12)

# Přidání několika dalších spritů do seznamu
# (jen jeden sprite - ten poslední - bude ve skutečnosti pohyblivý)
all_sprites.add(wall1)
all_sprites.add(wall2)
all_sprites.add(wall3)
all_sprites.add(wall4)
all_sprites.add(wall5)
all_sprites.add(player)



# Posun všech spritů ve skupině na základě jejich rychlosti
def move_sprites(sprite_group, playground_width, playground_height):
    for sprite in sprite_group:
        # Posun spritu
        sprite.rect.x = sprite.rect.x + sprite.speed_x
        sprite.rect.y = sprite.rect.y + sprite.speed_y
        # Kontrola, zda sprite nenarazil do okrajů okna
        if sprite.rect.x </gc 0:
            sprite.rect.x = 0
            sprite.speed_x = 0
        if sprite.rect.x + sprite.rect.width >/gc playground_width:
            sprite.rect.x = playground_width - sprite.rect.width
            sprite.speed_x = 0
        if sprite.rect.y </gc 0:
            sprite.rect.y = 0
            sprite.speed_y = 0
        if sprite.rect.y + sprite.rect.height >/gc playground_height:
            sprite.rect.y = playground_height - sprite.rect.height
            sprite.speed_y = 0



# Vykreslení celé scény na obrazovku
def draw_scene(display, background_color, sprite_group):
    # Vyplnění plochy okna černou barvou
    display.fill(background_color)
    # Vykreslení celé skupiny spritů do bufferu
    sprite_group.draw(display)
    # Obnovení obsahu obrazovky (překlopení zadního a předního bufferu)
    pygame.display.update()



# Zjistí kolize spritu se "stěnami" (nepohyblivými sprity)
def check_collisions(player, sprite_group):
    # Vytvoření seznamu spritů, které kolidují s hráčem
    hit_list = pygame.sprite.spritecollide(player, sprite_group, False)
    collisions = len(hit_list)
    # Přenastavení titulku okna
    pygame.display.set_caption('Pygame test #20: collisions ' + str(collisions))



# Hlavní herní smyčka
while True:
    # Načtení a zpracování všech událostí z fronty
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
        if event.type == KEYDOWN:
            if event.key == K_ESCAPE:
                pygame.quit()
                sys.exit()
            # Stiskem kurzorových kláves je možné měnit směr pohybu spritu
            elif event.key == pygame.K_LEFT:
                player.speed_x = -3
            elif event.key == pygame.K_RIGHT:
                player.speed_x = +3
            elif event.key == pygame.K_UP:
                player.speed_y = -3
            elif event.key == pygame.K_DOWN:
                player.speed_y = +3
        if event.type == KEYUP:
            # Puštění kurzorových kláves vede k zastavení pohybu spritu
            if event.key == pygame.K_LEFT:
                player.speed_x = 0
            elif event.key == pygame.K_RIGHT:
                player.speed_x = 0
            elif event.key == pygame.K_UP:
                player.speed_y = 0
            elif event.key == pygame.K_DOWN:
                player.speed_y = 0

    move_sprites(all_sprites, display.get_width(), display.get_height())
    check_collisions(player, all_sprites)
    draw_scene(display, BLACK, all_sprites)
    clock.tick(20)

# finito

04

Obrázek 4: Kolize hráče s jiným (nepohyblivým) spritem. Počitadlo zde dosáhlo hodnoty 2.

4. Vylepšení předchozího kódu: detekce hráče se sebou samým je ignorována

Předchozí příklad trpěl jednou nectností při výpočtu kolizí, která by v praxi komplikovala způsob dalšího použití seznamu spritů, jež kolidují s hráčem. Vzhledem k tomu, že sprite představující samotného hráče je součástí seznamu všech spritů, je vždy detekována minimálně jedna kolize, a to konkrétně kolize hráče se sebou samým. Aby se tomuto nedostatku předešlo, je možné vytvořit dva seznamy spritů, přičemž první seznam bude obsahovat všechny sprity a druhý seznam všechny sprity kromě samotného hráče. První seznam bude používán pro vykreslování, druhý pak pouze pro detekci kolizí. Podívejme se nyní, jak tuto změnu realizovat.

Nejprve vytvoříme všechny sprity. Ve skutečnosti ani nepotřebujeme reference na sprity ukládat do samostatných proměnných, je to však přehlednější:

# Vytvoření několika typů spritů
#                     barva  x   y velikost
wall1  = BlockySprite(GRAY, 50, 10, 10)
wall2  = BlockySprite(GRAY, 15, 100, 100)
wall3  = BlockySprite(GRAY, 15, 100, 150)
wall4  = BlockySprite(GRAY, 15, 200, 100)
wall5  = BlockySprite(GRAY, 15, 200, 150)
player = BlockySprite(RED,  25, WIDTH/2-12, HEIGHT/2-12)

Dále vytvoříme seznam všech spritů, tj. jak nepohyblivých spritů, tak i samotného hráče:

# Objekt sdružující všechny sprity
all_sprites = pygame.sprite.Group()

# Přidání několika dalších spritů do seznamu
# (jen jeden sprite - ten poslední - bude ve skutečnosti pohyblivý)
all_sprites.add(wall1)
all_sprites.add(wall2)
all_sprites.add(wall3)
all_sprites.add(wall4)
all_sprites.add(wall5)
all_sprites.add(player)

Dále vytvoříme seznam všech spritů, ovšem bez hráče:

# Objekt sdružující všechny sprity kromě hráče
all_sprites_but_player = pygame.sprite.Group()

# Seznam všech nepohyblivých spritů
all_sprites_but_player.add(wall1)
all_sprites_but_player.add(wall2)
all_sprites_but_player.add(wall3)
all_sprites_but_player.add(wall4)
all_sprites_but_player.add(wall5)

Kolize se budou testovat mezi hráčem (player) a druhým seznamem (all_sprites_but_player):

check_collisions(player, all_sprites_but_player)

05

Obrázek 5: Úprava programového kódu: prozatím nedošlo ke kolizi hráče s jiným (nepohyblivým) spritem, počitadlo tedy správně ukazuje hodnotu 0.

5. Zdrojový kód demonstračního příkladu pygame21

Úprava předchozího programového kódu způsobem naznačeným v předchozí kapitole vedla ke vzniku nového příkladu, jehož úplný zdrojový kód je vypsán pod tímto odstavcem. Povšimněte si zejména způsobu manipulace s datovými strukturami uloženými v proměnných all_sprites a all_sprites_but_player:

#!/usr/bin/python
# vim: set fileencoding=utf-8

# Demonstrační příklady využívající knihovnu Pygame

# Příklad číslo 21: použití spritů, pohyblivý sprite


import pygame, sys, os, math

# Nutno importovat kvůli konstantám QUIT atd.
from pygame.locals import *

# Velikost okna aplikace
WIDTH = 320
HEIGHT = 240



# Třída představující sprite zobrazený jako jednobarevný čtverec.
class BlockySprite(pygame.sprite.Sprite):
    # Konstruktor
    def __init__(self, color, size, x, y):
        # Nejprve je nutné zavolat konstruktor předka,
        # tj. konstruktor třídy pygame.sprite.Sprite:
        pygame.sprite.Sprite.__init__(self)

        # Vytvoření obrázku představujícího vizuální obraz spritu:
        self.image = pygame.Surface([size,size])
        self.image.fill(color)

        # Vytvoření obalového obdélníku
        # (velikost se získá z rozměru obrázku)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y

        # Počáteční rychlost spritu
        self.speed_x = 0
        self.speed_y = 0



# Inicializace knihovny Pygame
pygame.init()

clock = pygame.time.Clock()

# Vytvoření okna pro vykreslování
display = pygame.display.set_mode([WIDTH, HEIGHT])

# Nastavení titulku okna
pygame.display.set_caption('Pygame test #21')

# Konstanty s n-ticemi představujícími základní barvy
BLACK   = (  0,   0,   0)
RED     = (255,   0,   0)
GRAY    = (128, 128, 128)

# Objekt sdružující všechny sprity
all_sprites = pygame.sprite.Group()
# Objekt sdružující všechny sprity kromě hráče
all_sprites_but_player = pygame.sprite.Group()

# Vytvoření několika typů spritů
#                     barva  x   y velikost
wall1  = BlockySprite(GRAY, 50, 10, 10)
wall2  = BlockySprite(GRAY, 15, 100, 100)
wall3  = BlockySprite(GRAY, 15, 100, 150)
wall4  = BlockySprite(GRAY, 15, 200, 100)
wall5  = BlockySprite(GRAY, 15, 200, 150)
player = BlockySprite(RED,  25, WIDTH/2-12, HEIGHT/2-12)

# Přidání několika dalších spritů do seznamu
# (jen jeden sprite - ten poslední - bude ve skutečnosti pohyblivý)
all_sprites.add(wall1)
all_sprites.add(wall2)
all_sprites.add(wall3)
all_sprites.add(wall4)
all_sprites.add(wall5)
all_sprites.add(player)

# Seznam všech nepohyblivých spritů
all_sprites_but_player.add(wall1)
all_sprites_but_player.add(wall2)
all_sprites_but_player.add(wall3)
all_sprites_but_player.add(wall4)
all_sprites_but_player.add(wall5)



# Posun všech spritů ve skupině na základě jejich rychlosti
def move_sprites(sprite_group, playground_width, playground_height):
    for sprite in sprite_group:
        # Posun spritu
        sprite.rect.x = sprite.rect.x + sprite.speed_x
        sprite.rect.y = sprite.rect.y + sprite.speed_y
        # Kontrola, zda sprite nenarazil do okrajů okna
        if sprite.rect.x </gc 0:
            sprite.rect.x = 0
            sprite.speed_x = 0
        if sprite.rect.x + sprite.rect.width >/gc playground_width:
            sprite.rect.x = playground_width - sprite.rect.width
            sprite.speed_x = 0
        if sprite.rect.y </gc 0:
            sprite.rect.y = 0
            sprite.speed_y = 0
        if sprite.rect.y + sprite.rect.height >/gc playground_height:
            sprite.rect.y = playground_height - sprite.rect.height
            sprite.speed_y = 0



# Vykreslení celé scény na obrazovku
def draw_scene(display, background_color, sprite_group):
    # Vyplnění plochy okna černou barvou
    display.fill(background_color)
    # Vykreslení celé skupiny spritů do bufferu
    sprite_group.draw(display)
    # Obnovení obsahu obrazovky (překlopení zadního a předního bufferu)
    pygame.display.update()



# Zjistí kolize spritu se "stěnami" (nepohyblivými sprity)
def check_collisions(player, sprite_group):
    # Vytvoření seznamu spritů, které kolidují s hráčem
    hit_list = pygame.sprite.spritecollide(player, sprite_group, False)
    collisions = len(hit_list)
    # Přenastavení titulku okna
    pygame.display.set_caption('Pygame test #21: collisions ' + str(collisions))



# Hlavní herní smyčka
while True:
    # Načtení a zpracování všech událostí z fronty
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
        if event.type == KEYDOWN:
            if event.key == K_ESCAPE:
                pygame.quit()
                sys.exit()
            # Stiskem kurzorových kláves je možné měnit směr pohybu spritu
            elif event.key == pygame.K_LEFT:
                player.speed_x = -3
            elif event.key == pygame.K_RIGHT:
                player.speed_x = +3
            elif event.key == pygame.K_UP:
                player.speed_y = -3
            elif event.key == pygame.K_DOWN:
                player.speed_y = +3
        if event.type == KEYUP:
            # Puštění kurzorových kláves vede k zastavení pohybu spritu
            if event.key == pygame.K_LEFT:
                player.speed_x = 0
            elif event.key == pygame.K_RIGHT:
                player.speed_x = 0
            elif event.key == pygame.K_UP:
                player.speed_y = 0
            elif event.key == pygame.K_DOWN:
                player.speed_y = 0

    move_sprites(all_sprites, display.get_width(), display.get_height())
    check_collisions(player, all_sprites_but_player)
    draw_scene(display, BLACK, all_sprites)
    clock.tick(20)

# finito

06

Obrázek 6: Kolize hráče s jiným (nepohyblivým) spritem, počitadlo správně ukazuje hodnotu 1.

6. Zvýraznění spritu, s nímž došlo ke kolizi

Výše uvedený příklad nyní upravíme takovým způsobem, aby se sprite, s níž hráč koliduje, vybarvil, a tím pádem i ve vykreslované 2D scéně zvýraznil. Výchozí barva spritů bude nastavena na světle šedou, barva spritu, do něhož narazí hráč, pak na barvu žlutou. Úprava programového kódu je ve skutečnosti velmi jednoduchá. Nejprve je nutné doplnit třídu BlockySprite o dvě nové metody pojmenované yellowColor() a grayColor(). Tyto metody po svém zavolání jednoduše vybarví celou plochu spritu zadanou barvou. Nejedná se sice o nejrychlejší řešení, ale prozatím je dostatečně rychlé i pro spuštění příkladu na (relativně) pomalém Raspberry Pi:

# Třída představující sprite zobrazený jako jednobarevný čtverec.
class BlockySprite(pygame.sprite.Sprite):
    ...
    ...
    ...

    # Nastavení barvy spritu, který kolidoval s hráčem
    def yellowColor(self):
        self.image.fill(YELLOW)

    # Nastavení barvy spritu, který nekolidoval s hráčem
    def grayColor(self):
        self.image.fill(GRAY)

O změnu barvy těch spritů, které kolidují s hráčem, se postará uživatelská funkce nazvaná change_colors(). Této funkci se předá seznam všech spritů a taktéž seznam spritů, s nimiž došlo ke kolizi. Na základě obsahu těchto dvou seznamů se každý sprite vybarví šedou nebo žlutou barvou:

# Změna barvy spritu na základě kolize s hráčem
def change_colors(sprite_group, hit_list):
    # Projít všemi sprity ze skupiny, kterou detekovala kolizní funkce
    for sprite in sprite_group:
        if sprite in hit_list:
            sprite.yellowColor()
        else:
            sprite.grayColor()

07

Obrázek 7: Prozatím nenastala kolize hráče s jiným (nepohyblivým) spritem.

08

Obrázek 8: Kolize hráče s jiným (nepohyblivým) spritem.

09

Obrázek 9: Kolize hráče s více (konkrétně se dvěma) sprity.

7. Zdrojový kód demonstračního příkladu pygame22: vysvícení spritů

Princip popsaný v šesté kapitole nyní budeme aplikovat v uceleném programovém kódu, který je kompletně vypsán pod tímto odstavcem:

#!/usr/bin/python
# vim: set fileencoding=utf-8

# Demonstrační příklady využívající knihovnu Pygame

# Příklad číslo 22: použití spritů, pohyblivý sprite


import pygame, sys, os, math

# Nutno importovat kvůli konstantám QUIT atd.
from pygame.locals import *

# Velikost okna aplikace
WIDTH = 320
HEIGHT = 240



# Třída představující sprite zobrazený jako jednobarevný čtverec.
class BlockySprite(pygame.sprite.Sprite):
    # Konstruktor
    def __init__(self, color, size, x, y):
        # Nejprve je nutné zavolat konstruktor předka,
        # tj. konstruktor třídy pygame.sprite.Sprite:
        pygame.sprite.Sprite.__init__(self)

        # Vytvoření obrázku představujícího vizuální obraz spritu:
        self.image = pygame.Surface([size,size])
        self.image.fill(color)

        # Vytvoření obalového obdélníku
        # (velikost se získá z rozměru obrázku)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y

        # Počáteční rychlost spritu
        self.speed_x = 0
        self.speed_y = 0

    # Nastavení barvy spritu, který kolidoval s hráčem
    def yellowColor(self):
        self.image.fill(YELLOW)

    # Nastavení barvy spritu, který nekolidoval s hráčem
    def grayColor(self):
        self.image.fill(GRAY)



# Inicializace knihovny Pygame
pygame.init()

clock = pygame.time.Clock()

# Vytvoření okna pro vykreslování
display = pygame.display.set_mode([WIDTH, HEIGHT])

# Nastavení titulku okna
pygame.display.set_caption('Pygame test #22')

# Konstanty s n-ticemi představujícími základní barvy
BLACK   = (  0,   0,   0)
RED     = (255,   0,   0)
GRAY    = (128, 128, 128)
YELLOW  = (255, 255,   0)

# Objekt sdružující všechny sprity
all_sprites = pygame.sprite.Group()
# Objekt sdružující všechny sprity kromě hráče
all_sprites_but_player = pygame.sprite.Group()

# Vytvoření několika typů spritů
#                     barva  x   y velikost
wall1  = BlockySprite(GRAY, 50, 10, 10)
wall2  = BlockySprite(GRAY, 15, 100, 100)
wall3  = BlockySprite(GRAY, 15, 100, 150)
wall4  = BlockySprite(GRAY, 15, 200, 100)
wall5  = BlockySprite(GRAY, 15, 200, 150)
wall6  = BlockySprite(GRAY, 15, 150, 100)
wall7  = BlockySprite(GRAY, 15, 150, 150)
player = BlockySprite(RED,  40, WIDTH/2-20, HEIGHT/2-20)

# Přidání několika dalších spritů do seznamu
# (jen jeden sprite - ten poslední - bude ve skutečnosti pohyblivý)
all_sprites.add(wall1)
all_sprites.add(wall2)
all_sprites.add(wall3)
all_sprites.add(wall4)
all_sprites.add(wall5)
all_sprites.add(wall6)
all_sprites.add(wall7)
all_sprites.add(player)

# Seznam všech nepohyblivých spritů
all_sprites_but_player.add(wall1)
all_sprites_but_player.add(wall2)
all_sprites_but_player.add(wall3)
all_sprites_but_player.add(wall4)
all_sprites_but_player.add(wall5)
all_sprites_but_player.add(wall6)
all_sprites_but_player.add(wall7)



# Posun všech spritů ve skupině na základě jejich rychlosti
def move_sprites(sprite_group, playground_width, playground_height):
    for sprite in sprite_group:
        # Posun spritu
        sprite.rect.x = sprite.rect.x + sprite.speed_x
        sprite.rect.y = sprite.rect.y + sprite.speed_y
        # Kontrola, zda sprite nenarazil do okrajů okna
        if sprite.rect.x </gc 0:
            sprite.rect.x = 0
            sprite.speed_x = 0
        if sprite.rect.x + sprite.rect.width >/gc playground_width:
            sprite.rect.x = playground_width - sprite.rect.width
            sprite.speed_x = 0
        if sprite.rect.y </gc 0:
            sprite.rect.y = 0
            sprite.speed_y = 0
        if sprite.rect.y + sprite.rect.height >/gc playground_height:
            sprite.rect.y = playground_height - sprite.rect.height
            sprite.speed_y = 0



# Vykreslení celé scény na obrazovku
def draw_scene(display, background_color, sprite_group):
    # Vyplnění plochy okna černou barvou
    display.fill(background_color)
    # Vykreslení celé skupiny spritů do bufferu
    sprite_group.draw(display)
    # Obnovení obsahu obrazovky (překlopení zadního a předního bufferu)
    pygame.display.update()



# Změna barvy spritu na základě kolize s hráčem
def change_colors(sprite_group, hit_list):
    # Projít všemi sprity ze skupiny, kterou detekovala kolizní funkce
    for sprite in sprite_group:
        if sprite in hit_list:
            sprite.yellowColor()
        else:
            sprite.grayColor()



# Zjistí kolize spritu se "stěnami" (nepohyblivými sprity)
def check_collisions(player, sprite_group):
    # Vytvoření seznamu spritů, které kolidují s hráčem
    hit_list = pygame.sprite.spritecollide(player, sprite_group, False)
    # Změna barev kolidujících spritů
    change_colors(sprite_group, hit_list)
    collisions = len(hit_list)
    # Přenastavení titulku okna
    pygame.display.set_caption('Pygame test #22: collisions ' + str(collisions))



# Hlavní herní smyčka
while True:
    # Načtení a zpracování všech událostí z fronty
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
        if event.type == KEYDOWN:
            if event.key == K_ESCAPE:
                pygame.quit()
                sys.exit()
            # Stiskem kurzorových kláves je možné měnit směr pohybu spritu
            elif event.key == pygame.K_LEFT:
                player.speed_x = -3
            elif event.key == pygame.K_RIGHT:
                player.speed_x = +3
            elif event.key == pygame.K_UP:
                player.speed_y = -3
            elif event.key == pygame.K_DOWN:
                player.speed_y = +3
        if event.type == KEYUP:
            # Puštění kurzorových kláves vede k zastavení pohybu spritu
            if event.key == pygame.K_LEFT:
                player.speed_x = 0
            elif event.key == pygame.K_RIGHT:
                player.speed_x = 0
            elif event.key == pygame.K_UP:
                player.speed_y = 0
            elif event.key == pygame.K_DOWN:
                player.speed_y = 0

    move_sprites(all_sprites, display.get_width(), display.get_height())
    check_collisions(player, all_sprites_but_player)
    draw_scene(display, BLACK, all_sprites)
    clock.tick(20)

# finito

10

Obrázek 10: Kolize hráče s více (konkrétně se čtyřmi) sprity.

8. Kolize se sprity, které nemají čtvercový či obdélníkový tvar

Tvary všech spritů, které jsme doposud ve všech demonstračních příkladech používali, byly čtvercové či obdélníkové, což znamená, že vizuální tvar spritu na obrazovce přesně odpovídal rozměrům spritů uložených v datové struktuře rect. V naprosté většině případů je však nutné v reálných aplikacích pracovat se sprity odlišných tvarů. V knihovně Pygame pro tyto případy existují dva další postupy, jak zjistit kolizi spritů jiných tvarů, než je čtverec nebo obdélník.

11

Obrázek 11: Kruhový sprite použitý v dalších příkladech.

Pokud se jedná o kruhové sprity, lze použít funkci pygame.sprite.collide_circle(), které se předají dva sprity a výsledkem je pravdivostní hodnota nastavená v závislosti na tom, zda se dvě kružnice, kterou oba sprity představují, překrývají či nikoli. Jak již víme z předchozího textu, je možné takovou callback funkci použít v pygame.sprite.spritecollide() (předává se ve čtvrtém parametru). Druhá užitečná funkce se jmenuje pygame.sprite.collide_mask() a použije se tehdy, pokud je zapotřebí vypočítat kolizi s přesností jednoho pixelu. Tato funkce je však nejpomalejší, proto je vhodné ji používat opravdu pouze ve chvíli, kdy je to kvůli tvarům spritů nezbytně nutné.

12

Obrázek 12: Druhý kruhový sprite použitý v dalších příkladech.

13

Obrázek 13: Barevně upravené druhý kruhový sprite, který je taktéž použitý v dalších příkladech.

9. Zdrojový kód demonstračního příkladu pygame23: špatně vypočtená kolize kruhových spritů

Podívejme se nejprve na příklad, v němž se sice používají kruhové sprity, ale výpočet kolize je prováděn „starým“ způsobem, tj. pouze na základě detekce překryvu obdélníků. Tento příklad se liší od předchozích příkladů v tom ohledu, že se obrázek spritu načte z externího souboru, zatímco dříve jsme sprity jednoduše vyplnili nějakou barvou. Kvůli tomu, že budeme potřebovat zvýraznit náraz hráče do dalších spritů, načteme pro každý sprite dva obrázky, které se budou pouze prohazovat, podobně jako jsme u předchozích příkladů sprity přebarvovali.

Načtení obrázků v konstruktoru třídy CircularSprite:

        # Načtení obrázků, jeden pro normální sprite,
        # druhý pro sprite, který koliduje s hráčem
        self.normal_image = pygame.image.load("images/" + normal_image_name + ".png")
        self.collision_image = pygame.image.load("images/" + collision_image_name + ".png")

        # Vytvoření obrázku představujícího vizuální obraz spritu:
        self.image = self.normal_image

Změna obrázků po detekci kolize je zajištěna dvojicí funkcí:

    # Nastavení obrázku u spritu, který kolidoval s hráčem
    def setCollisionImage(self):
        self.image = self.collision_image

    # Nastavení obrázku u spritu, který nekolidoval s hráčem
    def setNormalImage(self):
        self.image = self.normal_image

Vlastní reakce na detekci kolize:

# Změna obrázku spritu na základě kolize s hráčem
def change_sprite_image(sprite_group, hit_list):
    # Projít všemi sprity ze skupiny, kterou detekovala kolizní funkce
    for sprite in sprite_group:
        if sprite in hit_list:
            sprite.setCollisionImage()
        else:
            sprite.setNormalImage()



# Zjistí kolize spritu se "stěnami" (nepohyblivými sprity)
def check_collisions(player, sprite_group):
    # Vytvoření seznamu spritů, které kolidují s hráčem
    hit_list = pygame.sprite.spritecollide(player, sprite_group, False)
    # Změna obrázků kolidujících spritů
    change_sprite_image(sprite_group, hit_list)
    collisions = len(hit_list)
    # Přenastavení titulku okna
    pygame.display.set_caption('Pygame test #23: collisions ' + str(collisions))

14

Obrázek 14: Uživatel může pohybovat spritem s obrázkem zeměkoule. Zde prozatím nedošlo k žádné kolizi s ostatními sprity.

Podívejme se nyní na úplný výpis zdrojového kódu tohoto příkladu:

#!/usr/bin/python
# vim: set fileencoding=utf-8

# Demonstrační příklady využívající knihovnu Pygame

# Příklad číslo 23: použití spritů, pohyblivý sprite,
#                   kolize, různé tvary kolidujících spritů


import pygame, sys, os, math

# Nutno importovat kvůli konstantám QUIT atd.
from pygame.locals import *

# Velikost okna aplikace
WIDTH = 320
HEIGHT = 240



# Třída představující sprite zobrazený jako jednobarevný čtverec.
class CircularSprite(pygame.sprite.Sprite):
    # Konstruktor
    def __init__(self, x, y, normal_image_name, collision_image_name):
        # Nejprve je nutné zavolat konstruktor předka,
        # tj. konstruktor třídy pygame.sprite.Sprite:
        pygame.sprite.Sprite.__init__(self)

        # Načtení obrázků, jeden pro normální sprite,
        # druhý pro sprite, který koliduje s hráčem
        self.normal_image = pygame.image.load("images/" + normal_image_name + ".png")
        self.collision_image = pygame.image.load("images/" + collision_image_name + ".png")

        # Vytvoření obrázku představujícího vizuální obraz spritu:
        self.image = self.normal_image
        #self.image.fill(color) - toto nyní nepoužijeme, pozůstatek z BlockySprite

        # Vytvoření obalového obdélníku
        # (velikost se získá z rozměru obrázku)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y

        # Počáteční rychlost spritu
        self.speed_x = 0
        self.speed_y = 0

    # Nastavení obrázku u spritu, který kolidoval s hráčem
    def setCollisionImage(self):
        self.image = self.collision_image

    # Nastavení obrázku u spritu, který nekolidoval s hráčem
    def setNormalImage(self):
        self.image = self.normal_image



# Inicializace knihovny Pygame
pygame.init()

clock = pygame.time.Clock()

# Vytvoření okna pro vykreslování
display = pygame.display.set_mode([WIDTH, HEIGHT])

# Nastavení titulku okna
pygame.display.set_caption('Pygame test #23')

# Konstanty s n-ticemi představujícími základní barvy
BLACK   = (  0,   0,   0)

# Objekt sdružující všechny sprity
all_sprites = pygame.sprite.Group()
# Objekt sdružující všechny sprity kromě hráče
all_sprites_but_player = pygame.sprite.Group()

# Vytvoření několika typů spritů
#                         x    y   první obr. druhý obr.
wall1  = CircularSprite( 20,  40, "sprite3", "sprite2")
wall2  = CircularSprite( 20, 140, "sprite3", "sprite2")
wall3  = CircularSprite(220,  40, "sprite3", "sprite2")
wall4  = CircularSprite(220, 140, "sprite3", "sprite2")
wall5  = CircularSprite(120,  40, "sprite3", "sprite2")
wall6  = CircularSprite(120, 140, "sprite3", "sprite2")
player = CircularSprite(WIDTH/2-20, HEIGHT/2-20, "sprite1", "sprite1")

# Přidání několika dalších spritů do seznamu
# (jen jeden sprite - ten poslední - bude ve skutečnosti pohyblivý)
all_sprites.add(wall1)
all_sprites.add(wall2)
all_sprites.add(wall3)
all_sprites.add(wall4)
all_sprites.add(wall5)
all_sprites.add(wall6)
all_sprites.add(player)

# Seznam všech nepohyblivých spritů
all_sprites_but_player.add(wall1)
all_sprites_but_player.add(wall2)
all_sprites_but_player.add(wall3)
all_sprites_but_player.add(wall4)
all_sprites_but_player.add(wall5)
all_sprites_but_player.add(wall6)



# Posun všech spritů ve skupině na základě jejich rychlosti
def move_sprites(sprite_group, playground_width, playground_height):
    for sprite in sprite_group:
        # Posun spritu
        sprite.rect.x = sprite.rect.x + sprite.speed_x
        sprite.rect.y = sprite.rect.y + sprite.speed_y
        # Kontrola, zda sprite nenarazil do okrajů okna
        if sprite.rect.x </gc 0:
            sprite.rect.x = 0
            sprite.speed_x = 0
        if sprite.rect.x + sprite.rect.width >/gc playground_width:
            sprite.rect.x = playground_width - sprite.rect.width
            sprite.speed_x = 0
        if sprite.rect.y </gc 0:
            sprite.rect.y = 0
            sprite.speed_y = 0
        if sprite.rect.y + sprite.rect.height >/gc playground_height:
            sprite.rect.y = playground_height - sprite.rect.height
            sprite.speed_y = 0



# Vykreslení celé scény na obrazovku
def draw_scene(display, background_color, sprite_group):
    # Vyplnění plochy okna černou barvou
    display.fill(background_color)
    # Vykreslení celé skupiny spritů do bufferu
    sprite_group.draw(display)
    # Obnovení obsahu obrazovky (překlopení zadního a předního bufferu)
    pygame.display.update()



# Změna obrázku spritu na základě kolize s hráčem
def change_sprite_image(sprite_group, hit_list):
    # Projít všemi sprity ze skupiny, kterou detekovala kolizní funkce
    for sprite in sprite_group:
        if sprite in hit_list:
            sprite.setCollisionImage()
        else:
            sprite.setNormalImage()



# Zjistí kolize spritu se "stěnami" (nepohyblivými sprity)
def check_collisions(player, sprite_group):
    # Vytvoření seznamu spritů, které kolidují s hráčem
    hit_list = pygame.sprite.spritecollide(player, sprite_group, False)
    # Změna obrázků kolidujících spritů
    change_sprite_image(sprite_group, hit_list)
    collisions = len(hit_list)
    # Přenastavení titulku okna
    pygame.display.set_caption('Pygame test #23: collisions ' + str(collisions))



# Hlavní herní smyčka
while True:
    # Načtení a zpracování všech událostí z fronty
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
        if event.type == KEYDOWN:
            if event.key == K_ESCAPE:
                pygame.quit()
                sys.exit()
            # Stiskem kurzorových kláves je možné měnit směr pohybu spritu
            elif event.key == pygame.K_LEFT:
                player.speed_x = -3
            elif event.key == pygame.K_RIGHT:
                player.speed_x = +3
            elif event.key == pygame.K_UP:
                player.speed_y = -3
            elif event.key == pygame.K_DOWN:
                player.speed_y = +3
        if event.type == KEYUP:
            # Puštění kurzorových kláves vede k zastavení pohybu spritu
            if event.key == pygame.K_LEFT:
                player.speed_x = 0
            elif event.key == pygame.K_RIGHT:
                player.speed_x = 0
            elif event.key == pygame.K_UP:
                player.speed_y = 0
            elif event.key == pygame.K_DOWN:
                player.speed_y = 0

    move_sprites(all_sprites, display.get_width(), display.get_height())
    check_collisions(player, all_sprites_but_player)
    draw_scene(display, BLACK, all_sprites)
    clock.tick(20)

# finito

15

Obrázek 15: Zde můžeme vidět problém: detekce kolize v případě, kdy se sice protnou obalové obdélníky spritů, ale nikoli jejich vybarvené pixely.

10. Zdrojový kód demonstračního příkladu pygame24: použití korektní detekce kolize

V dnešním posledním příkladu se již detekce kolize kruhových spritů provádí korektně. Je tomu tak z toho důvodu, že se u každého spritu pamatuje i jejich poloměr:

# Třída představující sprite zobrazený jako jednobarevný čtverec.
class CircularSprite(pygame.sprite.Sprite):
    # Konstruktor
    def __init__(self, x, y, radius, normal_image_name, collision_image_name):
        ...
        ...
        ...
        # U kulatých spritů se nastavuje i poloměr
        self.radius = radius

A dále se při detekci kolizí volá již výše zmíněná callback funkce pygame.sprite.collide_circle():

# Zjistí kolize spritu se "stěnami" (nepohyblivými sprity)
def check_collisions(player, sprite_group):
    # Vytvoření seznamu spritů, které kolidují s hráčem
    hit_list = pygame.sprite.spritecollide(player, sprite_group, False, pygame.sprite.collide_circle)
    # Změna obrázků kolidujících spritů
    change_sprite_image(sprite_group, hit_list)
    collisions = len(hit_list)
    # Přenastavení titulku okna
    pygame.display.set_caption('Pygame test #24: collisions ' + str(collisions))

Opět se podívejme na výpis celého zdrojového kódu, z něhož budou všechny změny zřejmé:

#!/usr/bin/python
# vim: set fileencoding=utf-8

# Demonstrační příklady využívající knihovnu Pygame

# Příklad číslo 24: použití spritů, pohyblivý sprite,
#                   kolize, různé tvary kolidujících spritů
#                   kolize se korektně počítá pro kruhové sprity


import pygame, sys, os, math

# Nutno importovat kvůli konstantám QUIT atd.
from pygame.locals import *

# Velikost okna aplikace
WIDTH = 320
HEIGHT = 240



# Třída představující sprite zobrazený jako jednobarevný čtverec.
class CircularSprite(pygame.sprite.Sprite):
    # Konstruktor
    def __init__(self, x, y, radius, normal_image_name, collision_image_name):
        # Nejprve je nutné zavolat konstruktor předka,
        # tj. konstruktor třídy pygame.sprite.Sprite:
        pygame.sprite.Sprite.__init__(self)

        # Načtení obrázků, jeden pro normální sprite,
        # druhý pro sprite, který koliduje s hráčem
        self.normal_image = pygame.image.load("images/" + normal_image_name + ".png")
        self.collision_image = pygame.image.load("images/" + collision_image_name + ".png")

        # Vytvoření obrázku představujícího vizuální obraz spritu:
        self.image = self.normal_image
        #self.image.fill(color) - toto nyní nepoužijeme, pozůstatek z BlockySprite

        # Vytvoření obalového obdélníku
        # (velikost se získá z rozměru obrázku)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y

        # U kulatých spritů se nastavuje i poloměr
        self.radius = radius

        # Počáteční rychlost spritu
        self.speed_x = 0
        self.speed_y = 0

    # Nastavení obrázku u spritu, který kolidoval s hráčem
    def setCollisionImage(self):
        self.image = self.collision_image

    # Nastavení obrázku u spritu, který nekolidoval s hráčem
    def setNormalImage(self):
        self.image = self.normal_image



# Inicializace knihovny Pygame
pygame.init()

clock = pygame.time.Clock()

# Vytvoření okna pro vykreslování
display = pygame.display.set_mode([WIDTH, HEIGHT])

# Nastavení titulku okna
pygame.display.set_caption('Pygame test #24')

# Konstanty s n-ticemi představujícími základní barvy
BLACK   = (  0,   0,   0)

# Objekt sdružující všechny sprity
all_sprites = pygame.sprite.Group()
# Objekt sdružující všechny sprity kromě hráče
all_sprites_but_player = pygame.sprite.Group()

# Vytvoření několika typů spritů
#                         x    y   r  první obr. druhý obr.
wall1  = CircularSprite( 20,  40, 31, "sprite3", "sprite2")
wall2  = CircularSprite( 20, 140, 31, "sprite3", "sprite2")
wall3  = CircularSprite(220,  40, 31, "sprite3", "sprite2")
wall4  = CircularSprite(220, 140, 31, "sprite3", "sprite2")
wall5  = CircularSprite(120,  40, 31, "sprite3", "sprite2")
wall6  = CircularSprite(120, 140, 31, "sprite3", "sprite2")
player = CircularSprite(WIDTH/2-20, HEIGHT/2-20, 22, "sprite1", "sprite1")

# Přidání několika dalších spritů do seznamu
# (jen jeden sprite - ten poslední - bude ve skutečnosti pohyblivý)
all_sprites.add(wall1)
all_sprites.add(wall2)
all_sprites.add(wall3)
all_sprites.add(wall4)
all_sprites.add(wall5)
all_sprites.add(wall6)
all_sprites.add(player)

# Seznam všech nepohyblivých spritů
all_sprites_but_player.add(wall1)
all_sprites_but_player.add(wall2)
all_sprites_but_player.add(wall3)
all_sprites_but_player.add(wall4)
all_sprites_but_player.add(wall5)
all_sprites_but_player.add(wall6)



# Posun všech spritů ve skupině na základě jejich rychlosti
def move_sprites(sprite_group, playground_width, playground_height):
    for sprite in sprite_group:
        # Posun spritu
        sprite.rect.x = sprite.rect.x + sprite.speed_x
        sprite.rect.y = sprite.rect.y + sprite.speed_y
        # Kontrola, zda sprite nenarazil do okrajů okna
        if sprite.rect.x </gc 0:
            sprite.rect.x = 0
            sprite.speed_x = 0
        if sprite.rect.x + sprite.rect.width >/gc playground_width:
            sprite.rect.x = playground_width - sprite.rect.width
            sprite.speed_x = 0
        if sprite.rect.y </gc 0:
            sprite.rect.y = 0
            sprite.speed_y = 0
        if sprite.rect.y + sprite.rect.height >/gc playground_height:
            sprite.rect.y = playground_height - sprite.rect.height
            sprite.speed_y = 0



# Vykreslení celé scény na obrazovku
def draw_scene(display, background_color, sprite_group):
    # Vyplnění plochy okna černou barvou
    display.fill(background_color)
    # Vykreslení celé skupiny spritů do bufferu
    sprite_group.draw(display)
    # Obnovení obsahu obrazovky (překlopení zadního a předního bufferu)
    pygame.display.update()



# Změna obrázku spritu na základě kolize s hráčem
def change_sprite_image(sprite_group, hit_list):
    # Projít všemi sprity ze skupiny, kterou detekovala kolizní funkce
    for sprite in sprite_group:
        if sprite in hit_list:
            sprite.setCollisionImage()
        else:
            sprite.setNormalImage()



# Zjistí kolize spritu se "stěnami" (nepohyblivými sprity)
def check_collisions(player, sprite_group):
    # Vytvoření seznamu spritů, které kolidují s hráčem
    hit_list = pygame.sprite.spritecollide(player, sprite_group, False, pygame.sprite.collide_circle)
    # Změna obrázků kolidujících spritů
    change_sprite_image(sprite_group, hit_list)
    collisions = len(hit_list)
    # Přenastavení titulku okna
    pygame.display.set_caption('Pygame test #24: collisions ' + str(collisions))



# Hlavní herní smyčka
while True:
    # Načtení a zpracování všech událostí z fronty
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
        if event.type == KEYDOWN:
            if event.key == K_ESCAPE:
                pygame.quit()
                sys.exit()
            # Stiskem kurzorových kláves je možné měnit směr pohybu spritu
            elif event.key == pygame.K_LEFT:
                player.speed_x = -3
            elif event.key == pygame.K_RIGHT:
                player.speed_x = +3
            elif event.key == pygame.K_UP:
                player.speed_y = -3
            elif event.key == pygame.K_DOWN:
                player.speed_y = +3
        if event.type == KEYUP:
            # Puštění kurzorových kláves vede k zastavení pohybu spritu
            if event.key == pygame.K_LEFT:
                player.speed_x = 0
            elif event.key == pygame.K_RIGHT:
                player.speed_x = 0
            elif event.key == pygame.K_UP:
                player.speed_y = 0
            elif event.key == pygame.K_DOWN:
                player.speed_y = 0

    move_sprites(all_sprites, display.get_width(), display.get_height())
    check_collisions(player, all_sprites_but_player)
    draw_scene(display, BLACK, all_sprites)
    clock.tick(20)

# finito

16

Obrázek 16: Poslední demonstrační příklad ihned po svém spuštění.

11. Obsah následující části seriálu

V následující části seriálu o programovacích jazycích i o knihovnách a frameworcích vhodných pro výuku programování popř. pro výuku základů počítačové grafiky, se budeme zabývat možnostmi, které programátorům nabízí modul nazvaný pygame.transform. Jak již název tohoto modulu naznačuje, je možné s jeho využitím aplikovat afinní transformace na všechny objekty typu Surface, čehož je možné využít v mnoha (mnohdy velmi zajímavých) grafických efektech a dosáhnout tak i „3D“ vzhledu hry či grafického dema.

17

Obrázek 17: Poslední demonstrační příklad: obalové obdélníky spritů se sice překrývají, ale kolize nebyla detekována.

12. Repositář se zdrojovými kódy všech dnešních demonstračních příkladů

Všechny demonstrační příklady, s nimiž jsme se v dnešním článku seznámili, byly, podobně jako ve všech předchozích částech tohoto seriálu, uloženy do Git repositáře umístěného na GitHubu (https://github.com/tisnik/presentations). Poslední verze zdrojových kódů naleznete pod těmito odkazy:

# Příklad Zdrojový kód
1 pygame20.py https://github.com/tisnik/presentations/blob/master/pygame/pygame20.py
2 pygame21.py https://github.com/tisnik/presentations/blob/master/pygame/pygame21.py
3 pygame22.py https://github.com/tisnik/presentations/blob/master/pygame/pygame22.py
4 pygame23.py https://github.com/tisnik/presentations/blob/master/pygame/pygame23.py
5 pygame24.py https://github.com/tisnik/presentations/blob/master/pygame/pygame24.py

18

Obrázek 18: Poslední demonstrační příklad: až nyní došlo ke kolizi.

13. Funkce použité v dnešních demonstračních příkladech

Opět si pro úplnost vypišme funkce a metody nabízené knihovnou Pygame, které jsme použili v dnešních demonstračních příkladech:

  1. pygame.init()
    http://www.pygame.org/docs/ref/pygame.html#pygame.init
  2. pygame.quit()
    http://www.pygame.org/docs/ref/pygame.html#pygame.quit
  3. pygame.display.set_mode()
    http://www.pygame.org/docs/ref/display.html#pygame.display.set_mode
  4. pygame.display.set_caption()
    http://www.pygame.org/docs/ref/display.html#pygame.display.set_caption
  5. pygame.display.quit()
    http://www.pygame.org/docs/ref/display.html#pygame.display.quit
  6. pygame.display.update()
    http://www.pygame.org/docs/ref/display.html#pygame.display.update
  7. pygame.event.get()
    http://www.pygame.org/docs/ref/event.html#pygame.event.get
  8. pygame.time.Clock()
    http://www.pygame.org/docs/ref/time.html#pygame.time.Clock
  9. pygame.time.Clock.tick()
    http://www.pygame.org/docs/ref/time.html#pygame.time.Clock.tick
  10. pygame.Surface()
    http://www.pygame.org/docs/ref/surface.html
  11. pygame.Surface.fill()
    http://www.pygame.org/docs/ref/surface.html#pygame.Surface.fill
  12. pygame.image.load()
    http://www.pygame.org/docs/ref/image.html#pygame.image.load
  13. pygame.Sprite.__init__()
    http://www.pygame.org/docs/ref/sprite.html#pygame.sprite.Sprite
  14. pygame.sprite.Group()
    http://www.pygame.org/docs/ref/sprite.html#pygame.sprite.Group
  15. pygame.sprite.Group.add()
    http://www.pygame.org/docs/ref/sprite.html#pygame.sprite.Group.add
  16. pygame.sprite.Group.draw()
    http://www.pygame.org/docs/ref/sprite.html#pygame.sprite.Group.draw
  17. pygame.sprite.spritecollide()
    http://www.pygame.org/docs/ref/sprite.html#pygame.sprite.spritecollide
  18. pygame.sprite.collide_circle()
    http://www.pygame.org/docs/ref/sprite.html#pygame.sprite.collide_circle

19

Obrázek 19: Sprity se mohou překrývat v jiném pořadí.

14. Odkazy na Internetu

  1. Program Arcade Games With Python And Pygame
    http://programarcadegames.com/index.php?lang=en#
  2. Program Arcade Games With Python And Pygame: Chapter 13: Introduction to Sprites:
    http://programarcadegames.com/index.php?chapter=introduction_to_sprites#
  3. Pygame.org
    http://pygame.org/hifi.html
  4. Pygame - instalační soubory pro různé operační systémy
    http://pygame.org/download.shtml
  5. Pygame: documentation
    http://www.pygame.org/docs/
  6. Pygame Wiki: Getting Started
    http://www.pygame.org/wiki/GettingStarted
  7. Pygame Tutorials: Tutorials Basic
    http://pygametutorials.wikidot.com/tutorials-basic
  8. Pygame: Font class
    http://www.pygame.org/docs/ref/font.html
  9. Python.org (dokumentace k jazyku, odkazy na instalační soubory atd.)
    https://www.python.org/
  10. How to Draw with Pygame on Your Raspberry Pi
    http://www.dummies.com/how-to/content/how-to-draw-with-pygame-on-your-raspberry-pi.html
  11. Bresenham's line algorithm
    https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
  12. Xiaolin Wu's line algorithm
    https://en.wikipedia.org/wiki/Xiaolin_Wu%27s_line_algorithm
  13. Pyglet
    https://pypi.python.org/pypi/pyglet/1.2.4
  14. Pyglet documentation
    http://pythonhosted.org/pyglet/
  15. PyOpenGL
    https://pypi.python.org/pypi/PyOpenGL/
  16. Computer font (Wikipedia)
    https://en.wikipedia.org/wiki/Computer_font
  17. TrueType (Wikipedia)
    https://en.wikipedia.org/wiki/TrueType
  18. SDL and Fonts
    http://www.gamedev.net/page/resources/_/technical/game-programming/sdl--fonts-r1953
  19. SDL_ttf Documentation
    http://www.libsdl.org/projects/SDL_ttf/docs/
  20. SDL_ttf 2.0 (není prozatím součástí SDLJava)
    http://www.libsdl.org/projects/SDL_ttf/
  21. SDL_ttf doc
    http://www.libsdl.org/projects/SDL_ttf/docs/SDL_ttf_frame.html
  22. SDL 1.2 Documentation: SDL_Surface
    http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlsurface.html
  23. SDL 1.2 Documentation: SDL_PixelFormat
    http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlpixelformat.html
  24. SDL 1.2 Documentation: SDL_LockSurface
    http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdllocksurface.html
  25. SDL 1.2 Documentation: SDL_UnlockSurface
    http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlunlocksurface.html
  26. SDL 1.2 Documentation: SDL_LoadBMP
    http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlloadbmp.html
  27. SDL 1.2 Documentation: SDL_SaveBMP
    http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlsavebmp.html
  28. SDL 1.2 Documentation: SDL_BlitSurface
    http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlblitsurface.html
  29. SDL 1.2 Documentation: SDL_VideoInfo
    http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlvideoinfo.html
  30. SDL 1.2 Documentation: SDL_GetVideoInfo
    http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlgetvideoinfo.html
  31. Graphical user interface (Wikipedia)
    http://en.wikipedia.org/wiki/Graphical_user_interface
  32. The Real History of the GUI
    http://articles.sitepoint.com/article/real-history-gui
  33. Computer-History: Xerox Alto
    http://www.mr-gadget.de/apple/2004-01-15/computer-history-xerox-alto
  34. bitsavers.org
    http://www.bitsavers.org/
  35. Dokumenty k počítači Xerox Alto na bitsavers.org
    http://www.bitsavers.org/pdf/xerox/alto/
  36. The ALTO Computer
    http://www.maniacworld.com/alto-computer-video.html
  37. Xerox Alto Operating System and Alto Applications
    http://www.digibarn.com/collections/software/alto/index.html
  38. Xerox Alto (Wikipedia)
    http://en.wikipedia.org/wiki/Xerox_Alto
  39. BitBLT routines (1975)
    http://www.bitsavers.org/pdf/xerox/alto/BitBLT_Nov1975.pdf
  40. BitBlt in Squeak
    http://wiki.squeak.org/squeak/189
  41. Bitmaps, Device Contexts and BitBlt
    http://www.winprog.org/tutorial/bitmaps.html
  42. BitBlt Game Programming Tutorial
    http://www.freevbcode.com/ShowCode.asp?ID=3677
  43. Bit blit (Wikipedia)
    http://en.wikipedia.org/wiki/BitBLT
  44. The Xerox Alto
    http://toastytech.com/guis/alto3.html
  45. History of the graphical user interface
    http://en.wikipedia.org/wiki/History_of_the_graphical_user_interface
  46. Domovská stránka systému LÖVE
    http://love2d.org/
  47. Dokumentace k systému LÖVE
    http://love2d.org/wiki/love
  48. Domovská stránka programovacího jazyka Lua
    http://www.lua.org/
  49. Seriál o programovacím jazyku Lua (root.cz):
    http://www.root.cz/serialy/programovaci-jazyk-lua/
  50. Web o Lieru, Gusanos, GeneRally, Atari atd.
    http://karelik.wz.cz/
  51. Web o Lieru, Gusanos
    http://karelik.wz.cz/gusanos.php
  52. GUSANOS
    http://gusanos.sourceforge.net/
  53. GUSANOS Download
    http://sourceforge.net/projects/gusanos/
  54. Lua
    http://www.linuxexpres.cz/praxe/lua
  55. Lua
    http://cs.wikipedia.org/wiki/Lua
  56. Lua (programming language)
    http://en.wikipedia.org/wiki/Lua_(programming_language)
  57. The Lua Programming Language
    http://www.tiobe.com/index.php/paperinfo/tpci/Lua.html
  58. Lua Programming Gems
    http://www.lua.org/gems/
  59. LuaForge
    http://luaforge.net/
  60. Forge project tree
    http://luaforge.net/softwaremap/trove_list.php
  61. SdlBasic home page
    http://www.sdlbasic.altervista.org/main/
  62. SdlBasic examples
    http://nitrofurano.linuxkafe.com/sdlbasic/
  63. SdlBasic na Wikipedii
    http://en.wikipedia.org/wiki/SdlBasic
  64. Simple DirectMedia Layer
    http://en.wikipedia.org/wiki/Simple_DirectMedia_Layer
  65. SDLBASIC – The high-level interpreter for all?
    http://openbytes.wordpress.com/2008/11/08/sdlbasic-the-high-level-interpreter-for-all/
  66. FreeBasic home page
    http://www.freebasic.net/
  67. FreeBASIC (Wikipedia EN)
    https://en.wikipedia.org/wiki/FreeBASIC
  68. FreeBASIC Wiki
    http://www.freebasic.net/wiki/wikka.php?wakka=FBWiki
  69. FreeBASIC Manual
    http://www.freebasic.net/wiki/wikka.php?wakka=DocToc
  70. FreeBASIC (Wikipedia CZ)
    http://cs.wikipedia.org/wiki/FreeBASIC
  71. The Griffon Legend
    http://syn9.thingie.net/?table=griffonlegend
  72. Seriál Letní škola programovacího jazyka Logo
    http://www.root.cz/serialy/letni-skola-programovaciho-jazyka-logo/
  73. Scratch: oficiální stránka projektu
    http://scratch.mit.edu/
  74. Scratch: galerie projektů vytvořených ve Scratchi
    http://scratch.mit.edu/galleries/browse/newest
  75. Scratch: nápověda
    file:///usr/share/scratch/Help/en/index.html
  76. Scratch: obrazovky nápovědy
    file:///usr/share/scratch/Help/en/allscreens.html
  77. Scratch (Wikipedie CZ)
    http://cs.wikipedia.org/wiki/Scratch
  78. Scratch (programming language)
    http://en.wikipedia.org/wiki/Scratch_(programming_language)
  79. Scratch Modification
    http://wiki.scratch.mit.edu/wiki/Scratch_Modification
  80. Scratch Lowers Resistance to Programming
    http://www.wired.com/gadgetlab/2009/03/scratch-lowers/
  81. Snap!
    http://snap.berkeley.edu/
  82. Prostředí Snap!
    http://snap.berkeley.edu/snapsource/snap.html
  83. Alternatives to Scratch
    http://wiki.scratch.mit.edu/wiki/Alternatives_to_Scratch
  84. Basic-256 home page
    http://www.basic256.org/index_en
  85. Basic-256 Language Documentation
    http://doc.basic256.org/doku.php
  86. Basic-256 Art Gallery
    http://www.basic256.org/artgallery
  87. Basic-256 Tutorial
    http://www.basic256.org/tutorials
  88. Why BASIC?
    http://www.basic256.org/whybasic
  89. A book to teach ANYBODY how to program a computer (using BASIC)
    http://www.basicbook.org/
  90. BASIC Computer Games (published 1978) - Hammurabi
    http://atariarchives.org/basicgames/showpage.php?page=78
  91. Hamurabi - zdrojový kód v BASICu
    http://www.dunnington.u-net.com/public/basicgames/HMRABI