Browse Source

Update Pygame examples with asyncio support for web compatibility

master
skrabanek 7 days ago
parent
commit
5ded326110
  1. 428
      pygame/01_pexeso.py
  2. 382
      pygame/02_flappy_bird.py
  3. 278
      pygame/03_chytani_jablek.py
  4. 386
      pygame/04_pong_dva_hraci.py
  5. 338
      pygame/05_skakacka_gravitace.py
  6. 370
      pygame/06_vesmirna_strilecka.py
  7. 346
      pygame/07_had.py

428
pygame/01_pexeso.py

@ -1,216 +1,224 @@
import pygame import pygame
import asyncio # PŘIDÁNO PRO WEB: importujeme asyncio pro neblokující smyčku
import random import random
import sys import sys
# pygame.init() nastartuje všechny vnitřní moduly Pygame.
# Je to naprosto povinný první krok před tím, než můžeme v Pygame cokoliv udělat.
pygame.init()
# --- Konstanty okna a rozvržení mřížky ---
# Konstanty (velkými písmeny) se během hry nemění. Určují základní vlastnosti naší hry.
SIRKA_OKNA = 600
VYSKA_OKNA = 600
FPS = 30 # Počet snímků za sekundu (jak rychle se hra překresluje). 30 bohatě stačí pro Pexeso.
# Kolik karet chceme mít na šířku a na výšku?
RADKY = 4
SLOUPCE = 4
# Velikost jedné karty v pixelech (obrazových bodech)
KARTICKA_VELIKOST = 100
# Mezera mezi kartičkami
MEZERA = 20
# Výpočet celkové šířky a výšky celé mřížky karet, abychom ji pak mohli na obrazovce vycentrovat
Mrizka_sirka = (SLOUPCE * KARTICKA_VELIKOST) + ((SLOUPCE - 1) * MEZERA)
Mrizka_vyska = (RADKY * KARTICKA_VELIKOST) + ((RADKY - 1) * MEZERA)
# Vypočítáme, kde mřížka začíná na osách X (vodorovně) a Y (svisle), aby byla přesně uprostřed okna
Odsazeni_x = (SIRKA_OKNA - Mrizka_sirka) // 2
Odsazeni_y = (VYSKA_OKNA - Mrizka_vyska) // 2
# Barvy v Pygame se zadávají ve formátu (R, G, B) = Red, Green, Blue.
# Každá hodnota je od 0 (nic) do 255 (maximum dané barvy).
BILA = (255, 255, 255)
SEDA = (150, 150, 150)
TMAVE_MODRA = (20, 20, 80)
ZELENA_TEXT = (50, 255, 50)
# Budeme mít celkem 16 karet (4x4), což znamená 8 různých párů (8 unikátních barev).
BARVY_PARU = [
(255, 0, 0), # Červená
(0, 255, 0), # Světle Zelená
(0, 0, 255), # Modrá
(255, 255, 0), # Žlutá
(255, 0, 255), # Fialová
(0, 255, 255), # Azurová
(255, 165, 0), # Oranžová
(139, 69, 19) # Hnědá
]
# Vytvoříme samotné okno (obrazovku), kam se bude vše vykreslovat
okno = pygame.display.set_mode((SIRKA_OKNA, VYSKA_OKNA))
# Nastavíme text, který uvidíme nahoře v liště okna
pygame.display.set_caption("Pexeso (Hledání barevných párů)")
# Založíme si herní "hodiny" - pomohou nám udržovat hru v rychlosti stanovené přes FPS
hodiny = pygame.time.Clock()
# Fonty slouží pro vykreslování textu. Načteme si dva různé.
font = pygame.font.SysFont("arial", 40, bold=True)
font_maly = pygame.font.SysFont("arial", 20)
# --- Třída objektu - objektově orientované programování (OOP) ---
# Třída je jako "plánek" na vytvoření konkrétních věcí. Zde je to plánek na vytvoření "Kartičky".
# Každá kartička si bude sama pamatovat, jakou má barvu a jestli je už otočená.
class Karticka:
def __init__(self, radek, sloupec, barva_vnitrku):
self.barva = barva_vnitrku # Tato barva bude vidět, když se karta otočí
# Stavové proměnné (True = ano, False = ne)
self.odkryta = False # Zda ji hráč v tomto tahu právě teď otočil
self.nalezena = False # Zda už hráč našel její pár a karta je tak z kola venku
# Matematika! Vypočítáme přesnou pozici X a Y (v pixelech), kam se tato karta nakreslí.
self.x = Odsazeni_x + sloupec * (KARTICKA_VELIKOST + MEZERA)
self.y = Odsazeni_y + radek * (KARTICKA_VELIKOST + MEZERA)
# pygame.Rect vytvoří "neviditelný obdélník" kolem naší kartičky.
# Díky němu se mnohem snadněji zjišťuje, jestli na kartičku hráč kliknul myší.
self.rect = pygame.Rect(self.x, self.y, KARTICKA_VELIKOST, KARTICKA_VELIKOST)
def vykresli(self):
# Funkce (metoda), která zajistí, že se karta správně namaluje na obrazovku
if self.odkryta or self.nalezena:
# Pokud je karta odkrytá (otočená), namalujeme její tajnou barvu
pygame.draw.rect(okno, self.barva, self.rect)
else:
# Pokud karta leží rubem nahoru, namalujeme ji šedou (nebo jakýkoliv jiný vzor chceme)
pygame.draw.rect(okno, SEDA, self.rect)
# Kromě výplně nakreslíme ještě bílý obrys (rámeček) o tloušťce 3 pixely, ať to lépe vypadá
pygame.draw.rect(okno, BILA, self.rect, 3)
def hlavni_smycka():
# --- PŘÍPRAVA HRY ---
# 1. Připravíme si seznam barev. Chceme 8 párů, takže každou barvu z BARVY_PARU potřebujeme dvakrát.
barvy_do_hry = BARVY_PARU * 2
# Modul random má funkci shuffle, která pořadí položek v seznamu krásně zamíchá.
random.shuffle(barvy_do_hry)
# 2. Vytvoření "mřížky" kartiček (2D pole, tedy seznam v seznamu)
karticky = []
index_barvy = 0 # Slouží k tomu, abychom z listu barvy_do_hry postupně brali barvy
for radek in range(RADKY):
rada = [] # Vytvoříme prázdný seznam pro jeden konkrétní řádek
for sloupec in range(SLOUPCE):
# Pro každé políčko vytvoříme nový objekt podle našeho "plánku" Karticka
karta = Karticka(radek, sloupec, barvy_do_hry[index_barvy])
rada.append(karta) # Kartu přidáme do řádku
index_barvy += 1
karticky.append(rada) # Celý naplněný řádek přidáme do hlavní mřížky
# Proměnné pro systém hry - pamatujeme si, co hráč naklikal v aktuálním tahu
prvni_vybrana = None
druha_vybrana = None
pocet_pokusu = 0
nalezeno_paru = 0
# --- HLAVNÍ HERNÍ SMYČKA ---
# Tento cyklus 'while True' běží pořád dokola, stokrát za sekundu a tvoří samotnou hru
while True:
# 1. ZPRACOVÁNÍ UDÁLOSTÍ (Event handling)
# Události jsou vstupy od hráče: stisknutí klávesy, kliknutí myší, zavření okna křížkem...
for udalost in pygame.event.get():
# Pokud hráč klikl na červený křížek okna, ukončíme Pygame a celou aplikaci
if udalost.type == pygame.QUIT:
pygame.quit()
sys.exit()
# Kontrola kliknutí myší (MOUSEBUTTONDOWN = tlačítko zmáčknuto dolů, button 1 = levé tlačítko)
if udalost.type == pygame.MOUSEBUTTONDOWN and udalost.button == 1:
# Pokud už hráč v tomto tahu otočil dvě karty a čekáme, zabráníme mu klikat dál!
if prvni_vybrana is not None and druha_vybrana is not None:
continue # "continue" přeskočí zbytek kódu v této smyčce a nedovolí mu kartu otočit
# Kde přesně se myš nachází? Souřadnice X,Y na obrazovce.
mys_x, mys_y = pygame.mouse.get_pos()
# Projdeme všechny naše karty jednu po druhé a zkontrolujeme, zda hráč neklikl na některou z nich
for radek in range(RADKY):
for sloupec in range(SLOUPCE):
karta = karticky[radek][sloupec]
# Metoda collidepoint zjistí, zda se bod (myš) nachází uvnitř obdélníku (karty)
if karta.rect.collidepoint(mys_x, mys_y):
# Otočit můžeme jen kartu, která ještě NENÍ otočená
if not karta.odkryta and not karta.nalezena:
karta.odkryta = True # Hráč ji otočil!
# Systém logiky: Byla tohle první karta v tahu?
if prvni_vybrana is None:
prvni_vybrana = karta
else:
# Nebyla, takže to už musí být druhá karta v tahu.
druha_vybrana = karta
pocet_pokusu += 1 # Tah končí, započítáme pokus
# 2. VYKRESLOVÁNÍ (Kreslíme nový "snímek" - frame)
# Nejdřív vždy smažeme starý obraz tím, že celé okno přebarvíme jednolitou barvou
okno.fill(TMAVE_MODRA)
# Projdeme mřížku a řekneme každé kartičce, aby se nakreslila na okno
async def main(): # PŘIDÁNO PRO WEB: Zabalíme celou hru do asynchronní funkce
# pygame.init() nastartuje všechny vnitřní moduly Pygame.
# Je to naprosto povinný první krok před tím, než můžeme v Pygame cokoliv udělat.
pygame.init()
# --- Konstanty okna a rozvržení mřížky ---
# Konstanty (velkými písmeny) se během hry nemění. Určují základní vlastnosti naší hry.
SIRKA_OKNA = 600
VYSKA_OKNA = 600
FPS = 30 # Počet snímků za sekundu (jak rychle se hra překresluje). 30 bohatě stačí pro Pexeso.
# Kolik karet chceme mít na šířku a na výšku?
RADKY = 4
SLOUPCE = 4
# Velikost jedné karty v pixelech (obrazových bodech)
KARTICKA_VELIKOST = 100
# Mezera mezi kartičkami
MEZERA = 20
# Výpočet celkové šířky a výšky celé mřížky karet, abychom ji pak mohli na obrazovce vycentrovat
Mrizka_sirka = (SLOUPCE * KARTICKA_VELIKOST) + ((SLOUPCE - 1) * MEZERA)
Mrizka_vyska = (RADKY * KARTICKA_VELIKOST) + ((RADKY - 1) * MEZERA)
# Vypočítáme, kde mřížka začíná na osách X (vodorovně) a Y (svisle), aby byla přesně uprostřed okna
Odsazeni_x = (SIRKA_OKNA - Mrizka_sirka) // 2
Odsazeni_y = (VYSKA_OKNA - Mrizka_vyska) // 2
# Barvy v Pygame se zadávají ve formátu (R, G, B) = Red, Green, Blue.
# Každá hodnota je od 0 (nic) do 255 (maximum dané barvy).
BILA = (255, 255, 255)
SEDA = (150, 150, 150)
TMAVE_MODRA = (20, 20, 80)
ZELENA_TEXT = (50, 255, 50)
# Budeme mít celkem 16 karet (4x4), což znamená 8 různých párů (8 unikátních barev).
BARVY_PARU = [
(255, 0, 0), # Červená
(0, 255, 0), # Světle Zelená
(0, 0, 255), # Modrá
(255, 255, 0), # Žlutá
(255, 0, 255), # Fialová
(0, 255, 255), # Azurová
(255, 165, 0), # Oranžová
(139, 69, 19) # Hnědá
]
# Vytvoříme samotné okno (obrazovku), kam se bude vše vykreslovat
okno = pygame.display.set_mode((SIRKA_OKNA, VYSKA_OKNA))
# Nastavíme text, který uvidíme nahoře v liště okna
pygame.display.set_caption("Pexeso (Hledání barevných párů)")
# Založíme si herní "hodiny" - pomohou nám udržovat hru v rychlosti stanovené přes FPS
hodiny = pygame.time.Clock()
# Fonty slouží pro vykreslování textu. Načteme si dva různé.
font = pygame.font.SysFont("arial", 40, bold=True)
font_maly = pygame.font.SysFont("arial", 20)
# --- Třída objektu - objektově orientované programování (OOP) ---
# Třída je jako "plánek" na vytvoření konkrétních věcí. Zde je to plánek na vytvoření "Kartičky".
# Každá kartička si bude sama pamatovat, jakou má barvu a jestli je už otočená.
class Karticka:
def __init__(self, radek, sloupec, barva_vnitrku):
self.barva = barva_vnitrku # Tato barva bude vidět, když se karta otočí
# Stavové proměnné (True = ano, False = ne)
self.odkryta = False # Zda ji hráč v tomto tahu právě teď otočil
self.nalezena = False # Zda už hráč našel její pár a karta je tak z kola venku
# Matematika! Vypočítáme přesnou pozici X a Y (v pixelech), kam se tato karta nakreslí.
self.x = Odsazeni_x + sloupec * (KARTICKA_VELIKOST + MEZERA)
self.y = Odsazeni_y + radek * (KARTICKA_VELIKOST + MEZERA)
# pygame.Rect vytvoří "neviditelný obdélník" kolem naší kartičky.
# Díky němu se mnohem snadněji zjišťuje, jestli na kartičku hráč kliknul myší.
self.rect = pygame.Rect(self.x, self.y, KARTICKA_VELIKOST, KARTICKA_VELIKOST)
def vykresli(self):
# Funkce (metoda), která zajistí, že se karta správně namaluje na obrazovku
if self.odkryta or self.nalezena:
# Pokud je karta odkrytá (otočená), namalujeme její tajnou barvu
pygame.draw.rect(okno, self.barva, self.rect)
else:
# Pokud karta leží rubem nahoru, namalujeme ji šedou (nebo jakýkoliv jiný vzor chceme)
pygame.draw.rect(okno, SEDA, self.rect)
# Kromě výplně nakreslíme ještě bílý obrys (rámeček) o tloušťce 3 pixely, ať to lépe vypadá
pygame.draw.rect(okno, BILA, self.rect, 3)
def hlavni_smycka():
# --- PŘÍPRAVA HRY ---
# 1. Připravíme si seznam barev. Chceme 8 párů, takže každou barvu z BARVY_PARU potřebujeme dvakrát.
barvy_do_hry = BARVY_PARU * 2
# Modul random má funkci shuffle, která pořadí položek v seznamu krásně zamíchá.
random.shuffle(barvy_do_hry)
# 2. Vytvoření "mřížky" kartiček (2D pole, tedy seznam v seznamu)
karticky = []
index_barvy = 0 # Slouží k tomu, abychom z listu barvy_do_hry postupně brali barvy
for radek in range(RADKY): for radek in range(RADKY):
rada = [] # Vytvoříme prázdný seznam pro jeden konkrétní řádek
for sloupec in range(SLOUPCE): for sloupec in range(SLOUPCE):
karticky[radek][sloupec].vykresli()
# Vykreslíme text s počtem pokusů. Funkce render vytvoří z textu "obrázek", který pak můžeme vykreslit
text_tahy = font_maly.render(f"Počet pokusů: {pocet_pokusu}", True, BILA)
# blit je příkaz pro "nalep tento obrázek na dané souřadnice"
okno.blit(text_tahy, (10, 10))
# Zkontrolujeme vítězství
if nalezeno_paru == 8: # Maximum možných párů je 8
text_konec = font.render("Vítězství! Skvělá paměť.", True, ZELENA_TEXT)
# Center zarovná text hezky doprostřed požadované X pozice
rect = text_konec.get_rect(center=(SIRKA_OKNA // 2, VYSKA_OKNA - 30))
okno.blit(text_konec, rect)
# ZÁSADNÍ VĚC: Po tom co jsme do paměti Pygame naskládali příkazy kreslení,
# musíme to všechno najednou zviditelnit na obrazovce pomocí příkazu flip() nebo update()!
# Děláme to už teď, abychom (pokud budeme dole hru pauzovat), viděli otočenou druhou kartu.
pygame.display.flip()
# 3. HERNÍ LOGIKA: Vyhodnocení tahu
# Pokud už hráč vybral dvě karty, musíme zjistit, jestli udělal pár
if prvni_vybrana is not None and druha_vybrana is not None:
if prvni_vybrana.barva == druha_vybrana.barva:
# SUPER! Barvy se shodují, je to PÁR!
prvni_vybrana.nalezena = True
druha_vybrana.nalezena = True
nalezeno_paru += 1
else:
# ŠPATNĚ! Barvy se liší.
# Aby hráč vůbec stihl zaregistrovat barvu druhé karty, musíme na chvíli zmrazit hru
# wait(1000) hru zastaví přesně na 1000 milisekund (1 sekundu)
pygame.time.wait(1000)
# A pak obě karty otočíme zase lícem dolů (šedá strana nahoru)
prvni_vybrana.odkryta = False
druha_vybrana.odkryta = False
# Tah je u konce, ať už to byl pár nebo ne. Vyčistíme výběr pro další tah!
prvni_vybrana = None
druha_vybrana = None
# Rychlost smyčky: Řekneme hodinám, že chceme, aby smyčka běžela maximálně tolikrát za vteřinu, jak určuje FPS
hodiny.tick(FPS)
# Tohle říká Pythonu: Pokud tento soubor spouštíš jako hlavní program (ne jen importuješ),
# tak teprve tehdy spusť hlani_smycka()
if __name__ == "__main__":
hlavni_smycka()
# Pro každé políčko vytvoříme nový objekt podle našeho "plánku" Karticka
karta = Karticka(radek, sloupec, barvy_do_hry[index_barvy])
rada.append(karta) # Kartu přidáme do řádku
index_barvy += 1
karticky.append(rada) # Celý naplněný řádek přidáme do hlavní mřížky
# Proměnné pro systém hry - pamatujeme si, co hráč naklikal v aktuálním tahu
prvni_vybrana = None
druha_vybrana = None
pocet_pokusu = 0
nalezeno_paru = 0
# --- HLAVNÍ HERNÍ SMYČKA ---
# Tento cyklus 'while True' běží pořád dokola, stokrát za sekundu a tvoří samotnou hru
while True:
# 1. ZPRACOVÁNÍ UDÁLOSTÍ (Event handling)
# Události jsou vstupy od hráče: stisknutí klávesy, kliknutí myší, zavření okna křížkem...
for udalost in pygame.event.get():
# Pokud hráč klikl na červený křížek okna, ukončíme Pygame a celou aplikaci
if udalost.type == pygame.QUIT:
pygame.quit()
sys.exit()
# Kontrola kliknutí myší (MOUSEBUTTONDOWN = tlačítko zmáčknuto dolů, button 1 = levé tlačítko)
if udalost.type == pygame.MOUSEBUTTONDOWN and udalost.button == 1:
# Pokud už hráč v tomto tahu otočil dvě karty a čekáme, zabráníme mu klikat dál!
if prvni_vybrana is not None and druha_vybrana is not None:
continue # "continue" přeskočí zbytek kódu v této smyčce a nedovolí mu kartu otočit
# Kde přesně se myš nachází? Souřadnice X,Y na obrazovce.
mys_x, mys_y = pygame.mouse.get_pos()
# Projdeme všechny naše karty jednu po druhé a zkontrolujeme, zda hráč neklikl na některou z nich
for radek in range(RADKY):
for sloupec in range(SLOUPCE):
karta = karticky[radek][sloupec]
# Metoda collidepoint zjistí, zda se bod (myš) nachází uvnitř obdélníku (karty)
if karta.rect.collidepoint(mys_x, mys_y):
# Otočit můžeme jen kartu, která ještě NENÍ otočená
if not karta.odkryta and not karta.nalezena:
karta.odkryta = True # Hráč ji otočil!
# Systém logiky: Byla tohle první karta v tahu?
if prvni_vybrana is None:
prvni_vybrana = karta
else:
# Nebyla, takže to už musí být druhá karta v tahu.
druha_vybrana = karta
pocet_pokusu += 1 # Tah končí, započítáme pokus
# 2. VYKRESLOVÁNÍ (Kreslíme nový "snímek" - frame)
# Nejdřív vždy smažeme starý obraz tím, že celé okno přebarvíme jednolitou barvou
okno.fill(TMAVE_MODRA)
# Projdeme mřížku a řekneme každé kartičce, aby se nakreslila na okno
for radek in range(RADKY):
for sloupec in range(SLOUPCE):
karticky[radek][sloupec].vykresli()
# Vykreslíme text s počtem pokusů. Funkce render vytvoří z textu "obrázek", který pak můžeme vykreslit
text_tahy = font_maly.render(f"Počet pokusů: {pocet_pokusu}", True, BILA)
# blit je příkaz pro "nalep tento obrázek na dané souřadnice"
okno.blit(text_tahy, (10, 10))
# Zkontrolujeme vítězství
if nalezeno_paru == 8: # Maximum možných párů je 8
text_konec = font.render("Vítězství! Skvělá paměť.", True, ZELENA_TEXT)
# Center zarovná text hezky doprostřed požadované X pozice
rect = text_konec.get_rect(center=(SIRKA_OKNA // 2, VYSKA_OKNA - 30))
okno.blit(text_konec, rect)
# ZÁSADNÍ VĚC: Po tom co jsme do paměti Pygame naskládali příkazy kreslení,
# musíme to všechno najednou zviditelnit na obrazovce pomocí příkazu flip() nebo update()!
# Děláme to už teď, abychom (pokud budeme dole hru pauzovat), viděli otočenou druhou kartu.
pygame.display.flip()
# 3. HERNÍ LOGIKA: Vyhodnocení tahu
# Pokud už hráč vybral dvě karty, musíme zjistit, jestli udělal pár
if prvni_vybrana is not None and druha_vybrana is not None:
if prvni_vybrana.barva == druha_vybrana.barva:
# SUPER! Barvy se shodují, je to PÁR!
prvni_vybrana.nalezena = True
druha_vybrana.nalezena = True
nalezeno_paru += 1
else:
# ŠPATNĚ! Barvy se liší.
# Aby hráč vůbec stihl zaregistrovat barvu druhé karty, musíme na chvíli zmrazit hru
# wait(1000) hru zastaví přesně na 1000 milisekund (1 sekundu)
pygame.time.wait(1000)
# A pak obě karty otočíme zase lícem dolů (šedá strana nahoru)
prvni_vybrana.odkryta = False
druha_vybrana.odkryta = False
# Tah je u konce, ať už to byl pár nebo ne. Vyčistíme výběr pro další tah!
prvni_vybrana = None
druha_vybrana = None
# Rychlost smyčky: Řekneme hodinám, že chceme, aby smyčka běžela maximálně tolikrát za vteřinu, jak určuje FPS
hodiny.tick(FPS)
# PŘIDÁNO PRO WEB: Dáme prohlížeči šanci překreslit obrazovku
await asyncio.sleep(0)
# Tohle říká Pythonu: Pokud tento soubor spouštíš jako hlavní program (ne jen importuješ),
# tak teprve tehdy spusť hlani_smycka()
if __name__ == "__main__":
hlavni_smycka()
# PŘIDÁNO PRO WEB: Spuštění asynchronní hry
asyncio.run(main())

382
pygame/02_flappy_bird.py

@ -1,194 +1,202 @@
import pygame import pygame
import asyncio # PŘIDÁNO PRO WEB: importujeme asyncio pro neblokující smyčku
import random import random
import sys import sys
# pygame.init() připraví knihovnu Pygame k použití. Volat to musíme vždy jako první věc!
pygame.init()
# --- Nastavení okna a základních konstant ---
# Konstanty nepíšeme malými písmeny, ale VELKÝMI, abychom zdůraznili, že se jejich hodnota za běhu nemění.
SIRKA_OKNA = 400
VYSKA_OKNA = 600
FPS = 60 # FPS (Frames Per Second) = kolikrát se celá obrazovka překreslí za vteřinu. 60 je hezky plynulé.
# Barvy zapisujeme jako mix tří kanálů: (Červená, Zelená, Modrá) - každý má hodnotu 0 až 255.
# (0,0,0) je černá (žádná barva), (255,255,255) je bílá (plné všechny barvy).
CERNA = (0, 0, 0)
BILA = (255, 255, 255)
MODRA_OBLOHA = (135, 206, 235)
ZELENA = (34, 139, 34)
ZLUTYP_PTAK = (255, 215, 0)
CERVENA = (255, 0, 0)
# Vytvoření okna (plocha, kam se bude vše vykreslovat)
okno = pygame.display.set_mode((SIRKA_OKNA, VYSKA_OKNA))
pygame.display.set_caption("Flappy Bird") # Nápis v horní liště programu
# Herní hodiny pro řízení rychlosti smyčky
hodiny = pygame.time.Clock()
# Tvorba písem. font_velky použijeme na herní nápisy (Konec hry apod.), font_maly na menší text.
font_velky = pygame.font.SysFont("arial", 48, bold=True)
font_maly = pygame.font.SysFont("arial", 24)
# Pomocná funkce pro snazší kreslení textu. Abychom nemuseli pokaždé opakovat stejné složité příkazy.
def zobraz_text(text, font, barva, x, y, zarovnat_stred=False):
# render() převede text ze znaků do obrázku (tzv. "Surface"), který lze následně nakreslit.
plocha = font.render(text, True, barva)
if zarovnat_stred:
# Vypočítáme obdélník (rect) obrázku textu a řekneme, ať je jeho STŘED (center) na dané X a Y pozici
rect = plocha.get_rect(center=(x, y))
okno.blit(plocha, rect) # blit() nalepí obrázek plochy na 'okno'
else:
# Jinak ho plácneme normálně za jeho levý horní roh
okno.blit(plocha, (x, y))
def hlavni_smycka():
# --- PROMĚNNÉ PTÁKA ---
# Toto jsou proměnné, které se v čase mění, proto nejsou VELKÝMI PÍSMENY
ptak_x = 50 # Zleva je pták relativně blízko okraji
ptak_y = VYSKA_OKNA // 2 # Vertikálně (Y) ho položíme doprostřed okna
ptak_sirka = 30
ptak_vyska = 30
# Fyzika
rychlost_padu = 0 # Zpočátku stojí pták na místě
gravitace = 0.5 # Gravitace každým snímkem tuto 'rychlost_padu' zvyšuje směrem DOLŮ (kladné hodnoty Y)
sila_skoku = -8 # Skok je prostě náhlé nastavení rychlosti do záporných hodnot, tedy NAHORU!
# --- PROMĚNNÉ PŘEKÁŽEK (ZELENÉ TRUBKY) ---
trubka_sirka = 60
mezera_mezi_trubkami = 160 # Jak velikou skulinou bude hráč létat? (v pixelech)
rychlost_posunu = 3 # Překážky neustále jedou směrem doleva, aby to vypadalo, že pták letí dopředu
# Seznam do kterého budeme ukládat trubky. Použijeme slovníky {ključ: hodnota}.
# Např: {"x": 400, "y_stred": 300} ... 'y_stred' určuje, kde přesně je střed oné mezery kudy se letí
trubky = []
# Funkce k přidání nové trubky na určené X ose
def pridej_trubku(x_pozice):
# Nechceme mezeru úplně na kraji (nemožné projet), omezíme ji od 150px nahoře po 150px dole
stred_mezery = random.randint(150, VYSKA_OKNA - 150)
trubky.append({"x": x_pozice, "y_stred": stred_mezery})
# Přidáme první dvě trubky už před startem hry, nacházejí se zprvu mimo okno vpravo.
pridej_trubku(SIRKA_OKNA + 100)
pridej_trubku(SIRKA_OKNA + 400) # Další trubka má odstup 300 pixelů
skore = 0
# STAVOVÝ AUTOMAT - skvělý trik v programování. Znalost toho "ve kterém jsme stavu"
# nám pomáhá měnit logiku. Hra se chová úplně jinak, když jsme v MENU než když právě HRAJEME.
stav_hry = "START" # Možnosti: "START", "HRA", "KONEC"
# --- HERNÍ SMYČKA ---
while True:
# 1. ČTENÍ VSTUPŮ (UDÁLOSTI)
for udalost in pygame.event.get():
if udalost.type == pygame.QUIT:
pygame.quit() # Korektně ukončí knihovnu Pygame
sys.exit() # Ukončí celý Python skript
# Pokud někdo zmáčkl klávesu na klávesnici (KEYDOWN)
if udalost.type == pygame.KEYDOWN:
if udalost.key == pygame.K_SPACE: # Speciálně zkoumáme MEZERNÍK
# Co dělá mezerník záleží na tom, v jakém STAVU je hra:
if stav_hry == "START":
stav_hry = "HRA" # Přepneme hru na aktivní
rychlost_padu = sila_skoku # Hned v první sekundě mu dáme skokový impuls
elif stav_hry == "HRA":
rychlost_padu = sila_skoku # Hráč skočil! (Pták vzletí nahoru)
elif stav_hry == "KONEC":
return # Vyskočíme z této funkce, což vyústí ve spuštění znova (viz úplně dole v kódu)
# 2. LOGIKA A POHYB
# Zpracovává se jen pokud jsme ve stavu HRA
if stav_hry == "HRA":
# Aplikace gravitace: Rychlost pádu je stále vyšší kladné číslo
rychlost_padu += gravitace
# Přičtením této rychlosti k souřadnici Y pták pomalu (a čím dál rychleji) "padá" na zem okna
ptak_y += rychlost_padu
# Pohyb trubek doleva (zmenšujeme jejich osu X)
async def main(): # PŘIDÁNO PRO WEB: Zabalíme celou hru do asynchronní funkce
# pygame.init() připraví knihovnu Pygame k použití. Volat to musíme vždy jako první věc!
pygame.init()
# --- Nastavení okna a základních konstant ---
# Konstanty nepíšeme malými písmeny, ale VELKÝMI, abychom zdůraznili, že se jejich hodnota za běhu nemění.
SIRKA_OKNA = 400
VYSKA_OKNA = 600
FPS = 60 # FPS (Frames Per Second) = kolikrát se celá obrazovka překreslí za vteřinu. 60 je hezky plynulé.
# Barvy zapisujeme jako mix tří kanálů: (Červená, Zelená, Modrá) - každý má hodnotu 0 až 255.
# (0,0,0) je černá (žádná barva), (255,255,255) je bílá (plné všechny barvy).
CERNA = (0, 0, 0)
BILA = (255, 255, 255)
MODRA_OBLOHA = (135, 206, 235)
ZELENA = (34, 139, 34)
ZLUTYP_PTAK = (255, 215, 0)
CERVENA = (255, 0, 0)
# Vytvoření okna (plocha, kam se bude vše vykreslovat)
okno = pygame.display.set_mode((SIRKA_OKNA, VYSKA_OKNA))
pygame.display.set_caption("Flappy Bird") # Nápis v horní liště programu
# Herní hodiny pro řízení rychlosti smyčky
hodiny = pygame.time.Clock()
# Tvorba písem. font_velky použijeme na herní nápisy (Konec hry apod.), font_maly na menší text.
font_velky = pygame.font.SysFont("arial", 48, bold=True)
font_maly = pygame.font.SysFont("arial", 24)
# Pomocná funkce pro snazší kreslení textu. Abychom nemuseli pokaždé opakovat stejné složité příkazy.
def zobraz_text(text, font, barva, x, y, zarovnat_stred=False):
# render() převede text ze znaků do obrázku (tzv. "Surface"), který lze následně nakreslit.
plocha = font.render(text, True, barva)
if zarovnat_stred:
# Vypočítáme obdélník (rect) obrázku textu a řekneme, ať je jeho STŘED (center) na dané X a Y pozici
rect = plocha.get_rect(center=(x, y))
okno.blit(plocha, rect) # blit() nalepí obrázek plochy na 'okno'
else:
# Jinak ho plácneme normálně za jeho levý horní roh
okno.blit(plocha, (x, y))
def hlavni_smycka():
# --- PROMĚNNÉ PTÁKA ---
# Toto jsou proměnné, které se v čase mění, proto nejsou VELKÝMI PÍSMENY
ptak_x = 50 # Zleva je pták relativně blízko okraji
ptak_y = VYSKA_OKNA // 2 # Vertikálně (Y) ho položíme doprostřed okna
ptak_sirka = 30
ptak_vyska = 30
# Fyzika
rychlost_padu = 0 # Zpočátku stojí pták na místě
gravitace = 0.5 # Gravitace každým snímkem tuto 'rychlost_padu' zvyšuje směrem DOLŮ (kladné hodnoty Y)
sila_skoku = -8 # Skok je prostě náhlé nastavení rychlosti do záporných hodnot, tedy NAHORU!
# --- PROMĚNNÉ PŘEKÁŽEK (ZELENÉ TRUBKY) ---
trubka_sirka = 60
mezera_mezi_trubkami = 160 # Jak velikou skulinou bude hráč létat? (v pixelech)
rychlost_posunu = 3 # Překážky neustále jedou směrem doleva, aby to vypadalo, že pták letí dopředu
# Seznam do kterého budeme ukládat trubky. Použijeme slovníky {ključ: hodnota}.
# Např: {"x": 400, "y_stred": 300} ... 'y_stred' určuje, kde přesně je střed oné mezery kudy se letí
trubky = []
# Funkce k přidání nové trubky na určené X ose
def pridej_trubku(x_pozice):
# Nechceme mezeru úplně na kraji (nemožné projet), omezíme ji od 150px nahoře po 150px dole
stred_mezery = random.randint(150, VYSKA_OKNA - 150)
trubky.append({"x": x_pozice, "y_stred": stred_mezery})
# Přidáme první dvě trubky už před startem hry, nacházejí se zprvu mimo okno vpravo.
pridej_trubku(SIRKA_OKNA + 100)
pridej_trubku(SIRKA_OKNA + 400) # Další trubka má odstup 300 pixelů
skore = 0
# STAVOVÝ AUTOMAT - skvělý trik v programování. Znalost toho "ve kterém jsme stavu"
# nám pomáhá měnit logiku. Hra se chová úplně jinak, když jsme v MENU než když právě HRAJEME.
stav_hry = "START" # Možnosti: "START", "HRA", "KONEC"
# --- HERNÍ SMYČKA ---
while True:
# 1. ČTENÍ VSTUPŮ (UDÁLOSTI)
for udalost in pygame.event.get():
if udalost.type == pygame.QUIT:
pygame.quit() # Korektně ukončí knihovnu Pygame
sys.exit() # Ukončí celý Python skript
# Pokud někdo zmáčkl klávesu na klávesnici (KEYDOWN)
if udalost.type == pygame.KEYDOWN:
if udalost.key == pygame.K_SPACE: # Speciálně zkoumáme MEZERNÍK
# Co dělá mezerník záleží na tom, v jakém STAVU je hra:
if stav_hry == "START":
stav_hry = "HRA" # Přepneme hru na aktivní
rychlost_padu = sila_skoku # Hned v první sekundě mu dáme skokový impuls
elif stav_hry == "HRA":
rychlost_padu = sila_skoku # Hráč skočil! (Pták vzletí nahoru)
elif stav_hry == "KONEC":
return # Vyskočíme z této funkce, což vyústí ve spuštění znova (viz úplně dole v kódu)
# 2. LOGIKA A POHYB
# Zpracovává se jen pokud jsme ve stavu HRA
if stav_hry == "HRA":
# Aplikace gravitace: Rychlost pádu je stále vyšší kladné číslo
rychlost_padu += gravitace
# Přičtením této rychlosti k souřadnici Y pták pomalu (a čím dál rychleji) "padá" na zem okna
ptak_y += rychlost_padu
# Pohyb trubek doleva (zmenšujeme jejich osu X)
for t in trubky:
t["x"] -= rychlost_posunu
# Kontrola první trubky v seznamu: Nevyjela nám náhodou úplně ven z okna doleva?
if trubky[0]["x"] < -trubka_sirka:
trubky.pop(0) # Odstraníme první trubku ze seznamu, už ji nikdy neuvidíme
# Zjistíme, kde byla zhruba naposled vygenerovaná trubka, a přidáme novou ZA ni
posledni_trubka_x = trubky[-1]["x"]
pridej_trubku(posledni_trubka_x + 300)
# Pokud trubka odjede ven, znamená to, že ji hráč musel úspěšně přeletět!
skore += 1
# --- Detekce nárazů (KOLIZE) ---
# Pro kontrolu jestli pták do něčeho vrazil používáme pygame.Rect (neviditelné hranice objektů)
ptak_rect = pygame.Rect(ptak_x, ptak_y, ptak_sirka, ptak_vyska)
# 1) Náraz do stropu nebo do podlahy okna
if ptak_y < 0 or ptak_y + ptak_vyska > VYSKA_OKNA:
stav_hry = "KONEC" # Bum! Umřel.
# 2) Náraz do jakékoliv překážky
for t in trubky:
# Vypočteme, kde končí 'horní' překážka
horni_vyska = t["y_stred"] - (mezera_mezi_trubkami // 2)
horni_rect = pygame.Rect(t["x"], 0, trubka_sirka, horni_vyska)
# Vypočteme, kde začíná 'spodní' překážka (od středu + polovina mezery)
dolni_y = t["y_stred"] + (mezera_mezi_trubkami // 2)
# Výška spodní části je VYSKA_OKNA mínus počáteční Y souřadnice spodní trubky
dolni_rect = pygame.Rect(t["x"], dolni_y, trubka_sirka, VYSKA_OKNA - dolni_y)
# Pokud se "obdélník ptáka" dotýká / prolíná (colliderect) s horní nebo dolní trubkou
if ptak_rect.colliderect(horni_rect) or ptak_rect.colliderect(dolni_rect):
stav_hry = "KONEC"
# 3. KRESLENÍ (na 'platno')
# Smažeme stopu ze starého snímku překrytím čistou oblohou
okno.fill(MODRA_OBLOHA)
# Nakreslení obou překážek
for t in trubky: for t in trubky:
t["x"] -= rychlost_posunu
# Kontrola první trubky v seznamu: Nevyjela nám náhodou úplně ven z okna doleva?
if trubky[0]["x"] < -trubka_sirka:
trubky.pop(0) # Odstraníme první trubku ze seznamu, už ji nikdy neuvidíme
# Zjistíme, kde byla zhruba naposled vygenerovaná trubka, a přidáme novou ZA ni
posledni_trubka_x = trubky[-1]["x"]
pridej_trubku(posledni_trubka_x + 300)
# Pokud trubka odjede ven, znamená to, že ji hráč musel úspěšně přeletět!
skore += 1
# --- Detekce nárazů (KOLIZE) ---
# Pro kontrolu jestli pták do něčeho vrazil používáme pygame.Rect (neviditelné hranice objektů)
ptak_rect = pygame.Rect(ptak_x, ptak_y, ptak_sirka, ptak_vyska)
# 1) Náraz do stropu nebo do podlahy okna
if ptak_y < 0 or ptak_y + ptak_vyska > VYSKA_OKNA:
stav_hry = "KONEC" # Bum! Umřel.
# 2) Náraz do jakékoliv překážky
for t in trubky:
# Vypočteme, kde končí 'horní' překážka
# Horní trubka vykreslena od Y=0 (strop)
horni_vyska = t["y_stred"] - (mezera_mezi_trubkami // 2) horni_vyska = t["y_stred"] - (mezera_mezi_trubkami // 2)
horni_rect = pygame.Rect(t["x"], 0, trubka_sirka, horni_vyska)
# Vypočteme, kde začíná 'spodní' překážka (od středu + polovina mezery)
pygame.draw.rect(okno, ZELENA, (t["x"], 0, trubka_sirka, horni_vyska))
# Dolní trubka vykreslena od konce mezery (dolni_y) až dolu k podlaze
dolni_y = t["y_stred"] + (mezera_mezi_trubkami // 2) dolni_y = t["y_stred"] + (mezera_mezi_trubkami // 2)
# Výška spodní části je VYSKA_OKNA mínus počáteční Y souřadnice spodní trubky
dolni_rect = pygame.Rect(t["x"], dolni_y, trubka_sirka, VYSKA_OKNA - dolni_y)
# Pokud se "obdélník ptáka" dotýká / prolíná (colliderect) s horní nebo dolní trubkou
if ptak_rect.colliderect(horni_rect) or ptak_rect.colliderect(dolni_rect):
stav_hry = "KONEC"
# 3. KRESLENÍ (na 'platno')
# Smažeme stopu ze starého snímku překrytím čistou oblohou
okno.fill(MODRA_OBLOHA)
# Nakreslení obou překážek
for t in trubky:
# Horní trubka vykreslena od Y=0 (strop)
horni_vyska = t["y_stred"] - (mezera_mezi_trubkami // 2)
pygame.draw.rect(okno, ZELENA, (t["x"], 0, trubka_sirka, horni_vyska))
# Dolní trubka vykreslena od konce mezery (dolni_y) až dolu k podlaze
dolni_y = t["y_stred"] + (mezera_mezi_trubkami // 2)
pygame.draw.rect(okno, ZELENA, (t["x"], dolni_y, trubka_sirka, VYSKA_OKNA - dolni_y))
# Nakreslení postavy (Ptáka)
# int() ořezává desetinná čísla z fyziky do celých čísel pixelů, aby to šlo nakreslit
pygame.draw.rect(okno, ZLUTYP_PTAK, (ptak_x, int(ptak_y), ptak_sirka, ptak_vyska))
# --- ZOBRAZOVÁNÍ NÁPISŮ (UI) ---
if stav_hry == "START":
zobraz_text("Stiskni MEZERNÍK", font_maly, BILA, SIRKA_OKNA//2, VYSKA_OKNA//2 - 50, zarovnat_stred=True)
zobraz_text("pro začátek létání", font_maly, BILA, SIRKA_OKNA//2, VYSKA_OKNA//2, zarovnat_stred=True)
elif stav_hry == "HRA":
# Skóre nahoře
zobraz_text(str(skore), font_velky, BILA, SIRKA_OKNA//2, 50, zarovnat_stred=True)
elif stav_hry == "KONEC":
# GAME OVER statistiky po smrti
zobraz_text("GAME OVER", font_velky, CERVENA, SIRKA_OKNA//2, VYSKA_OKNA//2 - 50, zarovnat_stred=True)
zobraz_text(f"Skóre: {skore}", font_maly, BILA, SIRKA_OKNA//2, VYSKA_OKNA//2 + 10, zarovnat_stred=True)
zobraz_text("Stiskni MEZERNÍK pro restart", font_maly, CERNA, SIRKA_OKNA//2, VYSKA_OKNA//2 + 50, zarovnat_stred=True)
# "Otoč" buffer z paměti na reálný monitor - tady se stane kouzlo a vše se zobrazí!
pygame.display.flip()
# Pojistka pro správnou rychlost. Počká se milisekundu tak, abychom dodrželi stanovené FPS.
hodiny.tick(FPS)
# Toto funguje tak, že hru spouštíme donekonečna. Když hráč zemře a dá mezerník,
# funkce 'hlavni_smycka()' skončí (return), ale díky tomuto cyklu 'while True'
# se obratem zavolá od znova z čistého stolu a se skórem nula.
if __name__ == "__main__":
while True:
hlavni_smycka()
pygame.draw.rect(okno, ZELENA, (t["x"], dolni_y, trubka_sirka, VYSKA_OKNA - dolni_y))
# Nakreslení postavy (Ptáka)
# int() ořezává desetinná čísla z fyziky do celých čísel pixelů, aby to šlo nakreslit
pygame.draw.rect(okno, ZLUTYP_PTAK, (ptak_x, int(ptak_y), ptak_sirka, ptak_vyska))
# --- ZOBRAZOVÁNÍ NÁPISŮ (UI) ---
if stav_hry == "START":
zobraz_text("Stiskni MEZERNÍK", font_maly, BILA, SIRKA_OKNA//2, VYSKA_OKNA//2 - 50, zarovnat_stred=True)
zobraz_text("pro začátek létání", font_maly, BILA, SIRKA_OKNA//2, VYSKA_OKNA//2, zarovnat_stred=True)
elif stav_hry == "HRA":
# Skóre nahoře
zobraz_text(str(skore), font_velky, BILA, SIRKA_OKNA//2, 50, zarovnat_stred=True)
elif stav_hry == "KONEC":
# GAME OVER statistiky po smrti
zobraz_text("GAME OVER", font_velky, CERVENA, SIRKA_OKNA//2, VYSKA_OKNA//2 - 50, zarovnat_stred=True)
zobraz_text(f"Skóre: {skore}", font_maly, BILA, SIRKA_OKNA//2, VYSKA_OKNA//2 + 10, zarovnat_stred=True)
zobraz_text("Stiskni MEZERNÍK pro restart", font_maly, CERNA, SIRKA_OKNA//2, VYSKA_OKNA//2 + 50, zarovnat_stred=True)
# "Otoč" buffer z paměti na reálný monitor - tady se stane kouzlo a vše se zobrazí!
pygame.display.flip()
# Pojistka pro správnou rychlost. Počká se milisekundu tak, abychom dodrželi stanovené FPS.
hodiny.tick(FPS)
# PŘIDÁNO PRO WEB: Dáme prohlížeči šanci překreslit obrazovku
await asyncio.sleep(0)
# Toto funguje tak, že hru spouštíme donekonečna. Když hráč zemře a dá mezerník,
# funkce 'hlavni_smycka()' skončí (return), ale díky tomuto cyklu 'while True'
# se obratem zavolá od znova z čistého stolu a se skórem nula.
if __name__ == "__main__":
while True:
hlavni_smycka()
# PŘIDÁNO PRO WEB: Spuštění asynchronní hry
asyncio.run(main())

278
pygame/03_chytani_jablek.py

@ -1,140 +1,148 @@
import pygame import pygame
import asyncio # PŘIDÁNO PRO WEB: importujeme asyncio pro neblokující smyčku
import sys import sys
import random import random
# Inicializace knihovny Pygame - povinný krok. Nastaví všechny podmoduly zvuků, kreslení atd. # Inicializace knihovny Pygame - povinný krok. Nastaví všechny podmoduly zvuků, kreslení atd.
pygame.init()
# --- Nastavení herního okna ---
SIRKA = 800
VYSKA = 600
# Vytvoření hlavního okna hry o dané velikosti v pixelech (obrazových bodech)
okno = pygame.display.set_mode((SIRKA, VYSKA))
# Přidáme název hry do horní lišty softwarového okna
pygame.display.set_caption("Chytání jablek")
# --- Paleta barev ---
# Barvy zadáváme přes číselné hodnoty Červené, Zelené a Modré - RGB (Red, Green, Blue)
CERNA = (0, 0, 0) # Černá je absence všech barev
ZELENA = (0, 255, 0) # Barva hráče (košíku)
CERVENA = (255, 0, 0) # Barva padajícího jablka
BILA = (255, 255, 255) # Použijeme pro texty
# Vytvoření objektu 'Clock', který řídí rychlost, aby hra nejela super rychle na výkonných PC
hodiny = pygame.time.Clock()
# Tvorba "fontu" neboli stylu písma pro vypisování skóre atd.
font = pygame.font.SysFont("Arial", 36)
# --- Proměnné Hráče (Zelený Košík dole) ---
hrac_sirka = 100
hrac_vyska = 20
# Kde bude stát na začátku? X=vodorovně (uprostřed mínus půlka hráče), Y=svisle (úplně dole)
hrac_x = SIRKA // 2 - hrac_sirka // 2
hrac_y = VYSKA - 40
# Rychlost hráče říká, o kolik pixelů se posune, když drží šipku
rychlost_hrace = 8
# --- Proměnné Jablka (Červený čtvereček shora) ---
jablko_velikost = 30
# Vybereme mu náhodnou souřadnici X kdekoliv od okraje do okraje
jablko_x = random.randint(0, SIRKA - jablko_velikost)
# Y mu dáme do ZÁPORNÝCH HODNOT! To znamená, že začíná nahoře schovaný mimo obrazovku!
jablko_y = -jablko_velikost
rychlost_jablka = 5 # Jak rychle jablko letí dolů?
# Herní statistiky
skore = 0
zivoty = 3
# Pravdivostní proměnná (boolean) udávající, že aplikace běží.
bezime = True
# ==========================================
# HLAVNÍ HERNÍ SMYČKA (GAME LOOP)
# Vše, co se děje ve hře se opakuje zde, zhruba 60x za vteřinu
# ==========================================
while bezime:
# 1. ČTENÍ UDÁLOSTÍ
# Kontrolujeme, co uživatel dělá - například zda nechce program zavřít
for udalost in pygame.event.get():
if udalost.type == pygame.QUIT:
bezime = False # Když klikne na křížek, ukončíme smyčku tím, že z 'bezime' uděláme False
# Hra se fyzicky hraje jenom dokud máme nějaké životy
if zivoty > 0:
# 2. LOGIKA HRÁČE: Čteme stisknutí kláves v reálném čase
# pygame.key.get_pressed() vrací obrovský seznam s hodnotami True nebo False u VŠECH tlačítek na klávesnici
klavesy = pygame.key.get_pressed()
# Pokud drží levou šipku a navíc ještě nenarazil do levé zdi (x > 0)
if klavesy[pygame.K_LEFT] and hrac_x > 0:
hrac_x -= rychlost_hrace # Posuneme ho doleva (odečteme x)
# Pokud drží pravou šipku a nenarazil do pravé zdi (Zde musíme odečíst šířku postavy, abychom měřili od kraje)
if klavesy[pygame.K_RIGHT] and hrac_x < SIRKA - hrac_sirka:
hrac_x += rychlost_hrace # Posuneme ho doprava (přičteme x)
# 3. LOGIKA PADÁNÍ JABLKA
# Osa Y jde v počítačové grafice "shora dolů", takže se zvyšující se Y jablko padá níže
jablko_y += rychlost_jablka
# --- KOLIZE (Dotyk hráče s jablkem) ---
# Abychom snadno zjistili, jestli se dotýkají, "obalíme" je virtuálními obdélníky (Rect)
hrac_rect = pygame.Rect(hrac_x, hrac_y, hrac_sirka, hrac_vyska)
jablko_rect = pygame.Rect(jablko_x, jablko_y, jablko_velikost, jablko_velikost)
# Pokud se tyto dva obdélníky protnou/srazí... Hráč ho chytil!
if hrac_rect.colliderect(jablko_rect):
skore += 1 # Dáme mu bod
rychlost_jablka += 0.2 # Mírně hru zrychlíme, ať je to těžší!
# Vygenerujeme znovu to stejné jablko úplně nahoře na nové X pozici
jablko_y = -jablko_velikost
jablko_x = random.randint(0, SIRKA - jablko_velikost)
# 4. LOGIKA PŘEHLÉDNUTÍ JABLKA
# Co se stane, když jablko propadne až za spodní hranu obrazovky?
elif jablko_y > VYSKA:
zivoty -= 1 # Hráč přišel o život
# Musíme jablko i přesto respawnovat zpět nahoru, aby hra mohla pokračovat
jablko_y = -jablko_velikost
jablko_x = random.randint(0, SIRKA - jablko_velikost)
# 5. KRESLENÍ VŠEHO NA OBRAZOVKU
# Každý snímek musíme celé okno zalít černou barvou, jinak by postavička dělala šmouhy, jak se hýbe
okno.fill(CERNA)
# Kreslíme jenom to, co je zrovna relevantní
if zivoty > 0:
# Vykreslení obdélníku hráče
pygame.draw.rect(okno, ZELENA, (hrac_x, hrac_y, hrac_sirka, hrac_vyska))
# Vykreslení obdélníku jablka
pygame.draw.rect(okno, CERVENA, (jablko_x, jablko_y, jablko_velikost, jablko_velikost))
# Převedeme textovou proměnnou (string) do obrázku (surface)
text_skore = font.render(f"Skóre: {skore}", True, BILA)
text_zivoty = font.render(f"Životy: {zivoty}", True, BILA)
# 'Nalepíme' (blit) tyto obrázky textů na plátno (obrazovku)
okno.blit(text_skore, (10, 10))
okno.blit(text_zivoty, (SIRKA - 150, 10))
else:
# Hráč nemá životy, je KONEC HRY! Kreslíme varovný text doprostřed.
text_konec = font.render(f"KONEC HRY! Tvé skóre: {skore}", True, CERVENA)
okno.blit(text_konec, (SIRKA//2 - 180, VYSKA//2))
# --- FINALIZACE SNÍMKU ---
# Toto pošle všechny ty připravené obdélníčky a texty z paměti počítače rovnou do monitoru!
pygame.display.flip()
# A tohle vynutí pauzu natolik dlouhou, abychom za vteřinu nepřekročili 60 smyček (60 FPS)
hodiny.tick(60)
# Pokud 'bezime' skočí na False a vyskočíme ze smyčky 'while', musíme bezpečně zhasnout!
pygame.quit()
sys.exit()
async def main(): # PŘIDÁNO PRO WEB: Zabalíme celou hru do asynchronní funkce
pygame.init()
# --- Nastavení herního okna ---
SIRKA = 800
VYSKA = 600
# Vytvoření hlavního okna hry o dané velikosti v pixelech (obrazových bodech)
okno = pygame.display.set_mode((SIRKA, VYSKA))
# Přidáme název hry do horní lišty softwarového okna
pygame.display.set_caption("Chytání jablek")
# --- Paleta barev ---
# Barvy zadáváme přes číselné hodnoty Červené, Zelené a Modré - RGB (Red, Green, Blue)
CERNA = (0, 0, 0) # Černá je absence všech barev
ZELENA = (0, 255, 0) # Barva hráče (košíku)
CERVENA = (255, 0, 0) # Barva padajícího jablka
BILA = (255, 255, 255) # Použijeme pro texty
# Vytvoření objektu 'Clock', který řídí rychlost, aby hra nejela super rychle na výkonných PC
hodiny = pygame.time.Clock()
# Tvorba "fontu" neboli stylu písma pro vypisování skóre atd.
font = pygame.font.SysFont("Arial", 36)
# --- Proměnné Hráče (Zelený Košík dole) ---
hrac_sirka = 100
hrac_vyska = 20
# Kde bude stát na začátku? X=vodorovně (uprostřed mínus půlka hráče), Y=svisle (úplně dole)
hrac_x = SIRKA // 2 - hrac_sirka // 2
hrac_y = VYSKA - 40
# Rychlost hráče říká, o kolik pixelů se posune, když drží šipku
rychlost_hrace = 8
# --- Proměnné Jablka (Červený čtvereček shora) ---
jablko_velikost = 30
# Vybereme mu náhodnou souřadnici X kdekoliv od okraje do okraje
jablko_x = random.randint(0, SIRKA - jablko_velikost)
# Y mu dáme do ZÁPORNÝCH HODNOT! To znamená, že začíná nahoře schovaný mimo obrazovku!
jablko_y = -jablko_velikost
rychlost_jablka = 5 # Jak rychle jablko letí dolů?
# Herní statistiky
skore = 0
zivoty = 3
# Pravdivostní proměnná (boolean) udávající, že aplikace běží.
bezime = True
# ==========================================
# HLAVNÍ HERNÍ SMYČKA (GAME LOOP)
# Vše, co se děje ve hře se opakuje zde, zhruba 60x za vteřinu
# ==========================================
while bezime:
# 1. ČTENÍ UDÁLOSTÍ
# Kontrolujeme, co uživatel dělá - například zda nechce program zavřít
for udalost in pygame.event.get():
if udalost.type == pygame.QUIT:
bezime = False # Když klikne na křížek, ukončíme smyčku tím, že z 'bezime' uděláme False
# Hra se fyzicky hraje jenom dokud máme nějaké životy
if zivoty > 0:
# 2. LOGIKA HRÁČE: Čteme stisknutí kláves v reálném čase
# pygame.key.get_pressed() vrací obrovský seznam s hodnotami True nebo False u VŠECH tlačítek na klávesnici
klavesy = pygame.key.get_pressed()
# Pokud drží levou šipku a navíc ještě nenarazil do levé zdi (x > 0)
if klavesy[pygame.K_LEFT] and hrac_x > 0:
hrac_x -= rychlost_hrace # Posuneme ho doleva (odečteme x)
# Pokud drží pravou šipku a nenarazil do pravé zdi (Zde musíme odečíst šířku postavy, abychom měřili od kraje)
if klavesy[pygame.K_RIGHT] and hrac_x < SIRKA - hrac_sirka:
hrac_x += rychlost_hrace # Posuneme ho doprava (přičteme x)
# 3. LOGIKA PADÁNÍ JABLKA
# Osa Y jde v počítačové grafice "shora dolů", takže se zvyšující se Y jablko padá níže
jablko_y += rychlost_jablka
# --- KOLIZE (Dotyk hráče s jablkem) ---
# Abychom snadno zjistili, jestli se dotýkají, "obalíme" je virtuálními obdélníky (Rect)
hrac_rect = pygame.Rect(hrac_x, hrac_y, hrac_sirka, hrac_vyska)
jablko_rect = pygame.Rect(jablko_x, jablko_y, jablko_velikost, jablko_velikost)
# Pokud se tyto dva obdélníky protnou/srazí... Hráč ho chytil!
if hrac_rect.colliderect(jablko_rect):
skore += 1 # Dáme mu bod
rychlost_jablka += 0.2 # Mírně hru zrychlíme, ať je to těžší!
# Vygenerujeme znovu to stejné jablko úplně nahoře na nové X pozici
jablko_y = -jablko_velikost
jablko_x = random.randint(0, SIRKA - jablko_velikost)
# 4. LOGIKA PŘEHLÉDNUTÍ JABLKA
# Co se stane, když jablko propadne až za spodní hranu obrazovky?
elif jablko_y > VYSKA:
zivoty -= 1 # Hráč přišel o život
# Musíme jablko i přesto respawnovat zpět nahoru, aby hra mohla pokračovat
jablko_y = -jablko_velikost
jablko_x = random.randint(0, SIRKA - jablko_velikost)
# 5. KRESLENÍ VŠEHO NA OBRAZOVKU
# Každý snímek musíme celé okno zalít černou barvou, jinak by postavička dělala šmouhy, jak se hýbe
okno.fill(CERNA)
# Kreslíme jenom to, co je zrovna relevantní
if zivoty > 0:
# Vykreslení obdélníku hráče
pygame.draw.rect(okno, ZELENA, (hrac_x, hrac_y, hrac_sirka, hrac_vyska))
# Vykreslení obdélníku jablka
pygame.draw.rect(okno, CERVENA, (jablko_x, jablko_y, jablko_velikost, jablko_velikost))
# Převedeme textovou proměnnou (string) do obrázku (surface)
text_skore = font.render(f"Skóre: {skore}", True, BILA)
text_zivoty = font.render(f"Životy: {zivoty}", True, BILA)
# 'Nalepíme' (blit) tyto obrázky textů na plátno (obrazovku)
okno.blit(text_skore, (10, 10))
okno.blit(text_zivoty, (SIRKA - 150, 10))
else:
# Hráč nemá životy, je KONEC HRY! Kreslíme varovný text doprostřed.
text_konec = font.render(f"KONEC HRY! Tvé skóre: {skore}", True, CERVENA)
okno.blit(text_konec, (SIRKA//2 - 180, VYSKA//2))
# --- FINALIZACE SNÍMKU ---
# Toto pošle všechny ty připravené obdélníčky a texty z paměti počítače rovnou do monitoru!
pygame.display.flip()
# A tohle vynutí pauzu natolik dlouhou, abychom za vteřinu nepřekročili 60 smyček (60 FPS)
hodiny.tick(60)
# PŘIDÁNO PRO WEB: Dáme prohlížeči šanci překreslit obrazovku
await asyncio.sleep(0)
# Pokud 'bezime' skočí na False a vyskočíme ze smyčky 'while', musíme bezpečně zhasnout!
pygame.quit()
sys.exit()
# PŘIDÁNO PRO WEB: Spuštění asynchronní hry
asyncio.run(main())

386
pygame/04_pong_dva_hraci.py

@ -1,192 +1,200 @@
import pygame import pygame
import asyncio # PŘIDÁNO PRO WEB: importujeme asyncio pro neblokující smyčku
import sys import sys
# pygame.init() spustí vnitřní motory knihovny Pygame. Bez toho nejde ani otevřít okno.
pygame.init()
# --- NASTAVENÍ OKNA ---
SIRKA = 800
VYSKA = 600
# Okno upevníme na tyto rozměry
okno = pygame.display.set_mode((SIRKA, VYSKA))
pygame.display.set_caption("Pong - Plná Hra pro dva hráče")
# --- BARVY ---
BILA = (255, 255, 255)
CERNA = (0, 0, 0)
ZELENA = (0, 255, 0)
# Herní hodiny omezují, aby náš počítač nehrál 1000x za vteřinu, čímž by hra byla nehratelná.
hodiny = pygame.time.Clock()
# Tvorba písem (pro vykreslování textu potřebujeme vždy nastavit velikost a typ písma)
font_velky = pygame.font.SysFont("Arial", 50)
font_maly = pygame.font.SysFont("Arial", 30)
# --- STAVOVÝ AUTOMAT (State Machine) ---
# Trik profesionálů: Hra je vždy v nějakém "Stavu" (Menu, Hra, Konec).
# Podle tohoto čísla pak naše hlavní smyčka pozná, jaké má platit rozvržení obrazovky a pravidla.
STAV_MENU = 0
STAV_HRA = 1
STAV_KONEC = 2
stav = STAV_MENU # Začínáme logicky v hlavním menu
# --- PROMĚNNÉ HRÁČŮ (Pálky) A MÍČKU ---
palka_sirka = 15
palka_vyska = 100
rychlost_palky = 7
micek_velikost = 15
# Funkce, která vrátí všechny proměnné na začátek. Použije se po gólu nebo při startu nové hry.
def reset_hry():
# Klíčové slovo 'global' musíme použít vždycky, když uvnitř Funkce chceme MĚNIT proměnnou,
# která byla vytvořena venku (na nejvyšší úrovni skriptu).
global hrac1_y, hrac2_y, skore1, skore2, micek_x, micek_y, micek_rychlost_x, micek_rychlost_y
# Hráče zarovnáme na střed Y osy (Výška okna / 2 - polovina velikosti hráče)
hrac1_y = VYSKA // 2 - palka_vyska // 2
hrac2_y = VYSKA // 2 - palka_vyska // 2
skore1 = 0
skore2 = 0
# Míček přesně doprostřed obou os X a Y
micek_x = SIRKA // 2 - micek_velikost // 2
micek_y = VYSKA // 2 - micek_velikost // 2
# Míček poletí rychlostí 5 bodů za snímek šikmo dolů doprava
micek_rychlost_x = 5
micek_rychlost_y = 5
# Okamžitě to zavoláme, abychom proměnným výše vložili výchozí čísla hned při spuštění programu
reset_hry()
bezime = True
# Hlavní cyklus hry, který točí snímek za snímkem donekonečna, dokud aplikaci neukončíme.
while bezime:
# 1. ZPRACOVÁNÍ UDÁLOSTÍ
# Procházíme např. stisky tlačítek myši, křížek pro zavření okna, jedno-stisky kláves
for udalost in pygame.event.get():
if udalost.type == pygame.QUIT:
bezime = False # Ukončíme while-cyklus
# Zjišťujeme, zda se NESTISKNULA klávesa. Tohle se provede jen JEDNOU, i když ji uživatel drží!
if udalost.type == pygame.KEYDOWN:
if stav == STAV_MENU and udalost.key == pygame.K_SPACE:
# Jsme v menu, hráč stiskl MEZERNÍK. Přepneme hru do režimu STAV_HRA!
stav = STAV_HRA
elif stav == STAV_KONEC and udalost.key == pygame.K_SPACE:
# Jsme na obrazovce s vítězem, hráč chce hrát znovu.
reset_hry()
stav = STAV_HRA
# Oproti tomu toto zjistí AKTUÁLNĚ DRŽENÉ klávesy v tomto zlomku vteřiny
klavesy = pygame.key.get_pressed()
# 2. HERNÍ LOGIKA (HÝBÁNÍ SE)
# Tohle se provede JEN a POUZE, pokud zrovna hrajeme zápas!
if stav == STAV_HRA:
# Ovládání Hráče 1 vlevo (Klávesy W a S pro pohyb nahoru/dolů)
if klavesy[pygame.K_w] and hrac1_y > 0:
hrac1_y -= rychlost_palky
# Dolů nemůže víc, než je výška okna zmenšená o jeho vlastní výšku pálky
if klavesy[pygame.K_s] and hrac1_y < VYSKA - palka_vyska:
hrac1_y += rychlost_palky
# Ovládání Hráče 2 vpravo (Šipky Nahoru a Dolů)
if klavesy[pygame.K_UP] and hrac2_y > 0:
hrac2_y -= rychlost_palky
if klavesy[pygame.K_DOWN] and hrac2_y < VYSKA - palka_vyska:
hrac2_y += rychlost_palky
# Míček se za každou smyčku kousek posune. Tím vzniká dojem pohybu.
micek_x += micek_rychlost_x
micek_y += micek_rychlost_y
# --- Fyzika: Odraz míčku od stropu a podlahy ---
# Trik: Vynásobením rychlosti číslem -1 se obrátí znaménko.
# Z kladné rychlosti (+5 padá dolů) se stane záporná (-5 letí nahoru). Odrazil se!
if micek_y <= 0 or micek_y >= VYSKA - micek_velikost:
micek_rychlost_y *= -1
# --- Fyzika: Odraz od pálek hráčů ---
# Abychom to jednoduše spočítali, vyrobíme si takzvané virtuální Obdélníky (Rect)
rect_micek = pygame.Rect(micek_x, micek_y, micek_velikost, micek_velikost)
# Pálku Hráče 1 vykreslujeme fixně 30 pixelů od levého okraje
rect_hrac1 = pygame.Rect(30, hrac1_y, palka_sirka, palka_vyska)
# Pálku Hráče 2 fixně na pravé straně. (SIRKA - 30 od kraje - velikost pálky)
rect_hrac2 = pygame.Rect(SIRKA - 30 - palka_sirka, hrac2_y, palka_sirka, palka_vyska)
# Metoda 'colliderect' doslova zkoumá, jestli se tyto obdélníky v daný moment nepřekrývají
if rect_micek.colliderect(rect_hrac1) or rect_micek.colliderect(rect_hrac2):
# Odrazil se od pálky! Obrátíme jeho směr po ose X a ještě ho mírně zrychlíme (* -1.1)
micek_rychlost_x *= -1.1
# --- Vyhodnocení Gólu ---
if micek_x < 0:
# Přeletěl za levý okraj (Hráč 1 ho nezachytil). Bod pro Hráče 2!
skore2 += 1
micek_x, micek_y = SIRKA // 2, VYSKA // 2 # Teleportace míčku na prostředek
micek_rychlost_x = 5 # Reset rychlosti a ať letí k hráči co dostal gól (doprava)
elif micek_x > SIRKA:
# Přeletěl vpravo
skore1 += 1
micek_x, micek_y = SIRKA // 2, VYSKA // 2
micek_rychlost_x = -5 # Směr letu doleva
# --- Konec Zápasu ---
# Zápas končí, jakmile někdo dosáhne 5 bodů
if skore1 >= 5 or skore2 >= 5:
stav = STAV_KONEC
# 3. KRESLENÍ GRAFIKY
# Vždy smažeme starou stopu vyplněním okna čistě černou barvou
okno.fill(CERNA)
# Co na obrazovce bude záleží zcela na tom, ve kterém STAVU náš Stavový Automat právě je!
if stav == STAV_MENU:
# Vykreslení Menu. .render převede nápis z textu do "razítka"
nadpis = font_velky.render("PONG", True, BILA)
navod = font_maly.render("Stiskni MEZERNÍK pro start", True, ZELENA)
# Tyto razítka .blitnem (otiskneme) na plátno. Výpočet (SIRKA//2 - nadpis.get_width()//2) zarovná nápis čistě doprostřed obrazovky.
okno.blit(nadpis, (SIRKA//2 - nadpis.get_width()//2, 200))
okno.blit(navod, (SIRKA//2 - navod.get_width()//2, 300))
elif stav == STAV_HRA:
# Jsme ve hře. Vykreslíme půlící čáru (síť v Pongu). 'aaline' je hladká čára.
pygame.draw.aaline(okno, BILA, (SIRKA // 2, 0), (SIRKA // 2, VYSKA))
# Nakreslíme pálky na místa, která předtím logika vypočítala
pygame.draw.rect(okno, BILA, rect_hrac1)
pygame.draw.rect(okno, BILA, rect_hrac2)
# Míček vykreslíme jako 'ellipsu' napasovanou na ten neviditelný 'rect_micek' obdélník. Vznikne tak kruh.
pygame.draw.ellipse(okno, BILA, rect_micek)
# Kreslení obou skóre úplně nahoru
text_skore = font_velky.render(f"{skore1} {skore2}", True, BILA)
okno.blit(text_skore, (SIRKA // 2 - text_skore.get_width() // 2, 20))
elif stav == STAV_KONEC:
# Zjištění, kdo vyhrál pomocí tzv. ternárního if (vítězem je "Hráč 1" POKUD má skore1>=5, JINAK je to "Hráč 2")
vitez = "Hráč 1" if skore1 >= 5 else "Hráč 2"
# Razítka s nápisy
text_vitez = font_velky.render(f"{vitez} vyhrál!", True, ZELENA)
text_restart = font_maly.render("Stiskni MEZERNÍK pro novou hru", True, BILA)
# Obtisknutí doprostřed okna
okno.blit(text_vitez, (SIRKA//2 - text_vitez.get_width()//2, 200))
okno.blit(text_restart, (SIRKA//2 - text_restart.get_width()//2, 300))
# Obraz je složený nanečisto v paměti. .flip() ho teprve natvrdo pošle na monitor!
pygame.display.flip()
# Pauza na udržení stabilních 60 FPS
hodiny.tick(60)
# Konec programu
pygame.quit()
sys.exit()
async def main(): # PŘIDÁNO PRO WEB: Zabalíme celou hru do asynchronní funkce
# pygame.init() spustí vnitřní motory knihovny Pygame. Bez toho nejde ani otevřít okno.
pygame.init()
# --- NASTAVENÍ OKNA ---
SIRKA = 800
VYSKA = 600
# Okno upevníme na tyto rozměry
okno = pygame.display.set_mode((SIRKA, VYSKA))
pygame.display.set_caption("Pong - Plná Hra pro dva hráče")
# --- BARVY ---
BILA = (255, 255, 255)
CERNA = (0, 0, 0)
ZELENA = (0, 255, 0)
# Herní hodiny omezují, aby náš počítač nehrál 1000x za vteřinu, čímž by hra byla nehratelná.
hodiny = pygame.time.Clock()
# Tvorba písem (pro vykreslování textu potřebujeme vždy nastavit velikost a typ písma)
font_velky = pygame.font.SysFont("Arial", 50)
font_maly = pygame.font.SysFont("Arial", 30)
# --- STAVOVÝ AUTOMAT (State Machine) ---
# Trik profesionálů: Hra je vždy v nějakém "Stavu" (Menu, Hra, Konec).
# Podle tohoto čísla pak naše hlavní smyčka pozná, jaké má platit rozvržení obrazovky a pravidla.
STAV_MENU = 0
STAV_HRA = 1
STAV_KONEC = 2
stav = STAV_MENU # Začínáme logicky v hlavním menu
# --- PROMĚNNÉ HRÁČŮ (Pálky) A MÍČKU ---
palka_sirka = 15
palka_vyska = 100
rychlost_palky = 7
micek_velikost = 15
# Funkce, která vrátí všechny proměnné na začátek. Použije se po gólu nebo při startu nové hry.
def reset_hry():
# Klíčové slovo 'global' musíme použít vždycky, když uvnitř Funkce chceme MĚNIT proměnnou,
# která byla vytvořena venku (na nejvyšší úrovni skriptu).
global hrac1_y, hrac2_y, skore1, skore2, micek_x, micek_y, micek_rychlost_x, micek_rychlost_y
# Hráče zarovnáme na střed Y osy (Výška okna / 2 - polovina velikosti hráče)
hrac1_y = VYSKA // 2 - palka_vyska // 2
hrac2_y = VYSKA // 2 - palka_vyska // 2
skore1 = 0
skore2 = 0
# Míček přesně doprostřed obou os X a Y
micek_x = SIRKA // 2 - micek_velikost // 2
micek_y = VYSKA // 2 - micek_velikost // 2
# Míček poletí rychlostí 5 bodů za snímek šikmo dolů doprava
micek_rychlost_x = 5
micek_rychlost_y = 5
# Okamžitě to zavoláme, abychom proměnným výše vložili výchozí čísla hned při spuštění programu
reset_hry()
bezime = True
# Hlavní cyklus hry, který točí snímek za snímkem donekonečna, dokud aplikaci neukončíme.
while bezime:
# 1. ZPRACOVÁNÍ UDÁLOSTÍ
# Procházíme např. stisky tlačítek myši, křížek pro zavření okna, jedno-stisky kláves
for udalost in pygame.event.get():
if udalost.type == pygame.QUIT:
bezime = False # Ukončíme while-cyklus
# Zjišťujeme, zda se NESTISKNULA klávesa. Tohle se provede jen JEDNOU, i když ji uživatel drží!
if udalost.type == pygame.KEYDOWN:
if stav == STAV_MENU and udalost.key == pygame.K_SPACE:
# Jsme v menu, hráč stiskl MEZERNÍK. Přepneme hru do režimu STAV_HRA!
stav = STAV_HRA
elif stav == STAV_KONEC and udalost.key == pygame.K_SPACE:
# Jsme na obrazovce s vítězem, hráč chce hrát znovu.
reset_hry()
stav = STAV_HRA
# Oproti tomu toto zjistí AKTUÁLNĚ DRŽENÉ klávesy v tomto zlomku vteřiny
klavesy = pygame.key.get_pressed()
# 2. HERNÍ LOGIKA (HÝBÁNÍ SE)
# Tohle se provede JEN a POUZE, pokud zrovna hrajeme zápas!
if stav == STAV_HRA:
# Ovládání Hráče 1 vlevo (Klávesy W a S pro pohyb nahoru/dolů)
if klavesy[pygame.K_w] and hrac1_y > 0:
hrac1_y -= rychlost_palky
# Dolů nemůže víc, než je výška okna zmenšená o jeho vlastní výšku pálky
if klavesy[pygame.K_s] and hrac1_y < VYSKA - palka_vyska:
hrac1_y += rychlost_palky
# Ovládání Hráče 2 vpravo (Šipky Nahoru a Dolů)
if klavesy[pygame.K_UP] and hrac2_y > 0:
hrac2_y -= rychlost_palky
if klavesy[pygame.K_DOWN] and hrac2_y < VYSKA - palka_vyska:
hrac2_y += rychlost_palky
# Míček se za každou smyčku kousek posune. Tím vzniká dojem pohybu.
micek_x += micek_rychlost_x
micek_y += micek_rychlost_y
# --- Fyzika: Odraz míčku od stropu a podlahy ---
# Trik: Vynásobením rychlosti číslem -1 se obrátí znaménko.
# Z kladné rychlosti (+5 padá dolů) se stane záporná (-5 letí nahoru). Odrazil se!
if micek_y <= 0 or micek_y >= VYSKA - micek_velikost:
micek_rychlost_y *= -1
# --- Fyzika: Odraz od pálek hráčů ---
# Abychom to jednoduše spočítali, vyrobíme si takzvané virtuální Obdélníky (Rect)
rect_micek = pygame.Rect(micek_x, micek_y, micek_velikost, micek_velikost)
# Pálku Hráče 1 vykreslujeme fixně 30 pixelů od levého okraje
rect_hrac1 = pygame.Rect(30, hrac1_y, palka_sirka, palka_vyska)
# Pálku Hráče 2 fixně na pravé straně. (SIRKA - 30 od kraje - velikost pálky)
rect_hrac2 = pygame.Rect(SIRKA - 30 - palka_sirka, hrac2_y, palka_sirka, palka_vyska)
# Metoda 'colliderect' doslova zkoumá, jestli se tyto obdélníky v daný moment nepřekrývají
if rect_micek.colliderect(rect_hrac1) or rect_micek.colliderect(rect_hrac2):
# Odrazil se od pálky! Obrátíme jeho směr po ose X a ještě ho mírně zrychlíme (* -1.1)
micek_rychlost_x *= -1.1
# --- Vyhodnocení Gólu ---
if micek_x < 0:
# Přeletěl za levý okraj (Hráč 1 ho nezachytil). Bod pro Hráče 2!
skore2 += 1
micek_x, micek_y = SIRKA // 2, VYSKA // 2 # Teleportace míčku na prostředek
micek_rychlost_x = 5 # Reset rychlosti a ať letí k hráči co dostal gól (doprava)
elif micek_x > SIRKA:
# Přeletěl vpravo
skore1 += 1
micek_x, micek_y = SIRKA // 2, VYSKA // 2
micek_rychlost_x = -5 # Směr letu doleva
# --- Konec Zápasu ---
# Zápas končí, jakmile někdo dosáhne 5 bodů
if skore1 >= 5 or skore2 >= 5:
stav = STAV_KONEC
# 3. KRESLENÍ GRAFIKY
# Vždy smažeme starou stopu vyplněním okna čistě černou barvou
okno.fill(CERNA)
# Co na obrazovce bude záleží zcela na tom, ve kterém STAVU náš Stavový Automat právě je!
if stav == STAV_MENU:
# Vykreslení Menu. .render převede nápis z textu do "razítka"
nadpis = font_velky.render("PONG", True, BILA)
navod = font_maly.render("Stiskni MEZERNÍK pro start", True, ZELENA)
# Tyto razítka .blitnem (otiskneme) na plátno. Výpočet (SIRKA//2 - nadpis.get_width()//2) zarovná nápis čistě doprostřed obrazovky.
okno.blit(nadpis, (SIRKA//2 - nadpis.get_width()//2, 200))
okno.blit(navod, (SIRKA//2 - navod.get_width()//2, 300))
elif stav == STAV_HRA:
# Jsme ve hře. Vykreslíme půlící čáru (síť v Pongu). 'aaline' je hladká čára.
pygame.draw.aaline(okno, BILA, (SIRKA // 2, 0), (SIRKA // 2, VYSKA))
# Nakreslíme pálky na místa, která předtím logika vypočítala
pygame.draw.rect(okno, BILA, rect_hrac1)
pygame.draw.rect(okno, BILA, rect_hrac2)
# Míček vykreslíme jako 'ellipsu' napasovanou na ten neviditelný 'rect_micek' obdélník. Vznikne tak kruh.
pygame.draw.ellipse(okno, BILA, rect_micek)
# Kreslení obou skóre úplně nahoru
text_skore = font_velky.render(f"{skore1} {skore2}", True, BILA)
okno.blit(text_skore, (SIRKA // 2 - text_skore.get_width() // 2, 20))
elif stav == STAV_KONEC:
# Zjištění, kdo vyhrál pomocí tzv. ternárního if (vítězem je "Hráč 1" POKUD má skore1>=5, JINAK je to "Hráč 2")
vitez = "Hráč 1" if skore1 >= 5 else "Hráč 2"
# Razítka s nápisy
text_vitez = font_velky.render(f"{vitez} vyhrál!", True, ZELENA)
text_restart = font_maly.render("Stiskni MEZERNÍK pro novou hru", True, BILA)
# Obtisknutí doprostřed okna
okno.blit(text_vitez, (SIRKA//2 - text_vitez.get_width()//2, 200))
okno.blit(text_restart, (SIRKA//2 - text_restart.get_width()//2, 300))
# Obraz je složený nanečisto v paměti. .flip() ho teprve natvrdo pošle na monitor!
pygame.display.flip()
# Pauza na udržení stabilních 60 FPS
hodiny.tick(60)
# PŘIDÁNO PRO WEB: Dáme prohlížeči šanci překreslit obrazovku
await asyncio.sleep(0)
# Konec programu
pygame.quit()
sys.exit()
# PŘIDÁNO PRO WEB: Spuštění asynchronní hry
asyncio.run(main())

338
pygame/05_skakacka_gravitace.py

@ -1,169 +1,177 @@
import pygame import pygame
import asyncio # PŘIDÁNO PRO WEB: importujeme asyncio pro neblokující smyčku
import sys import sys
import random import random
pygame.init()
# --- NASTAVENÍ OKNA A PROMĚNNÝCH PROSTŘEDÍ ---
SIRKA = 800
VYSKA = 600
okno = pygame.display.set_mode((SIRKA, VYSKA))
pygame.display.set_caption("Skákačka přes překážky")
# --- BARVY ---
BILA = (255, 255, 255)
CERNA = (0, 0, 0)
MODRA = (50, 150, 255) # Na modrou oblohu
ZELENA = (50, 200, 50) # Zelená pro plošinu trávy
CERVENA = (255, 0, 0) # Nebezpečné překážky v cestě
hodiny = pygame.time.Clock()
font = pygame.font.SysFont("Arial", 40)
font_maly = pygame.font.SysFont("Arial", 25)
# --- STAVOVÝ AUTOMAT HRY ---
STAV_MENU = 0
STAV_HRA = 1
STAV_KONEC = 2
stav = STAV_MENU
# --- FYZIKA A HERNÍ PROMĚNNÉ ---
podlaha_y = VYSKA - 50 # V jaké hloubce se nachází hrana země/podlahy? (od shora dolů)
hrac_sirka = 40
hrac_vyska = 40
# Fyzika
# Gravitace způsobuje trvalé ZRYCHLOVÁNÍ pádu. Ne rychlost, zrychlení!
gravitace = 0.6
# Skok není nic jiného, než extrémní rána do záporných hodnot na ose Y (odstřelení do stropu)
sila_skoku = -12
def reset_hry():
"""Funkce pro nahození proměnných do výchozího stavu pro start čisté hry"""
global hrac_x, hrac_y, rychlost_y, na_zemi, prekazky, skore, rychlost_hry
hrac_x = 100 # Hráč stojí celkem blízko levému kraji, aby měl čas reagovat
# Hráče položíme tak, aby jeho nohy (y + výška) přesně seděly na 'podlaha_y'
hrac_y = podlaha_y - hrac_vyska
rychlost_y = 0
na_zemi = True # Nyní stojí na zemi (a smí tedy zmáčknout Skok)
# Místo abychom dělali překážku po překážce, uchováme je jako seznam (List)
prekazky = []
skore = 0
rychlost_hry = 6 # Jak rychle ubíhá terén směrem k hráči doleva?
reset_hry()
# --- VLASTNÍ ČASOVAČ (Custom Event) ---
# Trik z programování: Pygame neustále běží ve smyčce a "naslouchá" událostem (jako stisk myši).
# Zde si vyrobíme událost NAŠI VLASTNÍ. Pojmenujeme ji libovolně SPAWN_PREKAZKY.
SPAWN_PREKAZKY = pygame.USEREVENT + 1
# Nastavíme pygame časovač tak, ať tuto 'neviditelnou klávesu SPAWN_PREKAZKY' pípne
# do fronty událostí přesně každých 1500 milisekund (1.5 sekundy). Úplně automaticky!
pygame.time.set_timer(SPAWN_PREKAZKY, 1500)
bezime = True
while bezime:
# 1. ZPRACOVÁNÍ UDÁLOSTÍ
for udalost in pygame.event.get():
if udalost.type == pygame.QUIT:
bezime = False
# Zkoumání stisku kláves jako POUHÁ UDÁLOST. To znamená "zmáčklo se to"
# neboli neřešíme, jestli se to zrovna drží stlačené. Ideální pro Starty a Skoky.
if udalost.type == pygame.KEYDOWN:
if stav == STAV_MENU and udalost.key == pygame.K_SPACE:
stav = STAV_HRA
elif stav == STAV_KONEC and udalost.key == pygame.K_SPACE:
reset_hry()
stav = STAV_HRA
# Jak hráč skáče? Šipka nahoru! Ale MUSÍ zároveň stát na zemi (na_zemi == True),
# jinak bychom mohli skákat ve vzduchu jak pták (tzv. multi-jump)
elif stav == STAV_HRA and udalost.key == pygame.K_UP and na_zemi:
rychlost_y = sila_skoku # Tímto dáváme drtivý impulz rychlosti směrem NAHORU
na_zemi = False # Teď už jsme ve vzduchu!
# Tady čekáme na PÍPNUTÍ z našeho automatického Custom Časovače!!!
if udalost.type == SPAWN_PREKAZKY and stav == STAV_HRA:
# Objevil se signál, že uplynulo 1.5 sekundy.
# Přidáme novou překážku (čtvereček červený) na konec obrazovky (SIRKA)
# Tyto překážky ukládáme rovnou jako Rect, protože se to pak hodí u kolizí
prekazky.append(pygame.Rect(SIRKA, podlaha_y - 40, 30, 40))
# Aby to nebylo nudné, pokaždé hru o malilinký kousíček zrychlíme.
rychlost_hry += 0.1
# 2. LOGIKA
if stav == STAV_HRA:
# --- FYZIKA SKÁKÁNÍ ---
# Neúprosná gravitace v každém milisekundovém snímku přidává směrem DOLŮ!
rychlost_y += gravitace
# Změníme opravdovou Y pozici hráče o tuto aktuální rychlost.
# Výsledek? Hráč zpomaluje směrem nahoru... zastaví se... a začne opět zrychlovat dolů k zemi!
hrac_y += rychlost_y
# Musíme ale zabránit tomu, aby hráč propadl podlahou do hlubin!
if hrac_y + hrac_vyska >= podlaha_y:
# Opravíme jeho Y, posadíme ho přesně podrážkama na okraj podlahy
hrac_y = podlaha_y - hrac_vyska
rychlost_y = 0 # Rychlost dopadu zrušíme, stál by na místě
na_zemi = True # Oznámíme, že zase můžeme skákat, dotýkáme se
# Vytvoření virtuálního obalu přes našeho hráče kvůli měření kolizí
hrac_rect = pygame.Rect(hrac_x, hrac_y, hrac_sirka, hrac_vyska)
# --- LOGIKA VŠECH PŘEKÁŽEK NA TRATI ---
# For cyklus prekazky[:] zkoumá tzv. KOPII seznamu.
# Je to proto, abychom z originálu mohli prvek bezpečně vymazat a nic "nepřeskočili".
for p in prekazky[:]:
p.x -= int(rychlost_hry) # Každá jedna překážka jede nezadržitelně doleva směrem na nás
# Co když se obdélníčky Hráče a Překážky překrývají? -> BOUM!
if p.colliderect(hrac_rect):
stav = STAV_KONEC
# Pokud na nás nenarazila, zajela na levém okraji pod 0 a zmizela (p.x < -30)
if p.x < -30:
prekazky.remove(p) # Hráč ji s úspěchem přežil a překážka zajela za monitor. Smazat z RAM!
skore += 1 # Přičteme za ni bod.
# 3. KRESLENÍ (na monitor)
okno.fill(MODRA) # Obloha
# Nakreslíme zelený pruh od čáry podlahy (podlaha_y) dolů až k zemi, bude dělat vizuál trávy
pygame.draw.rect(okno, ZELENA, (0, podlaha_y, SIRKA, VYSKA - podlaha_y))
if stav == STAV_MENU:
text = font.render("SKÁKAČKA", True, BILA)
start = font_maly.render("Stiskni MEZERNÍK. Skáče se šipkou NAHORU.", True, CERNA)
okno.blit(text, (SIRKA//2 - text.get_width()//2, 200))
okno.blit(start, (SIRKA//2 - start.get_width()//2, 300))
elif stav == STAV_HRA:
# Nakreslení postavičky hráče - využijeme ten Rect obal, co už jsme si spočítali
pygame.draw.rect(okno, CERNA, hrac_rect)
# Nakreslíme úplně všechny načítané nepřátelské Rect obdélníky jako červené objekty
for p in prekazky:
pygame.draw.rect(okno, CERVENA, p)
skore_text = font.render(f"Skóre: {skore}", True, CERNA)
okno.blit(skore_text, (10, 10))
elif stav == STAV_KONEC:
text = font.render("KONEC HRY", True, CERVENA)
skore_text = font.render(f"Dosažené skóre: {skore}", True, BILA)
restart = font_maly.render("Stiskni MEZERNÍK pro novou hru", True, CERNA)
okno.blit(text, (SIRKA//2 - text.get_width()//2, 150))
okno.blit(skore_text, (SIRKA//2 - skore_text.get_width()//2, 220))
okno.blit(restart, (SIRKA//2 - restart.get_width()//2, 300))
# .flip() překlopí to, co jsme nakreslili v paměti na obrazovku k hráči
pygame.display.flip()
hodiny.tick(60) # Cílíme na hladkých 60 FPS (Frames Per Second)
pygame.quit()
sys.exit()
async def main(): # PŘIDÁNO PRO WEB: Zabalíme celou hru do asynchronní funkce
pygame.init()
# --- NASTAVENÍ OKNA A PROMĚNNÝCH PROSTŘEDÍ ---
SIRKA = 800
VYSKA = 600
okno = pygame.display.set_mode((SIRKA, VYSKA))
pygame.display.set_caption("Skákačka přes překážky")
# --- BARVY ---
BILA = (255, 255, 255)
CERNA = (0, 0, 0)
MODRA = (50, 150, 255) # Na modrou oblohu
ZELENA = (50, 200, 50) # Zelená pro plošinu trávy
CERVENA = (255, 0, 0) # Nebezpečné překážky v cestě
hodiny = pygame.time.Clock()
font = pygame.font.SysFont("Arial", 40)
font_maly = pygame.font.SysFont("Arial", 25)
# --- STAVOVÝ AUTOMAT HRY ---
STAV_MENU = 0
STAV_HRA = 1
STAV_KONEC = 2
stav = STAV_MENU
# --- FYZIKA A HERNÍ PROMĚNNÉ ---
podlaha_y = VYSKA - 50 # V jaké hloubce se nachází hrana země/podlahy? (od shora dolů)
hrac_sirka = 40
hrac_vyska = 40
# Fyzika
# Gravitace způsobuje trvalé ZRYCHLOVÁNÍ pádu. Ne rychlost, zrychlení!
gravitace = 0.6
# Skok není nic jiného, než extrémní rána do záporných hodnot na ose Y (odstřelení do stropu)
sila_skoku = -12
def reset_hry():
"""Funkce pro nahození proměnných do výchozího stavu pro start čisté hry"""
global hrac_x, hrac_y, rychlost_y, na_zemi, prekazky, skore, rychlost_hry
hrac_x = 100 # Hráč stojí celkem blízko levému kraji, aby měl čas reagovat
# Hráče položíme tak, aby jeho nohy (y + výška) přesně seděly na 'podlaha_y'
hrac_y = podlaha_y - hrac_vyska
rychlost_y = 0
na_zemi = True # Nyní stojí na zemi (a smí tedy zmáčknout Skok)
# Místo abychom dělali překážku po překážce, uchováme je jako seznam (List)
prekazky = []
skore = 0
rychlost_hry = 6 # Jak rychle ubíhá terén směrem k hráči doleva?
reset_hry()
# --- VLASTNÍ ČASOVAČ (Custom Event) ---
# Trik z programování: Pygame neustále běží ve smyčce a "naslouchá" událostem (jako stisk myši).
# Zde si vyrobíme událost NAŠI VLASTNÍ. Pojmenujeme ji libovolně SPAWN_PREKAZKY.
SPAWN_PREKAZKY = pygame.USEREVENT + 1
# Nastavíme pygame časovač tak, ať tuto 'neviditelnou klávesu SPAWN_PREKAZKY' pípne
# do fronty událostí přesně každých 1500 milisekund (1.5 sekundy). Úplně automaticky!
pygame.time.set_timer(SPAWN_PREKAZKY, 1500)
bezime = True
while bezime:
# 1. ZPRACOVÁNÍ UDÁLOSTÍ
for udalost in pygame.event.get():
if udalost.type == pygame.QUIT:
bezime = False
# Zkoumání stisku kláves jako POUHÁ UDÁLOST. To znamená "zmáčklo se to"
# neboli neřešíme, jestli se to zrovna drží stlačené. Ideální pro Starty a Skoky.
if udalost.type == pygame.KEYDOWN:
if stav == STAV_MENU and udalost.key == pygame.K_SPACE:
stav = STAV_HRA
elif stav == STAV_KONEC and udalost.key == pygame.K_SPACE:
reset_hry()
stav = STAV_HRA
# Jak hráč skáče? Šipka nahoru! Ale MUSÍ zároveň stát na zemi (na_zemi == True),
# jinak bychom mohli skákat ve vzduchu jak pták (tzv. multi-jump)
elif stav == STAV_HRA and udalost.key == pygame.K_UP and na_zemi:
rychlost_y = sila_skoku # Tímto dáváme drtivý impulz rychlosti směrem NAHORU
na_zemi = False # Teď už jsme ve vzduchu!
# Tady čekáme na PÍPNUTÍ z našeho automatického Custom Časovače!!!
if udalost.type == SPAWN_PREKAZKY and stav == STAV_HRA:
# Objevil se signál, že uplynulo 1.5 sekundy.
# Přidáme novou překážku (čtvereček červený) na konec obrazovky (SIRKA)
# Tyto překážky ukládáme rovnou jako Rect, protože se to pak hodí u kolizí
prekazky.append(pygame.Rect(SIRKA, podlaha_y - 40, 30, 40))
# Aby to nebylo nudné, pokaždé hru o malilinký kousíček zrychlíme.
rychlost_hry += 0.1
# 2. LOGIKA
if stav == STAV_HRA:
# --- FYZIKA SKÁKÁNÍ ---
# Neúprosná gravitace v každém milisekundovém snímku přidává směrem DOLŮ!
rychlost_y += gravitace
# Změníme opravdovou Y pozici hráče o tuto aktuální rychlost.
# Výsledek? Hráč zpomaluje směrem nahoru... zastaví se... a začne opět zrychlovat dolů k zemi!
hrac_y += rychlost_y
# Musíme ale zabránit tomu, aby hráč propadl podlahou do hlubin!
if hrac_y + hrac_vyska >= podlaha_y:
# Opravíme jeho Y, posadíme ho přesně podrážkama na okraj podlahy
hrac_y = podlaha_y - hrac_vyska
rychlost_y = 0 # Rychlost dopadu zrušíme, stál by na místě
na_zemi = True # Oznámíme, že zase můžeme skákat, dotýkáme se
# Vytvoření virtuálního obalu přes našeho hráče kvůli měření kolizí
hrac_rect = pygame.Rect(hrac_x, hrac_y, hrac_sirka, hrac_vyska)
# --- LOGIKA VŠECH PŘEKÁŽEK NA TRATI ---
# For cyklus prekazky[:] zkoumá tzv. KOPII seznamu.
# Je to proto, abychom z originálu mohli prvek bezpečně vymazat a nic "nepřeskočili".
for p in prekazky[:]:
p.x -= int(rychlost_hry) # Každá jedna překážka jede nezadržitelně doleva směrem na nás
# Co když se obdélníčky Hráče a Překážky překrývají? -> BOUM!
if p.colliderect(hrac_rect):
stav = STAV_KONEC
# Pokud na nás nenarazila, zajela na levém okraji pod 0 a zmizela (p.x < -30)
if p.x < -30:
prekazky.remove(p) # Hráč ji s úspěchem přežil a překážka zajela za monitor. Smazat z RAM!
skore += 1 # Přičteme za ni bod.
# 3. KRESLENÍ (na monitor)
okno.fill(MODRA) # Obloha
# Nakreslíme zelený pruh od čáry podlahy (podlaha_y) dolů až k zemi, bude dělat vizuál trávy
pygame.draw.rect(okno, ZELENA, (0, podlaha_y, SIRKA, VYSKA - podlaha_y))
if stav == STAV_MENU:
text = font.render("SKÁKAČKA", True, BILA)
start = font_maly.render("Stiskni MEZERNÍK. Skáče se šipkou NAHORU.", True, CERNA)
okno.blit(text, (SIRKA//2 - text.get_width()//2, 200))
okno.blit(start, (SIRKA//2 - start.get_width()//2, 300))
elif stav == STAV_HRA:
# Nakreslení postavičky hráče - využijeme ten Rect obal, co už jsme si spočítali
pygame.draw.rect(okno, CERNA, hrac_rect)
# Nakreslíme úplně všechny načítané nepřátelské Rect obdélníky jako červené objekty
for p in prekazky:
pygame.draw.rect(okno, CERVENA, p)
skore_text = font.render(f"Skóre: {skore}", True, CERNA)
okno.blit(skore_text, (10, 10))
elif stav == STAV_KONEC:
text = font.render("KONEC HRY", True, CERVENA)
skore_text = font.render(f"Dosažené skóre: {skore}", True, BILA)
restart = font_maly.render("Stiskni MEZERNÍK pro novou hru", True, CERNA)
okno.blit(text, (SIRKA//2 - text.get_width()//2, 150))
okno.blit(skore_text, (SIRKA//2 - skore_text.get_width()//2, 220))
okno.blit(restart, (SIRKA//2 - restart.get_width()//2, 300))
# .flip() překlopí to, co jsme nakreslili v paměti na obrazovku k hráči
pygame.display.flip()
hodiny.tick(60) # Cílíme na hladkých 60 FPS (Frames Per Second)
# PŘIDÁNO PRO WEB: Dáme prohlížeči šanci překreslit obrazovku
await asyncio.sleep(0)
pygame.quit()
sys.exit()
# PŘIDÁNO PRO WEB: Spuštění asynchronní hry
asyncio.run(main())

370
pygame/06_vesmirna_strilecka.py

@ -1,185 +1,193 @@
import pygame import pygame
import asyncio # PŘIDÁNO PRO WEB: importujeme asyncio pro neblokující smyčku
import sys import sys
import random import random
pygame.init()
# --- NASTAVENÍ OKNA ---
SIRKA = 800
VYSKA = 600
okno = pygame.display.set_mode((SIRKA, VYSKA))
pygame.display.set_caption("Vesmírná střílečka (Space Invaders)")
# --- BARVY ---
CERNA = (0, 0, 0)
ZELENA = (0, 255, 0) # Hráč (Vesmírná loď)
CERVENA = (255, 0, 0) # Nepřátelé (Mimozemšťani, Meteority)
ZLUCA = (255, 255, 0) # Projektily (Lasery)
BILA = (255, 255, 255) # Text a UI
hodiny = pygame.time.Clock()
font_velky = pygame.font.SysFont("Arial", 50)
font_maly = pygame.font.SysFont("Arial", 30)
# Herní stav 0,1,2 - stejná logika oddělení úvodního menu od zápasu
STAV_MENU = 0
STAV_HRA = 1
STAV_KONEC = 2
stav = STAV_MENU
# Rozměry vesmírné lodi hráče
hrac_sirka = 40
hrac_vyska = 40
rychlost_hrace = 6
rychlost_projektilu = 10 # Lasery létají celkem svižně nahoru
# Vytvoření vlastního časovače pro přidávání nepřátel, viz skákačka
SPAWN_NEPRITELE = pygame.USEREVENT + 1
def reset_hry():
"""Tato funkce srovná hru zpátky do prvního dne, když spustíme reset"""
global hrac_x, hrac_y, projektily, nepratele, skore, rychlost_nepritele, spawn_cas
# Hráč je dole nad hranou obrazovky
hrac_x = SIRKA // 2 - hrac_sirka // 2
hrac_y = VYSKA - 60
# Budou zde létat nezávisle na sobě desítky kulek i nepřátel.
# Abychom se v nich vyznali, naženeme je všechny do SEZNAMŮ.
projektily = []
nepratele = []
skore = 0
rychlost_nepritele = 2.0
spawn_cas = 1000 # Počáteční čas (v milisekundách) k objevování padajících mimozemšťanů
# Nastartování naší události (eventu), začne tikat...
pygame.time.set_timer(SPAWN_NEPRITELE, spawn_cas)
reset_hry()
bezime = True
while bezime:
# 1. ZPRACOVÁNÍ UDÁLOSTÍ
for udalost in pygame.event.get():
if udalost.type == pygame.QUIT:
bezime = False
# Zjišťujeme JEDNORÁZOVÉ stisknutí tlačítek. Například výstřel nebo posun v menu.
if udalost.type == pygame.KEYDOWN:
if stav == STAV_MENU and udalost.key == pygame.K_SPACE:
stav = STAV_HRA
elif stav == STAV_KONEC and udalost.key == pygame.K_SPACE:
reset_hry()
stav = STAV_HRA
elif stav == STAV_HRA and udalost.key == pygame.K_SPACE:
# --- VÝSTŘEL ---
# Hráč právě odpálil! Vypočítáme, kde zhruba leží střed jeho lodi (špička křídel)
projektil_x = hrac_x + hrac_sirka // 2 - 5
# A přímo na těchto souřadnicích se narodí nová "KULKA" v podobě Rect. Přidáme do listu.
projektily.append(pygame.Rect(projektil_x, hrac_y, 10, 20))
# Zde časovač zakřičel (třeba každou sekundu), že na nás padá další nepřítel
if udalost.type == SPAWN_NEPRITELE and stav == STAV_HRA:
# Narodil se nový nepřítel nahoře mimo obraz (proto je osa y záporná: -40) na náhodném místě osy X.
n_x = random.randint(0, SIRKA - 40)
nepratele.append(pygame.Rect(n_x, -40, 40, 40))
# STUPŇOVÁNÍ OBTÍŽNOSTI (Nepřátelé padají čím dál rychleji)
rychlost_nepritele += 0.05
# Čím déle hrajeme, tím se také zkracuje ten časovač a nepřátel je hustší roj.
# Omezíme to na min 300ms, aby ta hra byla alespoň fyzicky dohratelná a nepřátelé nespadli z jednolité zdi.
if spawn_cas > 300:
spawn_cas -= 20
# Musíme časovač s tímto novým o 20ms kratším časem znovu "nastartovat"
pygame.time.set_timer(SPAWN_NEPRITELE, spawn_cas)
if stav == STAV_HRA:
# 2. LOGIKA
# Ovládání vesmírné lodi doprava a doleva držením tlačítek
klavesy = pygame.key.get_pressed()
if klavesy[pygame.K_LEFT] and hrac_x > 0:
hrac_x -= rychlost_hrace
if klavesy[pygame.K_RIGHT] and hrac_x < SIRKA - hrac_sirka:
hrac_x += rychlost_hrace
# --- POHYB VŠECH LASERŮ (KULEK) ---
# Projektily[:] dělá kopii listu, to se musí dělat vždy, když chci za běhu for-cyklu z toho pole
# věci rovnou odstraňovat! Kdybychom neodstraňovali kulky, list by narostl a hra spadla na paměť.
for p in projektily[:]:
p.y -= rychlost_projektilu # Y se ZMENŠUJE, protože kulka cestuje odspodu (např. Y=500) nahoru (Y=0)
# Když kulka odcestuje nad okraj obrazovky do hlubin kosmu, smažeme ji
if p.y < 0: projektily.remove(p)
# Hráč samotný obalený do neviditelného Rect rámečku pro vyhodnocování havárií
hrac_rect = pygame.Rect(hrac_x, hrac_y, hrac_sirka, hrac_vyska)
# --- POHYB VŠECH NEPŘÁTEL & KOLIZE ---
for n in nepratele[:]:
n.y += int(rychlost_nepritele) # Zvyšujeme Y -> Meteorit letí strmě dolů z nebes k podlaze
# 1. Havárie! Pokud meteorit doletí až úplně dolů přes obrazovku, NEBO narazí do Hráče
if n.y > VYSKA or n.colliderect(hrac_rect):
stav = STAV_KONEC
# 2. Křížová kontrola, zda SE STŘETLA KULKA S NEPŘÍTELEM (Laser do Meteoritu)
# Každého meteorita porovnáme znovu se všemi létajícími lasery na scéně!
for p in projektily[:]:
# .colliderect znamená "prolnuly se tyhle dva čtverce?"
if p.colliderect(n):
# BUM! Nastal obrovský výbuch.
# Zmizí Laser i Meteorit, tak je oba bezpečně (jen pokud už nebyli smazáni) vymažeme
if p in projektily: projektily.remove(p)
if n in nepratele: nepratele.remove(n)
skore += 10 # +10 bodů do kasičky!
# SLOVO 'break' ZMRAZÍ TENTO VNITŘNÍ FOR CYKLUS!
# Má to logiku - Pokud tento Nepřítel 'n' byl teď vteřinu zničen, už nedává smysl dál
# v tomto mikrosnímku procházet zbylé kulky a ptát se, jestli ho střelily taky. Přerušíme to.
break
# 3. KRESLENÍ GRAFIKY
okno.fill(CERNA)
if stav == STAV_MENU:
text = font_velky.render("VESMÍRNÁ STŘÍLEČKA", True, ZLUCA)
start = font_maly.render("Stiskni MEZERNÍK. Střílíš mezerníkem, pohyb šipkami.", True, BILA)
okno.blit(text, (SIRKA//2 - text.get_width()//2, 200))
okno.blit(start, (SIRKA//2 - start.get_width()//2, 300))
elif stav == STAV_HRA:
# Jsme ve hře! Nyní se z paměti převedou čísla Rect rámečků do reálných obrazců na monitoru.
# Naše loď
pygame.draw.rect(okno, ZELENA, hrac_rect)
# Smyčka vykreslí všechny Lasery co jsou aktuálně ve vzduchu
for p in projektily:
pygame.draw.rect(okno, ZLUCA, p)
# Ta samá smyčka vykreslí pluky všech padajících Nepřátel
for n in nepratele:
pygame.draw.rect(okno, CERVENA, n)
# Zobrazení skóre
skore_text = font_maly.render(f"Skóre: {skore}", True, BILA)
okno.blit(skore_text, (10, 10))
elif stav == STAV_KONEC:
text = font_velky.render("ZEMŘEL JSI!", True, CERVENA)
skore_text = font_velky.render(f"Tvé skóre: {skore}", True, BILA)
restart = font_maly.render("Stiskni MEZERNÍK pro novou hru", True, ZLUCA)
okno.blit(text, (SIRKA//2 - text.get_width()//2, 150))
okno.blit(skore_text, (SIRKA//2 - skore_text.get_width()//2, 250))
okno.blit(restart, (SIRKA//2 - restart.get_width()//2, 350))
# Obraz je spočítán. Posíláme do HDMI portu na monitor! (.flip)
pygame.display.flip()
hodiny.tick(60)
pygame.quit()
sys.exit()
async def main(): # PŘIDÁNO PRO WEB: Zabalíme celou hru do asynchronní funkce
pygame.init()
# --- NASTAVENÍ OKNA ---
SIRKA = 800
VYSKA = 600
okno = pygame.display.set_mode((SIRKA, VYSKA))
pygame.display.set_caption("Vesmírná střílečka (Space Invaders)")
# --- BARVY ---
CERNA = (0, 0, 0)
ZELENA = (0, 255, 0) # Hráč (Vesmírná loď)
CERVENA = (255, 0, 0) # Nepřátelé (Mimozemšťani, Meteority)
ZLUCA = (255, 255, 0) # Projektily (Lasery)
BILA = (255, 255, 255) # Text a UI
hodiny = pygame.time.Clock()
font_velky = pygame.font.SysFont("Arial", 50)
font_maly = pygame.font.SysFont("Arial", 30)
# Herní stav 0,1,2 - stejná logika oddělení úvodního menu od zápasu
STAV_MENU = 0
STAV_HRA = 1
STAV_KONEC = 2
stav = STAV_MENU
# Rozměry vesmírné lodi hráče
hrac_sirka = 40
hrac_vyska = 40
rychlost_hrace = 6
rychlost_projektilu = 10 # Lasery létají celkem svižně nahoru
# Vytvoření vlastního časovače pro přidávání nepřátel, viz skákačka
SPAWN_NEPRITELE = pygame.USEREVENT + 1
def reset_hry():
"""Tato funkce srovná hru zpátky do prvního dne, když spustíme reset"""
global hrac_x, hrac_y, projektily, nepratele, skore, rychlost_nepritele, spawn_cas
# Hráč je dole nad hranou obrazovky
hrac_x = SIRKA // 2 - hrac_sirka // 2
hrac_y = VYSKA - 60
# Budou zde létat nezávisle na sobě desítky kulek i nepřátel.
# Abychom se v nich vyznali, naženeme je všechny do SEZNAMŮ.
projektily = []
nepratele = []
skore = 0
rychlost_nepritele = 2.0
spawn_cas = 1000 # Počáteční čas (v milisekundách) k objevování padajících mimozemšťanů
# Nastartování naší události (eventu), začne tikat...
pygame.time.set_timer(SPAWN_NEPRITELE, spawn_cas)
reset_hry()
bezime = True
while bezime:
# 1. ZPRACOVÁNÍ UDÁLOSTÍ
for udalost in pygame.event.get():
if udalost.type == pygame.QUIT:
bezime = False
# Zjišťujeme JEDNORÁZOVÉ stisknutí tlačítek. Například výstřel nebo posun v menu.
if udalost.type == pygame.KEYDOWN:
if stav == STAV_MENU and udalost.key == pygame.K_SPACE:
stav = STAV_HRA
elif stav == STAV_KONEC and udalost.key == pygame.K_SPACE:
reset_hry()
stav = STAV_HRA
elif stav == STAV_HRA and udalost.key == pygame.K_SPACE:
# --- VÝSTŘEL ---
# Hráč právě odpálil! Vypočítáme, kde zhruba leží střed jeho lodi (špička křídel)
projektil_x = hrac_x + hrac_sirka // 2 - 5
# A přímo na těchto souřadnicích se narodí nová "KULKA" v podobě Rect. Přidáme do listu.
projektily.append(pygame.Rect(projektil_x, hrac_y, 10, 20))
# Zde časovač zakřičel (třeba každou sekundu), že na nás padá další nepřítel
if udalost.type == SPAWN_NEPRITELE and stav == STAV_HRA:
# Narodil se nový nepřítel nahoře mimo obraz (proto je osa y záporná: -40) na náhodném místě osy X.
n_x = random.randint(0, SIRKA - 40)
nepratele.append(pygame.Rect(n_x, -40, 40, 40))
# STUPŇOVÁNÍ OBTÍŽNOSTI (Nepřátelé padají čím dál rychleji)
rychlost_nepritele += 0.05
# Čím déle hrajeme, tím se také zkracuje ten časovač a nepřátel je hustší roj.
# Omezíme to na min 300ms, aby ta hra byla alespoň fyzicky dohratelná a nepřátelé nespadli z jednolité zdi.
if spawn_cas > 300:
spawn_cas -= 20
# Musíme časovač s tímto novým o 20ms kratším časem znovu "nastartovat"
pygame.time.set_timer(SPAWN_NEPRITELE, spawn_cas)
if stav == STAV_HRA:
# 2. LOGIKA
# Ovládání vesmírné lodi doprava a doleva držením tlačítek
klavesy = pygame.key.get_pressed()
if klavesy[pygame.K_LEFT] and hrac_x > 0:
hrac_x -= rychlost_hrace
if klavesy[pygame.K_RIGHT] and hrac_x < SIRKA - hrac_sirka:
hrac_x += rychlost_hrace
# --- POHYB VŠECH LASERŮ (KULEK) ---
# Projektily[:] dělá kopii listu, to se musí dělat vždy, když chci za běhu for-cyklu z toho pole
# věci rovnou odstraňovat! Kdybychom neodstraňovali kulky, list by narostl a hra spadla na paměť.
for p in projektily[:]:
p.y -= rychlost_projektilu # Y se ZMENŠUJE, protože kulka cestuje odspodu (např. Y=500) nahoru (Y=0)
# Když kulka odcestuje nad okraj obrazovky do hlubin kosmu, smažeme ji
if p.y < 0: projektily.remove(p)
# Hráč samotný obalený do neviditelného Rect rámečku pro vyhodnocování havárií
hrac_rect = pygame.Rect(hrac_x, hrac_y, hrac_sirka, hrac_vyska)
# --- POHYB VŠECH NEPŘÁTEL & KOLIZE ---
for n in nepratele[:]:
n.y += int(rychlost_nepritele) # Zvyšujeme Y -> Meteorit letí strmě dolů z nebes k podlaze
# 1. Havárie! Pokud meteorit doletí až úplně dolů přes obrazovku, NEBO narazí do Hráče
if n.y > VYSKA or n.colliderect(hrac_rect):
stav = STAV_KONEC
# 2. Křížová kontrola, zda SE STŘETLA KULKA S NEPŘÍTELEM (Laser do Meteoritu)
# Každého meteorita porovnáme znovu se všemi létajícími lasery na scéně!
for p in projektily[:]:
# .colliderect znamená "prolnuly se tyhle dva čtverce?"
if p.colliderect(n):
# BUM! Nastal obrovský výbuch.
# Zmizí Laser i Meteorit, tak je oba bezpečně (jen pokud už nebyli smazáni) vymažeme
if p in projektily: projektily.remove(p)
if n in nepratele: nepratele.remove(n)
skore += 10 # +10 bodů do kasičky!
# SLOVO 'break' ZMRAZÍ TENTO VNITŘNÍ FOR CYKLUS!
# Má to logiku - Pokud tento Nepřítel 'n' byl teď vteřinu zničen, už nedává smysl dál
# v tomto mikrosnímku procházet zbylé kulky a ptát se, jestli ho střelily taky. Přerušíme to.
break
# 3. KRESLENÍ GRAFIKY
okno.fill(CERNA)
if stav == STAV_MENU:
text = font_velky.render("VESMÍRNÁ STŘÍLEČKA", True, ZLUCA)
start = font_maly.render("Stiskni MEZERNÍK. Střílíš mezerníkem, pohyb šipkami.", True, BILA)
okno.blit(text, (SIRKA//2 - text.get_width()//2, 200))
okno.blit(start, (SIRKA//2 - start.get_width()//2, 300))
elif stav == STAV_HRA:
# Jsme ve hře! Nyní se z paměti převedou čísla Rect rámečků do reálných obrazců na monitoru.
# Naše loď
pygame.draw.rect(okno, ZELENA, hrac_rect)
# Smyčka vykreslí všechny Lasery co jsou aktuálně ve vzduchu
for p in projektily:
pygame.draw.rect(okno, ZLUCA, p)
# Ta samá smyčka vykreslí pluky všech padajících Nepřátel
for n in nepratele:
pygame.draw.rect(okno, CERVENA, n)
# Zobrazení skóre
skore_text = font_maly.render(f"Skóre: {skore}", True, BILA)
okno.blit(skore_text, (10, 10))
elif stav == STAV_KONEC:
text = font_velky.render("ZEMŘEL JSI!", True, CERVENA)
skore_text = font_velky.render(f"Tvé skóre: {skore}", True, BILA)
restart = font_maly.render("Stiskni MEZERNÍK pro novou hru", True, ZLUCA)
okno.blit(text, (SIRKA//2 - text.get_width()//2, 150))
okno.blit(skore_text, (SIRKA//2 - skore_text.get_width()//2, 250))
okno.blit(restart, (SIRKA//2 - restart.get_width()//2, 350))
# Obraz je spočítán. Posíláme do HDMI portu na monitor! (.flip)
pygame.display.flip()
hodiny.tick(60)
# PŘIDÁNO PRO WEB: Dáme prohlížeči šanci překreslit obrazovku
await asyncio.sleep(0)
pygame.quit()
sys.exit()
# PŘIDÁNO PRO WEB: Spuštění asynchronní hry
asyncio.run(main())

346
pygame/07_had.py

@ -1,174 +1,182 @@
import pygame import pygame
import asyncio # PŘIDÁNO PRO WEB: importujeme asyncio pro neblokující smyčku
import random import random
import sys import sys
# Povinná příprava knihovny Pygame # Povinná příprava knihovny Pygame
pygame.init()
# --- NASTAVENÍ OKNA A MŘÍŽKY ---
# Hra SNAKE (Had) většinou nefunguje na pixely jako ostatní hry, ale funguje na jakési neviditelné "Mřížce".
SIRKA_OKNA = 800
VYSKA_OKNA = 600
VELIKOST_DILKU = 20 # Velikost jednoho čtverečku na mřížce (dílku hada i jídla)
FPS = 15 # Rychlost hada. Zde je nižší 15, protože pohyb po skocích na mřížce nevyžaduje 60 FPS.
# Definice Palety Barev (Red, Green, Blue)
CERNA = (0, 0, 0)
BILA = (255, 255, 255)
ZELENA = (0, 255, 0)
CERVENA = (255, 0, 0)
okno = pygame.display.set_mode((SIRKA_OKNA, VYSKA_OKNA))
pygame.display.set_caption("Klasický Had (Snake)")
hodiny = pygame.time.Clock()
font = pygame.font.SysFont("arial", 36)
# Pomocná funkce pro rychlé vykreslení textu na daných x, y.
# Zkracuje to kód, abychom pořád neopakovali metody render() a blit().
def zobraz_text(text, barva, x, y):
text_plocha = font.render(text, True, barva)
okno.blit(text_plocha, (x, y))
# Veškerou logiku hry teď poprvé schováme do tzv. Hlavní Funkce.
# Proč? Jakmile funkce skončí (kvůli výhře/prohře), velmi jednoduše se tímto trikem dá celá hra resetovat,
# jelikož stačí funkci zavolat odznova.
def hlavni_smycka():
# --- POČÁTEČNÍ STAV HRY ---
# Nejdůležitější princip této hry! Tělo hada není jeden objekt, je to SEZNAM SOUŘADNIC (pole v poli).
# Každý článek jeho těla má své [x, y]. Ten ÚPLNĚ PRVNÍ prvek na pozici 0 je HLAVA HADA.
had_telo = [
[SIRKA_OKNA // 2, VYSKA_OKNA // 2], # Hlava
[SIRKA_OKNA // 2 - VELIKOST_DILKU, VYSKA_OKNA // 2], # Druhý článek břicha
[SIRKA_OKNA // 2 - 2 * VELIKOST_DILKU, VYSKA_OKNA // 2] # Ocas
]
# Směr pohybu! Tohle číslo přidáme při každém snímku k souřadnicím HLAVY.
# Tím ji virtuálně posuneme vpřed.
smer_x = VELIKOST_DILKU # Z počátku letí had doprava, protože z osy X roste pozitivně
smer_y = 0 # Do vertikály Y mu zpočátku nepřidáváme nic, letí rovně
# --- POZICE JÍDLA (ČERVENÝ ČTVEREČEK) ---
# Jídlo musíme nasměrovat přesně do nějaké buňky mřížky!
# Trik: random.randrange(od, do, krok) ... Jídlo padne například na X=20, 40, 60... ale NIKDY na 17, 33 atd.
# Kdyby nebylo zarovnané na mřížku, tak ho had miné!
jidlo_x = random.randrange(0, SIRKA_OKNA, VELIKOST_DILKU)
jidlo_y = random.randrange(0, VYSKA_OKNA, VELIKOST_DILKU)
skore = 0
konec_hry = False # Boolean kontrolka pro Game Over
# Nekonečná smyčka tohoto jednoho konkrétního zápasu
while True:
# 1. ZPRACOVÁNÍ UDÁLOSTÍ
for udalost in pygame.event.get():
if udalost.type == pygame.QUIT:
pygame.quit()
sys.exit()
# Detekce šipek pro změnu SMĚRU pohybu HADA
if udalost.type == pygame.KEYDOWN:
# OCHRANA: Had nemůže provést fyzicky nemožný obrat o 180 stupňů a zajet si rovnou do těla!
# Tzn. pokud zmáčknul šipku DOLEVA, a had letí rovně/doprava (smer_x == 0),
# teprve pak si smí dovolit nastavit změnu směru.
if udalost.key == pygame.K_LEFT and smer_x == 0:
smer_x = -VELIKOST_DILKU # Záporná hodnota pro směr vlevo
smer_y = 0 # Vertikální se ruší
elif udalost.key == pygame.K_RIGHT and smer_x == 0:
smer_x = VELIKOST_DILKU # Kladná hodnota pro směr vpravo
smer_y = 0
elif udalost.key == pygame.K_UP and smer_y == 0:
smer_x = 0
smer_y = -VELIKOST_DILKU # Záporná hodnota směrem vzhůru k Y=0
elif udalost.key == pygame.K_DOWN and smer_y == 0:
smer_x = 0
smer_y = VELIKOST_DILKU
# Pokud zemřel a bliká varování, čekáme na Mezerník
if udalost.key == pygame.K_SPACE and konec_hry:
# Pokud nastane RETURN, znamená to, že se celá tato 'hlavni_smycka()' okamžitě ukončí,
# její paměť (proměnné had_telo atd) se nenávratně vymaže.
# Následně ji však náš spouštěč úplně dole v souboru spustí naprosto na čisto znova. (RESTART)
return
# LOGIKA HRY: Provede se jenom pokud žijeme
if not konec_hry:
# --- ZÁKLADNÍ PRINCIP POHYBU HADA ---
# Jak hada posunout?
# 1. Spočítáme, kde se bude nacházet hadova hlava v PŘÍŠTÍM Snímku
# Vezmeme si pozici staré hlavy, neboli had_telo na nultém indexu
nova_hlava_x = had_telo[0][0] + smer_x
nova_hlava_y = had_telo[0][1] + smer_y
# 2. Tuto ZCELA NOVOU POZICI vsuneme pomocí "insert" na ÚPLNÝ ZAČÁTEK (index 0) našeho Seznamu
# Náš had je najednou o kousek delší! (Natáhl hlavu tam, kam letí)
had_telo.insert(0, [nova_hlava_x, nova_hlava_y])
# --- SEŽRÁNÍ JÍDLA (Růst hada) ---
# Zjišťujeme, zda se souřadnice nové hlavy náhodou rovnou nerovnají pozici Jídla na mřížce.
# Nepoužíváme obdélníkové kolize, protože pracujeme s přesnou mřížkou!
if nova_hlava_x == jidlo_x and nova_hlava_y == jidlo_y:
skore += 1 # Bod do tabulky!
# Zrušíme staré jídlo a najdeme mu na hracím plánu nové náhodné místo (zarovnané na mřížku)
jidlo_x = random.randrange(0, SIRKA_OKNA, VELIKOST_DILKU)
jidlo_y = random.randrange(0, VYSKA_OKNA, VELIKOST_DILKU)
# TOHLE JE DŮLEŽITÉ: Náš hadí Seznam byl momentálně obohacen o přední novou "hlavu", tzn vyrostl, a
# a my mu ten starý OCAS NEZKRÁTÍME. Takže je opravdu objektivně o jedno políčko natrvalo větší!
else:
# Pokud jídlo na této pozici NEBYLO, my musíme zachovat hada stále stejně dlouhého.
# Nahoře jsme mu před chvílí přidali hlavičku. Tady dole mu z konce seznamu (ocásku)
# jeden prvek odebereme (pomocí .pop()), takže vlastně provedl POHYB A JE STÁLE STEJNĚ DLOUHÝ.
had_telo.pop()
# --- SMRTELNÉ NÁRAZY (Stěna a Sebevražda) ---
# Pokud vylétla nová hlava mimo rozměry Okna...
if (nova_hlava_x < 0 or nova_hlava_x >= SIRKA_OKNA or
nova_hlava_y < 0 or nova_hlava_y >= VYSKA_OKNA):
konec_hry = True # GAME OVER
# Nebo pokud had sežral vlastního ocas!
# for-cyklus "for dilek in had_telo[1:]" prohlédne VŠE kromě samotné hlavy (začíná od indexu 1, čili tělo)
# A pokud se na jedné jediné z těchto pozic těla nachází nově nakreslená hlava, tak had do sebe kousl.
for dilek in had_telo[1:]:
if nova_hlava_x == dilek[0] and nova_hlava_y == dilek[1]:
konec_hry = True
# --- VYKRESLOVÁNÍ (Obarvování monitoru) ---
okno.fill(CERNA) # Umytí tabule na černo
# 1. Jídlo (Čtverec o rozměrech jedné buňky na mřížce)
pygame.draw.rect(okno, CERVENA, (jidlo_x, jidlo_y, VELIKOST_DILKU, VELIKOST_DILKU))
# 2. Celý samotný dlouhý had
# Projdeme seznam všech buněk a pro každičkou z nich na daném [X, Y] nakreslíme jeden zelený kvádr
for dilek in had_telo:
pygame.draw.rect(okno, ZELENA, (dilek[0], dilek[1], VELIKOST_DILKU, VELIKOST_DILKU))
zobraz_text(f"Skóre: {skore}", BILA, 10, 10)
if konec_hry:
# Zásadní hláška uprostřed pole o tom, ať to zkusí hráč znovu.
zobraz_text("Konec hry! Stiskni MEZERNÍK pro novou hru.", CERVENA, 100, VYSKA_OKNA // 2)
# Hotový frame se prohazuje s tím na monitoru
pygame.display.flip()
# Omezení rychlosti
hodiny.tick(FPS)
# Úplný spodek skriptu Pythonu
# Tento 'if' ověřuje, jestli Python zapnul tento soubor napřímo jako první.
if __name__ == "__main__":
# NEKONEČNÁ SLUČKA ŽIVOTA.
# Jakmile uvnitř `hlavni_smycka` zavoláme 'return' kvůli Game Over + stisku Mezerníku,
# funkce se zničí, a tento while True ji opět vzkřísí z prachu k novému životu s čistým listem skóre.
while True:
hlavni_smycka()
async def main(): # PŘIDÁNO PRO WEB: Zabalíme celou hru do asynchronní funkce
pygame.init()
# --- NASTAVENÍ OKNA A MŘÍŽKY ---
# Hra SNAKE (Had) většinou nefunguje na pixely jako ostatní hry, ale funguje na jakési neviditelné "Mřížce".
SIRKA_OKNA = 800
VYSKA_OKNA = 600
VELIKOST_DILKU = 20 # Velikost jednoho čtverečku na mřížce (dílku hada i jídla)
FPS = 15 # Rychlost hada. Zde je nižší 15, protože pohyb po skocích na mřížce nevyžaduje 60 FPS.
# Definice Palety Barev (Red, Green, Blue)
CERNA = (0, 0, 0)
BILA = (255, 255, 255)
ZELENA = (0, 255, 0)
CERVENA = (255, 0, 0)
okno = pygame.display.set_mode((SIRKA_OKNA, VYSKA_OKNA))
pygame.display.set_caption("Klasický Had (Snake)")
hodiny = pygame.time.Clock()
font = pygame.font.SysFont("arial", 36)
# Pomocná funkce pro rychlé vykreslení textu na daných x, y.
# Zkracuje to kód, abychom pořád neopakovali metody render() a blit().
def zobraz_text(text, barva, x, y):
text_plocha = font.render(text, True, barva)
okno.blit(text_plocha, (x, y))
# Veškerou logiku hry teď poprvé schováme do tzv. Hlavní Funkce.
# Proč? Jakmile funkce skončí (kvůli výhře/prohře), velmi jednoduše se tímto trikem dá celá hra resetovat,
# jelikož stačí funkci zavolat odznova.
def hlavni_smycka():
# --- POČÁTEČNÍ STAV HRY ---
# Nejdůležitější princip této hry! Tělo hada není jeden objekt, je to SEZNAM SOUŘADNIC (pole v poli).
# Každý článek jeho těla má své [x, y]. Ten ÚPLNĚ PRVNÍ prvek na pozici 0 je HLAVA HADA.
had_telo = [
[SIRKA_OKNA // 2, VYSKA_OKNA // 2], # Hlava
[SIRKA_OKNA // 2 - VELIKOST_DILKU, VYSKA_OKNA // 2], # Druhý článek břicha
[SIRKA_OKNA // 2 - 2 * VELIKOST_DILKU, VYSKA_OKNA // 2] # Ocas
]
# Směr pohybu! Tohle číslo přidáme při každém snímku k souřadnicím HLAVY.
# Tím ji virtuálně posuneme vpřed.
smer_x = VELIKOST_DILKU # Z počátku letí had doprava, protože z osy X roste pozitivně
smer_y = 0 # Do vertikály Y mu zpočátku nepřidáváme nic, letí rovně
# --- POZICE JÍDLA (ČERVENÝ ČTVEREČEK) ---
# Jídlo musíme nasměrovat přesně do nějaké buňky mřížky!
# Trik: random.randrange(od, do, krok) ... Jídlo padne například na X=20, 40, 60... ale NIKDY na 17, 33 atd.
# Kdyby nebylo zarovnané na mřížku, tak ho had miné!
jidlo_x = random.randrange(0, SIRKA_OKNA, VELIKOST_DILKU)
jidlo_y = random.randrange(0, VYSKA_OKNA, VELIKOST_DILKU)
skore = 0
konec_hry = False # Boolean kontrolka pro Game Over
# Nekonečná smyčka tohoto jednoho konkrétního zápasu
while True:
# 1. ZPRACOVÁNÍ UDÁLOSTÍ
for udalost in pygame.event.get():
if udalost.type == pygame.QUIT:
pygame.quit()
sys.exit()
# Detekce šipek pro změnu SMĚRU pohybu HADA
if udalost.type == pygame.KEYDOWN:
# OCHRANA: Had nemůže provést fyzicky nemožný obrat o 180 stupňů a zajet si rovnou do těla!
# Tzn. pokud zmáčknul šipku DOLEVA, a had letí rovně/doprava (smer_x == 0),
# teprve pak si smí dovolit nastavit změnu směru.
if udalost.key == pygame.K_LEFT and smer_x == 0:
smer_x = -VELIKOST_DILKU # Záporná hodnota pro směr vlevo
smer_y = 0 # Vertikální se ruší
elif udalost.key == pygame.K_RIGHT and smer_x == 0:
smer_x = VELIKOST_DILKU # Kladná hodnota pro směr vpravo
smer_y = 0
elif udalost.key == pygame.K_UP and smer_y == 0:
smer_x = 0
smer_y = -VELIKOST_DILKU # Záporná hodnota směrem vzhůru k Y=0
elif udalost.key == pygame.K_DOWN and smer_y == 0:
smer_x = 0
smer_y = VELIKOST_DILKU
# Pokud zemřel a bliká varování, čekáme na Mezerník
if udalost.key == pygame.K_SPACE and konec_hry:
# Pokud nastane RETURN, znamená to, že se celá tato 'hlavni_smycka()' okamžitě ukončí,
# její paměť (proměnné had_telo atd) se nenávratně vymaže.
# Následně ji však náš spouštěč úplně dole v souboru spustí naprosto na čisto znova. (RESTART)
return
# LOGIKA HRY: Provede se jenom pokud žijeme
if not konec_hry:
# --- ZÁKLADNÍ PRINCIP POHYBU HADA ---
# Jak hada posunout?
# 1. Spočítáme, kde se bude nacházet hadova hlava v PŘÍŠTÍM Snímku
# Vezmeme si pozici staré hlavy, neboli had_telo na nultém indexu
nova_hlava_x = had_telo[0][0] + smer_x
nova_hlava_y = had_telo[0][1] + smer_y
# 2. Tuto ZCELA NOVOU POZICI vsuneme pomocí "insert" na ÚPLNÝ ZAČÁTEK (index 0) našeho Seznamu
# Náš had je najednou o kousek delší! (Natáhl hlavu tam, kam letí)
had_telo.insert(0, [nova_hlava_x, nova_hlava_y])
# --- SEŽRÁNÍ JÍDLA (Růst hada) ---
# Zjišťujeme, zda se souřadnice nové hlavy náhodou rovnou nerovnají pozici Jídla na mřížce.
# Nepoužíváme obdélníkové kolize, protože pracujeme s přesnou mřížkou!
if nova_hlava_x == jidlo_x and nova_hlava_y == jidlo_y:
skore += 1 # Bod do tabulky!
# Zrušíme staré jídlo a najdeme mu na hracím plánu nové náhodné místo (zarovnané na mřížku)
jidlo_x = random.randrange(0, SIRKA_OKNA, VELIKOST_DILKU)
jidlo_y = random.randrange(0, VYSKA_OKNA, VELIKOST_DILKU)
# TOHLE JE DŮLEŽITÉ: Náš hadí Seznam byl momentálně obohacen o přední novou "hlavu", tzn vyrostl, a
# a my mu ten starý OCAS NEZKRÁTÍME. Takže je opravdu objektivně o jedno políčko natrvalo větší!
else:
# Pokud jídlo na této pozici NEBYLO, my musíme zachovat hada stále stejně dlouhého.
# Nahoře jsme mu před chvílí přidali hlavičku. Tady dole mu z konce seznamu (ocásku)
# jeden prvek odebereme (pomocí .pop()), takže vlastně provedl POHYB A JE STÁLE STEJNĚ DLOUHÝ.
had_telo.pop()
# --- SMRTELNÉ NÁRAZY (Stěna a Sebevražda) ---
# Pokud vylétla nová hlava mimo rozměry Okna...
if (nova_hlava_x < 0 or nova_hlava_x >= SIRKA_OKNA or
nova_hlava_y < 0 or nova_hlava_y >= VYSKA_OKNA):
konec_hry = True # GAME OVER
# Nebo pokud had sežral vlastního ocas!
# for-cyklus "for dilek in had_telo[1:]" prohlédne VŠE kromě samotné hlavy (začíná od indexu 1, čili tělo)
# A pokud se na jedné jediné z těchto pozic těla nachází nově nakreslená hlava, tak had do sebe kousl.
for dilek in had_telo[1:]:
if nova_hlava_x == dilek[0] and nova_hlava_y == dilek[1]:
konec_hry = True
# --- VYKRESLOVÁNÍ (Obarvování monitoru) ---
okno.fill(CERNA) # Umytí tabule na černo
# 1. Jídlo (Čtverec o rozměrech jedné buňky na mřížce)
pygame.draw.rect(okno, CERVENA, (jidlo_x, jidlo_y, VELIKOST_DILKU, VELIKOST_DILKU))
# 2. Celý samotný dlouhý had
# Projdeme seznam všech buněk a pro každičkou z nich na daném [X, Y] nakreslíme jeden zelený kvádr
for dilek in had_telo:
pygame.draw.rect(okno, ZELENA, (dilek[0], dilek[1], VELIKOST_DILKU, VELIKOST_DILKU))
zobraz_text(f"Skóre: {skore}", BILA, 10, 10)
if konec_hry:
# Zásadní hláška uprostřed pole o tom, ať to zkusí hráč znovu.
zobraz_text("Konec hry! Stiskni MEZERNÍK pro novou hru.", CERVENA, 100, VYSKA_OKNA // 2)
# Hotový frame se prohazuje s tím na monitoru
pygame.display.flip()
# Omezení rychlosti
hodiny.tick(FPS)
# PŘIDÁNO PRO WEB: Dáme prohlížeči šanci překreslit obrazovku
await asyncio.sleep(0)
# Úplný spodek skriptu Pythonu
# Tento 'if' ověřuje, jestli Python zapnul tento soubor napřímo jako první.
if __name__ == "__main__":
# NEKONEČNÁ SLUČKA ŽIVOTA.
# Jakmile uvnitř `hlavni_smycka` zavoláme 'return' kvůli Game Over + stisku Mezerníku,
# funkce se zničí, a tento while True ji opět vzkřísí z prachu k novému životu s čistým listem skóre.
while True:
hlavni_smycka()
# PŘIDÁNO PRO WEB: Spuštění asynchronní hry
asyncio.run(main())
Loading…
Cancel
Save