7 changed files with 1292 additions and 1236 deletions
@ -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()) |
|||
@ -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) |
|||
pygame.draw.rect(okno, ZELENA, (t["x"], 0, trubka_sirka, horni_vyska)) |
|||
|
|||
# Vypočteme, kde začíná 'spodní' překážka (od středu + polovina mezery) |
|||
# 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()) |
|||
@ -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()) |
|||
@ -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()) |
|||
@ -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()) |
|||
@ -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ě! |
|||
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[:]: |
|||
# .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() |
|||
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()) |
|||
@ -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()) |
|||
Loading…
Reference in new issue