From ffeb939b990a327fb00adca16976068922f1b2e6 Mon Sep 17 00:00:00 2001 From: KubMakCZ Date: Fri, 29 May 2026 09:19:29 +0200 Subject: [PATCH] extra games & asset tutorial --- 35_pygame_zaklady/05_assety.py | 90 +++++++++ 35_pygame_zaklady/extra_01_chytani_jablek.py | 107 ++++++++++ 35_pygame_zaklady/extra_02_flappy_bird.py | 172 ++++++++++++++++ 35_pygame_zaklady/extra_03_had.py | 138 +++++++++++++ 35_pygame_zaklady/extra_04_pong_dva_hraci.py | 146 ++++++++++++++ .../extra_05_skakacka_gravitace.py | 141 +++++++++++++ .../extra_06_vesmirna_strilecka.py | 154 ++++++++++++++ 35_pygame_zaklady/extra_07_pexeso.py | 188 ++++++++++++++++++ 8 files changed, 1136 insertions(+) create mode 100644 35_pygame_zaklady/05_assety.py create mode 100644 35_pygame_zaklady/extra_01_chytani_jablek.py create mode 100644 35_pygame_zaklady/extra_02_flappy_bird.py create mode 100644 35_pygame_zaklady/extra_03_had.py create mode 100644 35_pygame_zaklady/extra_04_pong_dva_hraci.py create mode 100644 35_pygame_zaklady/extra_05_skakacka_gravitace.py create mode 100644 35_pygame_zaklady/extra_06_vesmirna_strilecka.py create mode 100644 35_pygame_zaklady/extra_07_pexeso.py diff --git a/35_pygame_zaklady/05_assety.py b/35_pygame_zaklady/05_assety.py new file mode 100644 index 0000000..9ef0891 --- /dev/null +++ b/35_pygame_zaklady/05_assety.py @@ -0,0 +1,90 @@ +import pygame +import sys +import os + +pygame.init() +pygame.mixer.init() # DŮLEŽITÉ: Inicializace zvukového modulu + +okno = pygame.display.set_mode((800, 600)) +pygame.display.set_caption("Lekce 5: Práce s obrázky a zvuky (Assety)") + +# ===================================================================== +# 1. NAČÍTÁNÍ OBRÁZKŮ +# ===================================================================== +# Pro správné načtení je nejlepší mít obrázky ve stejné složce jako skript. +# Doporučujeme formát .png (podporuje průhledné pozadí) nebo .jpg. +cesta_k_obrazku = "hrac.png" + +try: + # Načteme obrázek z disku + obrazek_hrace = pygame.image.load(cesta_k_obrazku) + + # DŮLEŽITÉ: convert_alpha() výrazně zrychlí vykreslování v Pygame + # a zachová průhlednost u PNG obrázků. + obrazek_hrace = obrazek_hrace.convert_alpha() + + # Pokud je obrázek moc velký, můžeme ho zmenšit (např. na 50x50 pixelů) + obrazek_hrace = pygame.transform.scale(obrazek_hrace, (50, 50)) + + obrazek_nacten = True +except FileNotFoundError: + print(f"POZOR: Obrázek '{cesta_k_obrazku}' nebyl nalezen! Nakreslím místo něj čtverec.") + obrazek_nacten = False + +# ===================================================================== +# 2. NAČÍTÁNÍ ZVUKŮ +# ===================================================================== +# Zvuky (.wav nebo .ogg - mp3 někdy v Pygame zlobí) +cesta_ke_zvuku = "skok.wav" + +try: + zvuk_skoku = pygame.mixer.Sound(cesta_ke_zvuku) + # Zvuku můžeme nastavit hlasitost (0.0 až 1.0) + zvuk_skoku.set_volume(0.5) + zvuk_nacten = True +except FileNotFoundError: + print(f"POZOR: Zvuk '{cesta_ke_zvuku}' nebyl nalezen! Hra poběží potichu.") + zvuk_nacten = False + + +# Pozice a barvy +x, y = 350, 250 +CERNA = (0, 0, 0) +MODRA = (0, 0, 255) +BILA = (255, 255, 255) + +font = pygame.font.SysFont("Arial", 24) + +bezime = True +while bezime: + for udalost in pygame.event.get(): + if udalost.type == pygame.QUIT: + bezime = False + + # Zvuk přehrajeme např. při stisku mezerníku + if udalost.type == pygame.KEYDOWN: + if udalost.key == pygame.K_SPACE: + if zvuk_nacten: + zvuk_skoku.play() # Přehraje zvuk + else: + print("PÍÍÍP! (Tady by hrál zvuk)") + + # Vykreslování + okno.fill(CERNA) + + text = font.render("Stiskni MEZERNÍK pro přehrání zvuku.", True, BILA) + okno.blit(text, (10, 10)) + + if obrazek_nacten: + # Místo pygame.draw.rect() použijeme okno.blit() pro vykreslení obrázku + okno.blit(obrazek_hrace, (x, y)) + else: + # Nouzové řešení, pokud obrázek chybí + pygame.draw.rect(okno, MODRA, (x, y, 50, 50)) + text_chyba = font.render(f"Chybí soubor {cesta_k_obrazku}", True, (255, 0, 0)) + okno.blit(text_chyba, (x, y - 30)) + + pygame.display.flip() + +pygame.quit() +sys.exit() diff --git a/35_pygame_zaklady/extra_01_chytani_jablek.py b/35_pygame_zaklady/extra_01_chytani_jablek.py new file mode 100644 index 0000000..57356f8 --- /dev/null +++ b/35_pygame_zaklady/extra_01_chytani_jablek.py @@ -0,0 +1,107 @@ +import pygame +import sys +import random + +# Inicializace knihovny Pygame (nutné zavolat na začátku každého Pygame programu) +pygame.init() + +# --- Nastavení okna --- +SIRKA = 800 +VYSKA = 600 +okno = pygame.display.set_mode((SIRKA, VYSKA)) +pygame.display.set_caption("Chytání jablek") + +# --- Barvy (R, G, B) --- +CERNA = (0, 0, 0) +ZELENA = (0, 255, 0) # Barva hráče (košíku) +CERVENA = (255, 0, 0) # Barva jablka +BILA = (255, 255, 255) # Barva textu + +# Hodiny pro udržení stabilní rychlosti hry (FPS) +hodiny = pygame.time.Clock() +font = pygame.font.SysFont("Arial", 36) + +# --- Proměnné Hráče (Košíku) --- +hrac_sirka = 100 +hrac_vyska = 20 +# Začínáme uprostřed obrazovky dole +hrac_x = SIRKA // 2 - hrac_sirka // 2 +hrac_y = VYSKA - 40 +rychlost_hrace = 8 + +# --- Proměnné Jablka --- +jablko_velikost = 30 +# Jablko začne na náhodné X pozici, ale nahoře mimo obrazovku (Y = záporné) +jablko_x = random.randint(0, SIRKA - jablko_velikost) +jablko_y = -jablko_velikost +rychlost_jablka = 5 + +skore = 0 +zivoty = 3 + +bezime = True +# --- Hlavní herní smyčka --- +while bezime: + # 1. Zpracování událostí (kliknutí na křížek pro zavření okna) + for udalost in pygame.event.get(): + if udalost.type == pygame.QUIT: + bezime = False + + # Pokud ještě máme životy, hrajeme + if zivoty > 0: + # 2. Logika pohybu hráče (čtení aktuálně stisknutých kláves) + klavesy = pygame.key.get_pressed() + if klavesy[pygame.K_LEFT] and hrac_x > 0: + hrac_x -= rychlost_hrace + # Omezení, aby hráč nevyjel zprava ven + if klavesy[pygame.K_RIGHT] and hrac_x < SIRKA - hrac_sirka: + hrac_x += rychlost_hrace + + # 3. Logika padání jablka (měníme pouze osu Y) + jablko_y += rychlost_jablka + + # --- KOLIZE (Zjištění, zda hráč chytil jablko) --- + # Vytvoříme si virtuální obdélníky (Rect) pro zjednodušení detekce + hrac_rect = pygame.Rect(hrac_x, hrac_y, hrac_sirka, hrac_vyska) + jablko_rect = pygame.Rect(jablko_x, jablko_y, jablko_velikost, jablko_velikost) + + # Metoda colliderect zjistí, zda se dva obdélníky právě teď překrývají + if hrac_rect.colliderect(jablko_rect): + skore += 1 + rychlost_jablka += 0.2 # Hra se postupně stává těžší (jablka padají rychleji) + + # Vygenerování nového jablka nahoře + jablko_y = -jablko_velikost + jablko_x = random.randint(0, SIRKA - jablko_velikost) + + # Co když jablko spadne na zem a my ho nechytíme? + elif jablko_y > VYSKA: + zivoty -= 1 + # Vygenerování nového jablka nahoře pro další pokus + jablko_y = -jablko_velikost + jablko_x = random.randint(0, SIRKA - jablko_velikost) + + # 4. Vykreslování na obrazovku + okno.fill(CERNA) # Vymazání předchozího snímku z obrazovky + + if zivoty > 0: + # Vykreslení hráče + pygame.draw.rect(okno, ZELENA, (hrac_x, hrac_y, hrac_sirka, hrac_vyska)) + # Vykreslení jablka + pygame.draw.rect(okno, CERVENA, (jablko_x, jablko_y, jablko_velikost, jablko_velikost)) + + # Vykreslení textů (Skóre a Životy) + text_skore = font.render(f"Skóre: {skore}", True, BILA) + text_zivoty = font.render(f"Životy: {zivoty}", True, BILA) + okno.blit(text_skore, (10, 10)) # blit znamená "překresli tento text na okno" + okno.blit(text_zivoty, (SIRKA - 150, 10)) + else: + # Pokud došly životy, zobrazíme červený nápis Konec Hry uprostřed + text_konec = font.render(f"KONEC HRY! Tvé skóre: {skore}", True, CERVENA) + okno.blit(text_konec, (SIRKA//2 - 180, VYSKA//2)) + + pygame.display.flip() # Propíšeme vše na monitor pro hráče + hodiny.tick(60) # Udržujeme rychlost na stabilních 60 snímcích za sekundu (FPS) + +pygame.quit() +sys.exit() diff --git a/35_pygame_zaklady/extra_02_flappy_bird.py b/35_pygame_zaklady/extra_02_flappy_bird.py new file mode 100644 index 0000000..a37b359 --- /dev/null +++ b/35_pygame_zaklady/extra_02_flappy_bird.py @@ -0,0 +1,172 @@ +import pygame +import random +import sys + +# Inicializace Pygame +pygame.init() + +# --- Nastavení okna a základních konstant --- +SIRKA_OKNA = 400 +VYSKA_OKNA = 600 +FPS = 60 # Vyšší FPS pro plynulejší pád + +# Barvy (R, G, B) +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 +okno = pygame.display.set_mode((SIRKA_OKNA, VYSKA_OKNA)) +pygame.display.set_caption("Flappy Bird") + +# Hodiny pro řízení rychlosti hry +hodiny = pygame.time.Clock() + +# Písma pro texty +font_velky = pygame.font.SysFont("arial", 48, bold=True) +font_maly = pygame.font.SysFont("arial", 24) + +def zobraz_text(text, font, barva, x, y, zarovnat_stred=False): + """Pomocná funkce pro text. Umí vycentrovat text podle zadané pozice x, y.""" + plocha = font.render(text, True, barva) + if zarovnat_stred: + rect = plocha.get_rect(center=(x, y)) + okno.blit(plocha, rect) + else: + okno.blit(plocha, (x, y)) + +def hlavni_smycka(): + # --- Proměnné Ptáka --- + ptak_x = 50 + ptak_y = VYSKA_OKNA // 2 + ptak_sirka = 30 + ptak_vyska = 30 + + rychlost_padu = 0 # Aktuální vertikální rychlost + gravitace = 0.5 # Jak moc ptáka přitahuje zem v každém snímku + sila_skoku = -8 # Negativní hodnota posouvá ptáka nahoru + + # --- Proměnné Překážek (Trubek) --- + trubka_sirka = 60 + mezera_mezi_trubkami = 160 # Výšková mezera kudy pták prolétá + rychlost_posunu = 3 # Rychlost, kterou jedou trubky doleva + + # Seznam překážek. Budeme ukládat slovníky se souřadnicemi. + # Např: {"x": 400, "y_stred": 300} -> 'y_stred' určuje, kde je střed mezery pro průlet. + trubky = [] + + # Funkce, která náhodně vymyslí, kde bude mezera, a přidá trubku do seznamu + def pridej_trubku(x_pozice): + # Střed mezery nebude příliš blízko u okraje (150 pixelů od okrajů) + stred_mezery = random.randint(150, VYSKA_OKNA - 150) + trubky.append({"x": x_pozice, "y_stred": stred_mezery}) + + # Přidáme první dvě překážky na začátek (úplně mimo obrazovku vpravo) + pridej_trubku(SIRKA_OKNA + 100) + pridej_trubku(SIRKA_OKNA + 400) # Druhá trubka bude o 300 pixelů dále + + skore = 0 + # Stavy hry jsou výborné pro pochopení "Stavových automatů" (State Machines) + stav_hry = "START" # Stavy: "START", "HRA", "KONEC" + + # --- Hlavní herní smyčka --- + while True: + # 1. Zpracování událostí (klávesnice) + for udalost in pygame.event.get(): + if udalost.type == pygame.QUIT: + pygame.quit() + sys.exit() + + if udalost.type == pygame.KEYDOWN: + if udalost.key == pygame.K_SPACE: + # Co dělá mezerník záleží na tom, v jakém stavu je hra + if stav_hry == "START": + stav_hry = "HRA" # Zapneme hru a ihned skočíme + rychlost_padu = sila_skoku + elif stav_hry == "HRA": + rychlost_padu = sila_skoku # Skok nahoru + elif stav_hry == "KONEC": + return # Vyskočíme z této funkce, aby vnější smyčka spustila hru od znova + + # 2. Logika a pohyb + if stav_hry == "HRA": + # Pták padá vlivem gravitace + rychlost_padu += gravitace + ptak_y += rychlost_padu + + # Posouváme všechny trubky doleva + for t in trubky: + t["x"] -= rychlost_posunu + + # Odstranění trubky, která celá zmizela za levým okrajem obrazovky + if trubky[0]["x"] < -trubka_sirka: + trubky.pop(0) # Smaže první trubku v seznamu + + # Spočítáme pozici X té poslední trubky v seznamu a novou dáme za ni + posledni_trubka_x = trubky[-1]["x"] + pridej_trubku(posledni_trubka_x + 300) + + # Pokud zmizí trubka (pták ji přeletěl), získáme bod! + skore += 1 + + # --- Detekce kolizí (nárazů) --- + # Vytvoříme 'Neviditelný obdélník' (Rect) kolem ptáka pro snadnější zjišťování kolizí + ptak_rect = pygame.Rect(ptak_x, ptak_y, ptak_sirka, ptak_vyska) + + # Náraz do stropu nebo podlahy okna + if ptak_y < 0 or ptak_y + ptak_vyska > VYSKA_OKNA: + stav_hry = "KONEC" + + # Náraz do trubek + for t in trubky: + # Rect pro horní zelenou trubku + horni_vyska = t["y_stred"] - (mezera_mezi_trubkami // 2) + horni_rect = pygame.Rect(t["x"], 0, trubka_sirka, horni_vyska) + + # Rect pro dolní zelenou trubku + dolni_y = t["y_stred"] + (mezera_mezi_trubkami // 2) + dolni_rect = pygame.Rect(t["x"], dolni_y, trubka_sirka, VYSKA_OKNA - dolni_y) + + # Pokud se náš obdélník ptáka překrývá s horní nebo dolní trubkou, je konec! + if ptak_rect.colliderect(horni_rect) or ptak_rect.colliderect(dolni_rect): + stav_hry = "KONEC" + + # 3. Vykreslování + okno.fill(MODRA_OBLOHA) + + # Vykreslení trubek + for t in trubky: + # Horní trubka + horni_vyska = t["y_stred"] - (mezera_mezi_trubkami // 2) + pygame.draw.rect(okno, ZELENA, (t["x"], 0, trubka_sirka, horni_vyska)) + + # Dolní trubka + dolni_y = t["y_stred"] + (mezera_mezi_trubkami // 2) + pygame.draw.rect(okno, ZELENA, (t["x"], dolni_y, trubka_sirka, VYSKA_OKNA - dolni_y)) + + # Vykreslení ptáka (žlutý čtvereček) + pygame.draw.rect(okno, ZLUTYP_PTAK, (ptak_x, int(ptak_y), ptak_sirka, ptak_vyska)) + + # Texty přes obrazovku podle toho, v jakém stavu hra je + 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": + # Konec hry - tisk statistik uprostřed + 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) + + pygame.display.flip() # Prohození bufferu na obrazovku + hodiny.tick(FPS) # Udržuje hru na správné rychlosti snímků + +# Takzvaný "Entry point" - zde program skutečně začne vykonávat kód +if __name__ == "__main__": + while True: + hlavni_smycka() diff --git a/35_pygame_zaklady/extra_03_had.py b/35_pygame_zaklady/extra_03_had.py new file mode 100644 index 0000000..33c274c --- /dev/null +++ b/35_pygame_zaklady/extra_03_had.py @@ -0,0 +1,138 @@ +import pygame +import random +import sys + +# Inicializace Pygame +pygame.init() + +# --- Nastavení okna a základních konstant --- +SIRKA_OKNA = 800 +VYSKA_OKNA = 600 +VELIKOST_DILKU = 20 # Velikost jednoho čtverečku hada a jídla +FPS = 15 # Rychlost hry (počet snímků za sekundu) + +# Barvy (R, G, B) +CERNA = (0, 0, 0) +BILA = (255, 255, 255) +ZELENA = (0, 255, 0) +CERVENA = (255, 0, 0) + +# Vytvoření okna +okno = pygame.display.set_mode((SIRKA_OKNA, VYSKA_OKNA)) +pygame.display.set_caption("Klasický Had (Snake)") + +# Hodiny pro řízení rychlosti hry +hodiny = pygame.time.Clock() +font = pygame.font.SysFont("arial", 36) + +def zobraz_text(text, barva, x, y): + """Pomocná funkce pro vykreslení textu na obrazovku.""" + text_plocha = font.render(text, True, barva) + okno.blit(text_plocha, (x, y)) + +def hlavni_smycka(): + # --- Počáteční stav hry --- + # Had je reprezentován seznamem souřadnic [x, y]. První prvek je hlava. + had_telo = [ + [SIRKA_OKNA // 2, VYSKA_OKNA // 2], + [SIRKA_OKNA // 2 - VELIKOST_DILKU, VYSKA_OKNA // 2], + [SIRKA_OKNA // 2 - 2 * VELIKOST_DILKU, VYSKA_OKNA // 2] + ] + + # Směr pohybu (změna x, změna y) + smer_x = VELIKOST_DILKU + smer_y = 0 + + # Jídlo - náhodná pozice zarovnaná na mřížku + jidlo_x = random.randrange(0, SIRKA_OKNA, VELIKOST_DILKU) + jidlo_y = random.randrange(0, VYSKA_OKNA, VELIKOST_DILKU) + + skore = 0 + konec_hry = False + + # --- Hlavní herní smyčka --- + while True: + # 1. Zpracování událostí (klávesnice, křížek) + for udalost in pygame.event.get(): + if udalost.type == pygame.QUIT: + pygame.quit() + sys.exit() + + # Změna směru pomocí šipek + if udalost.type == pygame.KEYDOWN: + # Zabráníme otočení o 180 stupňů (had nemůže couvat do sebe) + if udalost.key == pygame.K_LEFT and smer_x == 0: + smer_x = -VELIKOST_DILKU + smer_y = 0 + elif udalost.key == pygame.K_RIGHT and smer_x == 0: + smer_x = VELIKOST_DILKU + smer_y = 0 + elif udalost.key == pygame.K_UP and smer_y == 0: + smer_x = 0 + smer_y = -VELIKOST_DILKU + elif udalost.key == pygame.K_DOWN and smer_y == 0: + smer_x = 0 + smer_y = VELIKOST_DILKU + + # Pokud je hra u konce, stiskem mezerníku ji restartujeme + if udalost.key == pygame.K_SPACE and konec_hry: + return # Ukončí tuto iteraci smyčky a spustí hru znovu + + if not konec_hry: + # 2. Logika hry (pohyb a kolize) + + # Vypočítáme novou pozici hlavy + nova_hlava_x = had_telo[0][0] + smer_x + nova_hlava_y = had_telo[0][1] + smer_y + + # Přidáme novou hlavu na začátek seznamu + had_telo.insert(0, [nova_hlava_x, nova_hlava_y]) + + # Kontrola kolize s jídlem + if nova_hlava_x == jidlo_x and nova_hlava_y == jidlo_y: + skore += 1 + # Vygenerujeme nové jídlo jinde + jidlo_x = random.randrange(0, SIRKA_OKNA, VELIKOST_DILKU) + jidlo_y = random.randrange(0, VYSKA_OKNA, VELIKOST_DILKU) + # DŮLEŽITÉ: Pokud had sní jídlo, neodmažeme ocas. Tím had povyroste! + else: + # Pokud had jídlo nesnědl, odmažeme ocas, aby délka zůstala stejná + had_telo.pop() + + # Kontrola kolize se stěnami (náraz do okraje 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 + + # Kontrola kolize hada se sebou samým + # Pokud nová pozice hlavy je na stejném místě jako jakýkoli jiný dílek těla + for dilek in had_telo[1:]: + if nova_hlava_x == dilek[0] and nova_hlava_y == dilek[1]: + konec_hry = True + + # 3. Vykreslování + okno.fill(CERNA) # Vymažeme obrazovku (černé pozadí) + + # Vykreslíme jídlo jako červený čtverec + pygame.draw.rect(okno, CERVENA, (jidlo_x, jidlo_y, VELIKOST_DILKU, VELIKOST_DILKU)) + + # Vykreslíme tělo hada jako zelené čtverce + for dilek in had_telo: + pygame.draw.rect(okno, ZELENA, (dilek[0], dilek[1], VELIKOST_DILKU, VELIKOST_DILKU)) + + # Zobrazíme skóre + zobraz_text(f"Skóre: {skore}", BILA, 10, 10) + + # Pokud hra skončila, zobrazíme text pro restart + if konec_hry: + zobraz_text("Konec hry! Stiskni MEZERNÍK pro novou hru.", CERVENA, 100, VYSKA_OKNA // 2) + + pygame.display.flip() # Propíšeme změny na obrazovku + + # 4. Časování - čekání, aby hra běžela na zadaný počet FPS + hodiny.tick(FPS) + +# Spouštíme hru v nekonečné smyčce, aby po Game Over a mezerníku začala znovu +if __name__ == "__main__": + while True: + hlavni_smycka() diff --git a/35_pygame_zaklady/extra_04_pong_dva_hraci.py b/35_pygame_zaklady/extra_04_pong_dva_hraci.py new file mode 100644 index 0000000..fb3e3d9 --- /dev/null +++ b/35_pygame_zaklady/extra_04_pong_dva_hraci.py @@ -0,0 +1,146 @@ +import pygame +import sys + +pygame.init() + +# --- Nastavení okna --- +SIRKA = 800 +VYSKA = 600 +okno = pygame.display.set_mode((SIRKA, VYSKA)) +pygame.display.set_caption("Pong - Plná Hra") + +# --- Barvy --- +BILA = (255, 255, 255) +CERNA = (0, 0, 0) +ZELENA = (0, 255, 0) + +hodiny = pygame.time.Clock() +font_velky = pygame.font.SysFont("Arial", 50) +font_maly = pygame.font.SysFont("Arial", 30) + +# --- Stavový automat --- +# Hra může být ve 3 různých stavech. Díky tomu víme, jaká pravidla zrovna platí a co kreslit +STAV_MENU = 0 +STAV_HRA = 1 +STAV_KONEC = 2 +stav = STAV_MENU # Na začátku jsme v menu + +# --- Proměnné hráčů (pálek) a míčku --- +palka_sirka = 15 +palka_vyska = 100 +rychlost_palky = 7 +micek_velikost = 15 + +# Funkce pro resetování proměnných na začátek (použije se při nové hře) +def reset_hry(): + # Pomocí klíčového slova 'global' říkáme funkci, aby upravila proměnné venku (celkové proměnné) + global hrac1_y, hrac2_y, skore1, skore2, micek_x, micek_y, micek_rychlost_x, micek_rychlost_y + hrac1_y = VYSKA // 2 - palka_vyska // 2 + hrac2_y = VYSKA // 2 - palka_vyska // 2 + skore1 = 0 + skore2 = 0 + micek_x = SIRKA // 2 - micek_velikost // 2 + micek_y = VYSKA // 2 - micek_velikost // 2 + micek_rychlost_x = 5 + micek_rychlost_y = 5 + +reset_hry() # Zavoláme ihned, abychom pro začátek vůbec měli výchozí hodnoty + +bezime = True +while bezime: + # 1. Zpracování událostí + for udalost in pygame.event.get(): + if udalost.type == pygame.QUIT: + bezime = False + + # Reakce na stisk kláves nezávisle na plynulém pohybu (např. pouhé stisknutí mezerníku) + if udalost.type == pygame.KEYDOWN: + if stav == STAV_MENU and udalost.key == pygame.K_SPACE: + stav = STAV_HRA # Z menu se posuneme rovnou do hry + elif stav == STAV_KONEC and udalost.key == pygame.K_SPACE: + reset_hry() # Z konce hry ji vyresetujeme a jdeme hrát + stav = STAV_HRA + + klavesy = pygame.key.get_pressed() + + # 2. Logika se provádí POUZE tehdy, když zrovna HRAJEME + if stav == STAV_HRA: + # Ovládání hráče 1 vlevo (Klávesy W a S) + if klavesy[pygame.K_w] and hrac1_y > 0: + hrac1_y -= rychlost_palky + 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 + + # Pohyb míčku (k X a Y se neustále přičítá aktuální rychlost) + micek_x += micek_rychlost_x + micek_y += micek_rychlost_y + + # --- Odraz míčku od horní a dolní stěny okna --- + # Trik: Vynásobením rychlosti číslem -1 se obrátí směr pohybu na opačný! + if micek_y <= 0 or micek_y >= VYSKA - micek_velikost: + micek_rychlost_y *= -1 + + # --- Odraz od pálek hráčů (kolize obdélníků) --- + rect_micek = pygame.Rect(micek_x, micek_y, micek_velikost, micek_velikost) + rect_hrac1 = pygame.Rect(30, hrac1_y, palka_sirka, palka_vyska) + rect_hrac2 = pygame.Rect(SIRKA - 30 - palka_sirka, hrac2_y, palka_sirka, palka_vyska) + + if rect_micek.colliderect(rect_hrac1) or rect_micek.colliderect(rect_hrac2): + micek_rychlost_x *= -1.1 # Po odrazu otočíme směr (do stran) a míček trošku zrychlíme (o 10%) + + # --- Zjištění gólu (míček vyletí z obrazovky ven vpravo nebo vlevo) --- + if micek_x < 0: + skore2 += 1 # Gól pro hráče 2 + micek_x, micek_y = SIRKA // 2, VYSKA // 2 # Reset na střed okna + micek_rychlost_x = 5 # Vrácení pomalejší rychlosti a míření na pravou stranu + elif micek_x > SIRKA: + skore1 += 1 # Gól pro hráče 1 + micek_x, micek_y = SIRKA // 2, VYSKA // 2 # Reset na střed okna + micek_rychlost_x = -5 # Míření na levou stranu + + # --- Podmínka pro vítězství ve hře --- + if skore1 >= 5 or skore2 >= 5: + stav = STAV_KONEC + + # 3. Vykreslování + okno.fill(CERNA) + + # Co přesně kreslíme? Záleží na tom, v jakém jsme stavu! + if stav == STAV_MENU: + nadpis = font_velky.render("PONG", True, BILA) + navod = font_maly.render("Stiskni MEZERNÍK pro start", True, ZELENA) + okno.blit(nadpis, (SIRKA//2 - nadpis.get_width()//2, 200)) + okno.blit(navod, (SIRKA//2 - navod.get_width()//2, 300)) + + elif stav == STAV_HRA: + # Vykreslení půlící čáry uprostřed (aaline znamená anti-aliased, tedy hezky vyhlazená čára) + pygame.draw.aaline(okno, BILA, (SIRKA // 2, 0), (SIRKA // 2, VYSKA)) + + # Vykreslení pálek a míčku z našich Rect obdélníků + pygame.draw.rect(okno, BILA, rect_hrac1) + pygame.draw.rect(okno, BILA, rect_hrac2) + pygame.draw.ellipse(okno, BILA, rect_micek) # ellipse z obdélníku udělá kruh/ovál + + # Skóre nahoře uprostřed + 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. zkráceného if/else (ternárního operátoru) + vitez = "Hráč 1" if skore1 >= 5 else "Hráč 2" + text_vitez = font_velky.render(f"{vitez} vyhrál!", True, ZELENA) + text_restart = font_maly.render("Stiskni MEZERNÍK pro novou hru", True, BILA) + okno.blit(text_vitez, (SIRKA//2 - text_vitez.get_width()//2, 200)) + okno.blit(text_restart, (SIRKA//2 - text_restart.get_width()//2, 300)) + + pygame.display.flip() + hodiny.tick(60) + +pygame.quit() +sys.exit() diff --git a/35_pygame_zaklady/extra_05_skakacka_gravitace.py b/35_pygame_zaklady/extra_05_skakacka_gravitace.py new file mode 100644 index 0000000..69b6085 --- /dev/null +++ b/35_pygame_zaklady/extra_05_skakacka_gravitace.py @@ -0,0 +1,141 @@ +import pygame +import sys +import random + +pygame.init() + +SIRKA = 800 +VYSKA = 600 +okno = pygame.display.set_mode((SIRKA, VYSKA)) +pygame.display.set_caption("Skákačka - Plná Hra") + +# --- Barvy --- +BILA = (255, 255, 255) +CERNA = (0, 0, 0) +MODRA = (50, 150, 255) # Obloha +ZELENA = (50, 200, 50) # Země +CERVENA = (255, 0, 0) # Překážky + +hodiny = pygame.time.Clock() +font = pygame.font.SysFont("Arial", 40) +font_maly = pygame.font.SysFont("Arial", 25) + +# --- Stavový automat --- +STAV_MENU = 0 +STAV_HRA = 1 +STAV_KONEC = 2 +stav = STAV_MENU + +# --- Nastavení hráče a fyziky --- +podlaha_y = VYSKA - 50 # Odkud začíná tráva (y souřadnice) +hrac_sirka = 40 +hrac_vyska = 40 + +# Gravitace v počítačové hře znamená, že rychlost pádu se KAZDÝM SNÍMKEM (FPS) zvyšuje! +gravitace = 0.6 +sila_skoku = -12 # Skok funguje jako nastavení nárazové záporné rychlosti (směr vzhůru) + +# --- Funkce pro reset --- +def reset_hry(): + global hrac_x, hrac_y, rychlost_y, na_zemi, prekazky, skore, rychlost_hry + hrac_x = 100 + hrac_y = podlaha_y - hrac_vyska + rychlost_y = 0 + na_zemi = True # Aby mohl hráč vyskočit, musí samozřejmě stát pevně na zemi + + prekazky = [] # Seznam všech aktuálních překážek v obrazovce, začíná prázdný + skore = 0 + rychlost_hry = 6 + +reset_hry() + +# --- Vlastní Časovač (Custom Event) --- +# Tímto si vytvoříme VLASTNÍ UDÁLOST podobnou pygame.QUIT, která se automaticky spustí každých 1500 milisekund (1.5s) +SPAWN_PREKAZKY = pygame.USEREVENT + 1 +pygame.time.set_timer(SPAWN_PREKAZKY, 1500) + +bezime = True +while bezime: + for udalost in pygame.event.get(): + if udalost.type == pygame.QUIT: + bezime = False + + 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 se skáče? Jen když se stiskne šipka nahoru A ZÁROVEŇ postava stojí na zemi + elif stav == STAV_HRA and udalost.key == pygame.K_UP and na_zemi: + rychlost_y = sila_skoku # Dáme mu masivní impuls směrem nahoru + na_zemi = False # Už není na zemi, takže nesmí znovu vyskočit (zabráníme double skokům) + + # Zpracování naší vlastní události pro přidání nové překážky + if udalost.type == SPAWN_PREKAZKY and stav == STAV_HRA: + # Přidáme nový pygame.Rect() překážky úplně na pravý konec obrazovky (SIRKA) + prekazky.append(pygame.Rect(SIRKA, podlaha_y - 40, 30, 40)) + rychlost_hry += 0.1 # Postupně celou hru zrychlujeme, čím déle hráč hraje + + if stav == STAV_HRA: + # --- Fyzika postavy --- + rychlost_y += gravitace # Gravitace neustále táhne rychlost do kladných hodnot (směrem dolů) + hrac_y += rychlost_y # Aplikujeme tuto rychlost na aktuální Y pozici hráče + + # Zastavení o podlahu, aby se hráč nepropadl skrz texturu do nekonečna + if hrac_y + hrac_vyska >= podlaha_y: + hrac_y = podlaha_y - hrac_vyska # Srovnáme ho přesně s podlahou + rychlost_y = 0 # Rychlost pádu je najednou nula + na_zemi = True # Zase může skákat + + hrac_rect = pygame.Rect(hrac_x, hrac_y, hrac_sirka, hrac_vyska) + + # --- Překážky --- + # Trik prekazky[:] znamená, že for-cyklus prochází přes KOPII seznamu. + # To nám umožňuje během cyklu prvky ze seznamu (z toho originálního) bezpečně odstraňovat + # bez toho, aby for-cyklus 'přeskočil' další prvky kvůli změně délky seznamu. + for p in prekazky[:]: + p.x -= int(rychlost_hry) # Překážka se hýbe směrem k hráči doleva + + # Pokud překážka narazí přímo do hráče + if p.colliderect(hrac_rect): + stav = STAV_KONEC + + # Pokud překážka zajela celá za levý okraj (x < -30) + if p.x < -30: + prekazky.remove(p) # Můžeme smazat + skore += 1 # Získáme bod za úspěšné přeskočení + + # --- Vykreslování --- + okno.fill(MODRA) # Obloha + # Tráva/podlaha (nakreslí se obdélník od y = podlaha_y až úplně dolů) + 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: + pygame.draw.rect(okno, CERNA, hrac_rect) # Nakreslení Hráče + for p in prekazky: + pygame.draw.rect(okno, CERVENA, p) # Nakreslení všech aktuálních překážek + + 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)) + + pygame.display.flip() + hodiny.tick(60) + +pygame.quit() +sys.exit() diff --git a/35_pygame_zaklady/extra_06_vesmirna_strilecka.py b/35_pygame_zaklady/extra_06_vesmirna_strilecka.py new file mode 100644 index 0000000..e0094d4 --- /dev/null +++ b/35_pygame_zaklady/extra_06_vesmirna_strilecka.py @@ -0,0 +1,154 @@ +import pygame +import sys +import random + +pygame.init() + +SIRKA = 800 +VYSKA = 600 +okno = pygame.display.set_mode((SIRKA, VYSKA)) +pygame.display.set_caption("Vesmírná střílečka - Plná Hra") + +# --- Barvy --- +CERNA = (0, 0, 0) +ZELENA = (0, 255, 0) # Hráč (naše loď) +CERVENA = (255, 0, 0) # Nepřátelé (meteority) +ZLUCA = (255, 255, 0) # Projektily (Lasery) +BILA = (255, 255, 255) # Text + +hodiny = pygame.time.Clock() +font_velky = pygame.font.SysFont("Arial", 50) +font_maly = pygame.font.SysFont("Arial", 30) + +STAV_MENU = 0 +STAV_HRA = 1 +STAV_KONEC = 2 +stav = STAV_MENU + +hrac_sirka = 40 +hrac_vyska = 40 +rychlost_hrace = 6 +rychlost_projektilu = 10 # Jak rychle letí laser nahoru + +# Vytvoření vlastního časovače pro přidávání nepřátel +SPAWN_NEPRITELE = pygame.USEREVENT + 1 + +# Funkce pro reset a začátek zbrusu nové hry +def reset_hry(): + global hrac_x, hrac_y, projektily, nepratele, skore, rychlost_nepritele, spawn_cas + hrac_x = SIRKA // 2 - hrac_sirka // 2 + hrac_y = VYSKA - 60 + + projektily = [] # Seznam na letící kulky (lasery) + nepratele = [] # Seznam na padající obdélníky + + skore = 0 + rychlost_nepritele = 2.0 + spawn_cas = 1000 # Čas k přidání nového nepřítele v ms (1 sekunda) + # Zapneme náš vytvořený časovač na zvolený interval + pygame.time.set_timer(SPAWN_NEPRITELE, spawn_cas) + +reset_hry() + +bezime = True +while bezime: + for udalost in pygame.event.get(): + if udalost.type == pygame.QUIT: + bezime = False + + 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 --- + # Když stiskneme mezerník, spočítáme střed hráče a vystřelíme nahoru + projektil_x = hrac_x + hrac_sirka // 2 - 5 + # Přidáme nový obdélník laseru do seznamu kulek + projektily.append(pygame.Rect(projektil_x, hrac_y, 10, 20)) + + # Když vyprší náš nastavený časovač SPAWN_NEPRITELE (tzn. uběhla sekunda) + if udalost.type == SPAWN_NEPRITELE and stav == STAV_HRA: + # Vygenerujeme nepřítele na náhodné X souřadnici, hned nad okrajem obrazovky (-40) + n_x = random.randint(0, SIRKA - 40) + nepratele.append(pygame.Rect(n_x, -40, 40, 40)) + + # Postupné zrychlování hry (těžší obtížnost s ubíhajícím časem) + rychlost_nepritele += 0.05 + if spawn_cas > 300: # Ochrana: Nechceme aby se spawnovali nesmyslně rychle (limit) + spawn_cas -= 20 # Zkrátíme čas mezi nepřáteli + pygame.time.set_timer(SPAWN_NEPRITELE, spawn_cas) + + if stav == STAV_HRA: + # --- Plynulý Pohyb Hráče --- + 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 vystřelených projektilů --- + # Používáme [:] pro KOPII seznamu, abychom z originálu mohli během cyklu mazat bez chyb + for p in projektily[:]: + p.y -= rychlost_projektilu # Kulky letí nahoru, y se zmenšuje + + # Pokud kulka vyletí nahoře z obrazovky, vymažeme ji, aby nezabírala paměť RAM + if p.y < 0: projektily.remove(p) + + # --- Pohyb nepřátel a kontrola kolizí --- + hrac_rect = pygame.Rect(hrac_x, hrac_y, hrac_sirka, hrac_vyska) + + for n in nepratele[:]: + n.y += int(rychlost_nepritele) # Nepřátelé letí dolů, y roste + + # Pokud nepřítel proletí až dolů (ZEMĚ) nebo narazí přímo do HRÁČE = GAME OVER + if n.y > VYSKA or n.colliderect(hrac_rect): + stav = STAV_KONEC + + # --- Zásah nepřítele kulkou (DVOJITÝ CYKLUS - FOR VE FORU) --- + # Kontrolujeme každého nepřítele vůči VŠEM létajícím kulkám + for p in projektily[:]: + if p.colliderect(n): + # Zásah! Odstraníme kulku i nepřítele ze seznamů + # Musíme bezpečně ověřit pomocí 'in', jestli už nebyla kulka/nepřítel vymazán + if p in projektily: projektily.remove(p) + if n in nepratele: nepratele.remove(n) + skore += 10 # Přičteme body za sestřel + + # Slovo 'break' okamžitě ukončí VNTŘNÍ FOR CYKLUS. + # Tento konkrétní nepřítel už byl zničen, nemá smysl zkoumat, jestli ho trefila další kulka. + break + + # --- Vykreslování všeho na monitor --- + 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: + # Postupně vykreslíme ze seznamů všechny aktuální obdélníčky pomocí pygame.draw.rect() + pygame.draw.rect(okno, ZELENA, hrac_rect) # Hráč + for p in projektily: pygame.draw.rect(okno, ZLUCA, p) # Všechny lasery (Žlutě) + for n in nepratele: pygame.draw.rect(okno, CERVENA, n) # Všichni nepřátelé (Červeně) + + 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)) + + pygame.display.flip() + hodiny.tick(60) + +pygame.quit() +sys.exit() diff --git a/35_pygame_zaklady/extra_07_pexeso.py b/35_pygame_zaklady/extra_07_pexeso.py new file mode 100644 index 0000000..b92d4f3 --- /dev/null +++ b/35_pygame_zaklady/extra_07_pexeso.py @@ -0,0 +1,188 @@ +import pygame +import random +import sys + +pygame.init() + +# --- Konstanty okna a rozvržení mřížky --- +SIRKA_OKNA = 600 +VYSKA_OKNA = 600 +FPS = 30 + +RADKY = 4 +SLOUPCE = 4 +KARTICKA_VELIKOST = 100 +MEZERA = 20 + +# Výpočet celkové šířky a výšky mřížky, abychom ji mohli vycentrovat +Mrizka_sirka = (SLOUPCE * KARTICKA_VELIKOST) + ((SLOUPCE - 1) * MEZERA) +Mrizka_vyska = (RADKY * KARTICKA_VELIKOST) + ((RADKY - 1) * MEZERA) + +# Odskoky zleva a shora pro vykreslování +Odsazeni_x = (SIRKA_OKNA - Mrizka_sirka) // 2 +Odsazeni_y = (VYSKA_OKNA - Mrizka_vyska) // 2 + +# Barvy (R, G, B) +BILA = (255, 255, 255) +SEDA = (150, 150, 150) +TMAVE_MODRA = (20, 20, 80) +ZELENA_TEXT = (50, 255, 50) + +# Potřebujeme 8 barev pro 8 párů = 16 karet +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á +] + +okno = pygame.display.set_mode((SIRKA_OKNA, VYSKA_OKNA)) +pygame.display.set_caption("Pexeso (Hledání barevných párů)") +hodiny = pygame.time.Clock() +font = pygame.font.SysFont("arial", 40, bold=True) +font_maly = pygame.font.SysFont("arial", 20) + +# --- Třída objektu - objektově orientované programování (OOP) --- +# Použití objektu pro "Kartičku" nám velmi zjednoduší udržování informace +# o tom, jaká barva na kartičce je, a jestli je otočená nebo zakrytá. +class Karticka: + def __init__(self, radek, sloupec, barva_vnitrku): + self.barva = barva_vnitrku + + # Stavy kartičky (boolean) + self.odkryta = False # Zda ji hráč právě otočil + self.nalezena = False # Zda už hráč našel její pár (tím ji vyřadil) + + # Výpočet její pevné x,y souřadnice na obrazovce podle toho v jakém je sloupci a řádku + self.x = Odsazeni_x + sloupec * (KARTICKA_VELIKOST + MEZERA) + self.y = Odsazeni_y + radek * (KARTICKA_VELIKOST + MEZERA) + + # Uložíme si Rect pro zjišťování kolize s myší + self.rect = pygame.Rect(self.x, self.y, KARTICKA_VELIKOST, KARTICKA_VELIKOST) + + def vykresli(self): + # Pokud je karta lícem nahoru (odkrytá nebo už nalezena), kreslíme její skrytou barvu + if self.odkryta or self.nalezena: + pygame.draw.rect(okno, self.barva, self.rect) + else: + # Rub karty - šedivá barva + pygame.draw.rect(okno, SEDA, self.rect) + + # Obrys kolem kartičky pro lepší vzhled + pygame.draw.rect(okno, BILA, self.rect, 3) + +def hlavni_smycka(): + # 1. Příprava seznamu barev + # Seznam BARVY_PARU znásobíme 2, abychom od každé barvy měli dvě (pár) + barvy_do_hry = BARVY_PARU * 2 + random.shuffle(barvy_do_hry) # Náhodně zamícháme pořadí + + # 2. Vytvoření mřížky kartiček (2D pole) + # Vznikne struktura 'seznam v seznamu', ke kartě se přistoupí pomocí karticky[radek][sloupec] + karticky = [] + index_barvy = 0 + for radek in range(RADKY): + rada = [] + for sloupec in range(SLOUPCE): + karta = Karticka(radek, sloupec, barvy_do_hry[index_barvy]) + rada.append(karta) + index_barvy += 1 + karticky.append(rada) + + # Proměnné pro pamatování si, na které karty hráč zrovna kliknul + prvni_vybrana = None + druha_vybrana = None + + pocet_pokusu = 0 + nalezeno_paru = 0 + + while True: + # --- Zpracování událostí --- + for udalost in pygame.event.get(): + if udalost.type == pygame.QUIT: + pygame.quit() + sys.exit() + + # Zjišťování KLIKNUTÍ MYŠÍ + if udalost.type == pygame.MOUSEBUTTONDOWN and udalost.button == 1: # button 1 = levé tlačítko + + # Bezpečnostní pojistka - zablokujeme klikání, pokud už máme 2 karty otočené + # a čekáme např. na jejich skrytí v případě, že se neshodují. + if prvni_vybrana is not None and druha_vybrana is not None: + continue + + # Kde přesně je myš? Získáme X a Y obrazovky + mys_x, mys_y = pygame.mouse.get_pos() + + # Projdeme všechny karty a zjistíme, jestli bod myši je v obdélníku karty + for radek in range(RADKY): + for sloupec in range(SLOUPCE): + karta = karticky[radek][sloupec] + + # collidepoint() vrací True, pokud je x,y uvnitř obdélníku karty + if karta.rect.collidepoint(mys_x, mys_y): + # Otočíme kartu, jen když ještě není odkrytá nebo nalezena + if not karta.odkryta and not karta.nalezena: + karta.odkryta = True + + # Je to první vybraná karta v tomto tahu? + if prvni_vybrana is None: + prvni_vybrana = karta + else: + # Je to druhá vybraná karta! Tah je dokončen. + druha_vybrana = karta + pocet_pokusu += 1 + + # --- Vykreslování --- + okno.fill(TMAVE_MODRA) + + # Zavoláme metodu vykresli() pro všechny kartičky v tabulce + for radek in range(RADKY): + for sloupec in range(SLOUPCE): + karticky[radek][sloupec].vykresli() + + # UI s počtem tahů + text_tahy = font_maly.render(f"Počet pokusů: {pocet_pokusu}", True, BILA) + okno.blit(text_tahy, (10, 10)) + + # Zpráva o vítězství + if nalezeno_paru == 8: # Maximum možných párů je polovina počtu karet + text_konec = font.render("Vítězství! Skvělá paměť.", True, ZELENA_TEXT) + rect = text_konec.get_rect(center=(SIRKA_OKNA // 2, VYSKA_OKNA - 30)) + okno.blit(text_konec, rect) + + # Provedeme "vyfocení" na obrazovku ABY BYLA DRUHÁ KARTA VIDĚT PŘED PAUZOU! + pygame.display.flip() + + # --- Logika Pexesa (Vyhodnocení po odkrytí druhé karty) --- + if prvni_vybrana is not None and druha_vybrana is not None: + if prvni_vybrana.barva == druha_vybrana.barva: + # Našli jsme PÁR! + prvni_vybrana.nalezena = True + druha_vybrana.nalezena = True + nalezeno_paru += 1 + else: + # NENAŠLI. Karty se k sobě nehodí. + # Musíme na 1 vteřinu zastavit program, aby si hráč stihl zapamatovat barvy. + # Použijeme pygame.time.wait(), které "zmrazí" program (hráč nemůže klikat atd.). + # Pro takhle jednoduché výukové pexeso je to naprosto v pořádku. + pygame.time.wait(1000) + + # Zase je otočíme rubem vzhůru + prvni_vybrana.odkryta = False + druha_vybrana.odkryta = False + + # Vynulujeme výběr pro další tah, ať hráč může volit další karty + prvni_vybrana = None + druha_vybrana = None + + # Rychlost smyčky + hodiny.tick(FPS) + +if __name__ == "__main__": + # Tady pexeso neběží donekonečna, po vítězství program čeká, dokud jej nezavřeme + hlavni_smycka()