From 5ded3261107444cd5a0ce4103626e7ba25c9bdea Mon Sep 17 00:00:00 2001 From: skrabanek Date: Wed, 10 Jun 2026 12:19:47 +0200 Subject: [PATCH] Update Pygame examples with asyncio support for web compatibility --- pygame/01_pexeso.py | 428 ++++++++++++++++---------------- pygame/02_flappy_bird.py | 382 ++++++++++++++-------------- pygame/03_chytani_jablek.py | 278 +++++++++++---------- pygame/04_pong_dva_hraci.py | 386 ++++++++++++++-------------- pygame/05_skakacka_gravitace.py | 338 +++++++++++++------------ pygame/06_vesmirna_strilecka.py | 370 +++++++++++++-------------- pygame/07_had.py | 346 +++++++++++++------------- 7 files changed, 1292 insertions(+), 1236 deletions(-) diff --git a/pygame/01_pexeso.py b/pygame/01_pexeso.py index 43c551f..483432d 100644 --- a/pygame/01_pexeso.py +++ b/pygame/01_pexeso.py @@ -1,216 +1,224 @@ import pygame +import asyncio # PŘIDÁNO PRO WEB: importujeme asyncio pro neblokující smyčku import random 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): + rada = [] # Vytvoříme prázdný seznam pro jeden konkrétní řádek 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()) \ No newline at end of file diff --git a/pygame/02_flappy_bird.py b/pygame/02_flappy_bird.py index 5f905f7..9c6f33b 100644 --- a/pygame/02_flappy_bird.py +++ b/pygame/02_flappy_bird.py @@ -1,194 +1,202 @@ import pygame +import asyncio # PŘIDÁNO PRO WEB: importujeme asyncio pro neblokující smyčku import random 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: - 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_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) - # 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()) \ No newline at end of file diff --git a/pygame/03_chytani_jablek.py b/pygame/03_chytani_jablek.py index ce2d52b..e4ac6c3 100644 --- a/pygame/03_chytani_jablek.py +++ b/pygame/03_chytani_jablek.py @@ -1,140 +1,148 @@ import pygame +import asyncio # PŘIDÁNO PRO WEB: importujeme asyncio pro neblokující smyčku import sys import random # 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()) \ No newline at end of file diff --git a/pygame/04_pong_dva_hraci.py b/pygame/04_pong_dva_hraci.py index 578876d..673ade8 100644 --- a/pygame/04_pong_dva_hraci.py +++ b/pygame/04_pong_dva_hraci.py @@ -1,192 +1,200 @@ import pygame +import asyncio # PŘIDÁNO PRO WEB: importujeme asyncio pro neblokující smyčku 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()) \ No newline at end of file diff --git a/pygame/05_skakacka_gravitace.py b/pygame/05_skakacka_gravitace.py index b257573..b08cfcb 100644 --- a/pygame/05_skakacka_gravitace.py +++ b/pygame/05_skakacka_gravitace.py @@ -1,169 +1,177 @@ import pygame +import asyncio # PŘIDÁNO PRO WEB: importujeme asyncio pro neblokující smyčku import sys 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()) \ No newline at end of file diff --git a/pygame/06_vesmirna_strilecka.py b/pygame/06_vesmirna_strilecka.py index 9602ada..a9a001c 100644 --- a/pygame/06_vesmirna_strilecka.py +++ b/pygame/06_vesmirna_strilecka.py @@ -1,185 +1,193 @@ import pygame +import asyncio # PŘIDÁNO PRO WEB: importujeme asyncio pro neblokující smyčku import sys 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()) \ No newline at end of file diff --git a/pygame/07_had.py b/pygame/07_had.py index 8be7a3f..4997b22 100644 --- a/pygame/07_had.py +++ b/pygame/07_had.py @@ -1,174 +1,182 @@ import pygame +import asyncio # PŘIDÁNO PRO WEB: importujeme asyncio pro neblokující smyčku import random import sys # 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()) \ No newline at end of file