Browse Source

Přidán návod pro studenty a odstraněny staré patchovací skripty s absolutními cestami

master
skrabanek 2 days ago
parent
commit
786549e9c2
  1. 23
      README.md
  2. 104
      README_PRO_STUDENTY.md
  3. 182
      pygame/07_had.py
  4. 11
      pygame/fix_syntax.py
  5. 25
      pygame/patch.py
  6. 29
      pygame/patch_safe.py

23
README.md

@ -59,4 +59,25 @@ Pokud nevíte jak začít, zkuste se držet těchto bodů:
4. **Rozdělte si práci:** Pokud na začátku narazíte na příliš složitý problém, rozdělte si ho na menší části. Nezkoušejte naprogramovat "bojový systém" jako celek. Nejdřív naprogramujte "stisknutí mezerníku ubere nepříteli 1 HP".
5. **Dělejte si zálohy:** Často ukládejte a pokud něco začne fungovat, udělejte si kopii souboru (nebo použijte Git!), než do toho začnete vrtat dál.
Hodně štěstí při vývoji! Nezapomeňte, že primárním cílem Game Jamu je se něco nového naučit a pobavit se u toho.
Hodně štěstí při vývoji! Nezapomeňte, že primárním cílem Game Jamu je se něco nového naučit a pobavit se u toho.
---
## 🌐 Optimalizace pro Školní Webový Portál (GameGlass)
Aby vaše hry fungovaly nejen u vás na počítači (Desktopu), ale šly bez problémů nahrát a hrát přímo ve webovém prohlížeči v našem školním systému, musíte dodržet několik důležitých technických pravidel:
### 1. Pygame hry (Nutnost použít asynchronní kód)
Webový prohlížeč neumožňuje tzv. "nekonečné blokovací smyčky" (zamrzla by vám celá záložka v prohlížeči). Vaše hlavní herní smyčka proto **musí** být napsána asynchronně pomocí modulu `asyncio`:
* **Pravidlo 1:** Hlavní smyčka hry musí být definována jako `async def hlavni_smycka():`.
* **Pravidlo 2:** Uvnitř `while True:` smyčky musíte na konec každého kola (typicky za `pygame.display.flip()`) přidat `await asyncio.sleep(0)`. Tento příkaz "na milisekundu" uvolní prohlížeč, aby stihl vykreslit obraz.
* **Pravidlo 3:** 🚫 **ZÁKAZ** používání `pygame.time.wait()` nebo `pygame.time.delay()`. Tyto funkce na webu totálně zamrazí celou obrazovku. Pokud potřebujete hru na vteřinu uspat (např. při chybě v Pexesu), použijte `await asyncio.sleep(1)`.
* **Pravidlo 4:** Spuštění hry na samotném konci souboru musí vypadat takto: `asyncio.run(hlavni_smycka())`
*(Prohlédněte si naše ukázky v adresáři `pygame/`, všechny jsou už pro web plně optimalizované!)*
### 2. Balení her pro odevzdání (ZIP)
Na portál se hry nahrávají ve formátu `.zip`. Je ale velmi důležité, co do ZIPu zabalíte:
* **Pygame a Čistý Python:** Hlavní spouštěcí soubor vaší hry by měl být umístěn přímo v kořenovém adresáři ZIPu (nezašívejte ho hluboko do složek). Náš portál se ho pokusí najít a pokud se jmenuje `main.py`, je to ideální. Spolu se zdrojovým kódem nezapomeňte do ZIPu přibalit i složku se zvuky a grafikou (např. `/assets`).
* **Ren'Py (Vizuální novely):** Nemusíte z Ren'Py enginu dělat žádný složitý "Web Export"! Stačí, když vezmete celou složku s vaším projektem (tu, která obsahuje podsložku `game/` se soubory `script.rpy` atd.) a rovnou ji zazipujete. Náš webový portál si tento ZIP automaticky rozbalí a nasadí do něj svůj vlastní webový RenPy motor. Je to naprosto bez starostí!

104
README_PRO_STUDENTY.md

@ -0,0 +1,104 @@
# 🎮 Návod pro studenty: Jak připravit hru pro GameGlass Portál
Ahoj! Abys mohl/a svou hru nahrát na náš školní GameGlass portál a ukázat ji všem ostatním přímo ve webovém prohlížeči, stačí dodržet několik jednoduchých pravidel.
---
## 1. Pravidla pro Pygame (Grafické hry)
Aby tvoje Pygame hra mohla běžet uvnitř internetového prohlížeče, musí být strukturována tzv. "asynchronně" (aby nezamrzla prohlížeč).
**Musíš udělat přesně tyto 3 kroky ve svém kódu:**
1. **Importuj `asyncio`:**
Na úplný začátek souboru, hned k `import pygame`, přidej:
```python
import pygame
import asyncio
```
2. **Zabal hru do asynchronní funkce:**
Celý kód tvé hry (nebo alespoň tvoji hlavní herní smyčku) musíš zabalit do asynchronní funkce (např. `async def main():`).
A na úplný konec souboru pak hru spustit pomocí `asyncio.run(main())`.
3. **Přidej odpočinkovou pauzu do smyčky:**
Do tvé hlavní smyčky `while True:` musíš (ideálně nakonec smyčky) přidat magické zaříkávadlo:
```python
await asyncio.sleep(0)
```
*Tento příkaz řekne prohlížeči: "Teď si můžeš na milisekundu odpočinout a vykreslit obrazovku". Bez tohoto příkazu hra okamžitě zamrzne a spadne!*
**Ukázková kostra správné Pygame hry:**
```python
import pygame
import asyncio
# --- Zde dej své funkce a nastavení ---
async def main():
pygame.init()
screen = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Zde se děje logika hry a vykreslování...
screen.fill((0, 0, 0))
pygame.display.flip()
# Omezení FPS
clock.tick(60)
# MAGICKÝ PŘÍKAZ (NUTNÉ PRO WEB!)
await asyncio.sleep(0)
pygame.quit()
# Spuštění asynchronní hry
if __name__ == "__main__":
asyncio.run(main())
```
---
## 2. Pravidla pro terminálové hry (Čistý Python textovky)
Pokud děláš obyčejnou textovou hru (pouze příkazy `print` a `input`), je to velmi jednoduché:
- Nemusíš používat `asyncio` vůbec.
- Můžeš normálně používat `input("Zadej volbu: ")`. Portál sám vytvoří interaktivní textové pole.
- Pokud chceš "vyčistit obrazovku" terminálu, můžeš použít klasický příkaz:
```python
import os
os.system('cls') # nebo os.system('clear')
```
---
## 3. Pravidla pro Ren'Py (Vizuální Novely)
- Tvoje hra bude spuštěna přes Web Assembly engine Ren'Py.
- **Krok navíc:** Před zabalením do ZIPu jdi do složky své hry a zazipuj POUZE obsah složky `game/` (tedy soubory `.rpy` a složku s obrázky `images/`). Portál nepotřebuje zbytek enginu (soubory `.exe`, `.sh`), engine už v portálu je.
---
## ⚠️ DŮLEŽITÉ UPOZORNĚNÍ K CESTÁM V KÓDU (Pro všechny hry)
**NIKDY** ve svém kódu nepoužívejte tzv. "natvrdo zadrátované cesty" k obrázkům nebo k vašemu disku! (Tzv. Absolutní cesty).
❌ **ŠPATNĚ (Na webu to spadne):**
```python
os.chdir(r"C:\Users\Student\Plocha\MojeHra") # NIKDY!
image = pygame.image.load(r"C:\Users\Student\Plocha\MojeHra\assets\hrdina.png")
```
✅ **SPRÁVNĚ (Relativní cesty):**
Aplikace běží v paměti na internetu (kde disk C:\ vůbec neexistuje). Používejte relativní cesty:
```python
image = pygame.image.load("assets/hrdina.png")
```
Až budeš mít hotovo, všechny své soubory a složky označ a zabal přímo do archivu **.ZIP** a nahraj do formuláře na portálu!

182
pygame/07_had.py

@ -1,182 +0,0 @@
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
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.
async 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)
# PRIDANO PRO WEB
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:
await hlavni_smycka()
# PŘIDÁNO PRO WEB: Spuštění asynchronní hry
asyncio.run(main())

11
pygame/fix_syntax.py

@ -1,11 +0,0 @@
import os
import glob
os.chdir(r'C:\gitprojekty_skola\python_gamejam_examples\pygame')
for f in glob.glob('*.py'):
if f.startswith('patch'): continue
with open(f, 'r', encoding='utf-8') as file:
content = file.read()
content = content.replace('async def hlavni_smycka():', 'async def hlavni_smycka():')
with open(f, 'w', encoding='utf-8') as file:
file.write(content)

25
pygame/patch.py

@ -1,25 +0,0 @@
import os
import glob
import re
os.chdir(r'C:\gitprojekty_skola\python_gamejam_examples\pygame')
for f in glob.glob('*.py'):
with open(f, 'r', encoding='utf-8') as file:
content = file.read()
# 1. async def
content = re.sub(r'(\s*)def hlavni_smycka\(\):', r'\1async def hlavni_smycka():', content)
# 2. Add await to hlavni_smycka() calls
content = re.sub(r'(\s+)hlavni_smycka\(\)', r'\1await hlavni_smycka()', content)
# 3. Fix asyncio.sleep(0)
# Odstranime stary await asyncio.sleep(0) a jeho komentar
content = re.sub(r'[ \t]*# PØIDÁNO PRO WEB: Dáme prohlížeèi šanci pøekreslit obrazovku\s+await asyncio\.sleep\(0\)\s*', '\n', content)
# Pridame ho tesne za hodiny.tick(...) se stejnym odsazenim
content = re.sub(r'([ \t]*)(.*hodiny\.tick.*)', r'\1\2\n\1# PØIDÁNO PRO WEB: Dáme prohlížeèi šanci pøekreslit obrazovku\n\1await asyncio.sleep(0)', content)
with open(f, 'w', encoding='utf-8') as file:
file.write(content)
print(f'Opraveno: {f}')

29
pygame/patch_safe.py

@ -1,29 +0,0 @@
import os
import glob
import re
os.chdir(r'C:\gitprojekty_skola\python_gamejam_examples\pygame')
for f in glob.glob('*.py'):
if f == 'patch.py':
continue
with open(f, 'r', encoding='utf-8') as file:
content = file.read()
# 1. async def
content = re.sub(r'(\s*)def hlavni_smycka\(\):', r'\1async async def await hlavni_smycka():', content)
# 2. Add await to await hlavni_smycka() calls
content = re.sub(r'(\s+)hlavni_smycka\(\)', r'\1await await hlavni_smycka()', content)
# 3. Fix asyncio.sleep(0)
# Match the old comment containing WEB without needing special chars
content = re.sub(r'[ \t]*#.*WEB.*\s+await asyncio\.sleep\(0\)\s*', '\n', content)
# Pridame ho tesne za hodiny.tick(...) se stejnym odsazenim
# PRIDANO PRO WEB
await asyncio.sleep(0)
content = re.sub(r'([ \t]*)(.*hodiny\.tick.*)', r'\1\2\n\1# PRIDANO PRO WEB\n\1await asyncio.sleep(0)', content)
with open(f, 'w', encoding='utf-8') as file:
file.write(content)
print(f'Opraveno: {f}')
Loading…
Cancel
Save