Úryvky kódu — vzory k úpravě
Tohle jsou útržky hotové k vložení — kód pro konkrétní úlohu. Proč a vyšší strukturu za nimi najdeš v Herních vzorech. Kostra níže je celá hra v malém; ukázky za ní doplňují jednotlivé části. Vyber potřebný kus, uprav názvy a přizpůsob limity své hře. Kód používá stejné zvyklosti jako přibalené příklady.
Celý tvar: hotová kostra ke spuštění
Sekce “Celý tvar: hotová kostra ke spuštění”Začni tady. Všechen měnitelný stav hry žije v jednom State objektu (st) a smyčka
každého snímku žije ve funkci main() — uvnitř funkce se její jména vyhledávají jako
rychlé lokály místo vyhledávání v globálním slovníku, což je na zařízení měřitelný zisk.
Každá ukázka níže zapadá do tohoto tvaru. Zkopíruj ji, přejmenuj pole State a doplň větev play.
# Ready-to-use picogame skeleton: a TITLE -> PLAY -> GAME-OVER state machine, with ALL game state# in one State object and the per-frame loop in a function (main). Copy it, rename the State fields,# and fill in the PLAY branch. Runs as-is here in the browser and on a device.import boardimport terminalioimport picogame as pgimport picogame_gameimport picogame_inputimport picogame_clockimport picogame_shapes as shapesimport picogame_ui as ui
W, H = board.DISPLAY.width, board.DISPLAY.height # size-independent: never hardcode 320x240BAR = 16 # reserved top HUD strip
scene, bufA, _ = picogame_game.setup(background=pg.rgb565(16, 20, 34), top=BAR)btn = picogame_input.Buttons()clock = picogame_clock.Clock(30)
hud = ui.HudBar(pg, board.DISPLAY, bufA, 0, 0, W, BAR, pg.rgb565(10, 12, 22))label = hud.label(terminalio.FONT, 4, 3, pg.rgb565(235, 240, 255), "")
# The player: a generated shape now; swap in a Bitmap (png2picogame) later.player = pg.Sprite(shapes.circle(14, pg.rgb565(240, 90, 90)), W // 2, H // 2)player.anchor = (0.5, 0.5)scene.add(player)
class State: """ALL mutable game state in ONE object, created once and never reassigned. reset() re-inits it in place, so main() can hold `st` and every helper sees the same state. Add your fields here; keep engine objects (sprites, pools, clock) as module globals, not in State.""" def __init__(self): self.reset()
def reset(self): self.mode = "title" # "title" -> "play" -> "over" self.score = 0
st = State()
def show(text): # set the HUD text and repaint the bar label.set(text) hud.draw()
def new_game(): st.reset() st.mode = "play" player.move(W // 2, H // 2) show("SCORE 0")
def main(): # The per-frame loop lives in a FUNCTION, not at module scope: here its names resolve as fast # array-indexed locals instead of globals-dict lookups -- a measured win on device. show("PRESS A TO START") while True: btn.poll() if st.mode == "play": # --- your game goes here: move, collide, score --- player.x += (btn.is_pressed(btn.RIGHT) - btn.is_pressed(btn.LEFT)) * 3 if btn.just_pressed(btn.A): st.score += 1 show("SCORE %d" % st.score) # set text where it CHANGES, not every frame if btn.just_pressed(btn.B): st.mode = "over" show("GAME OVER - press A") elif btn.just_pressed(btn.A): # title or game-over -> (re)start, no reload new_game() scene.refresh() clock.tick()
main()Stránka Herní vzory vysvětluje ten tvar; ukázky níže jsou jednotlivé tahy.
Rozměry displeje načti místo pevného zadání
Sekce “Rozměry displeje načti místo pevného zadání”Pokud se má rozvržení přizpůsobit různým podporovaným displejům, například 320×240 a
240×240, odvozuj pozice z W a H.
W, H = board.DISPLAY.width, board.DISPLAY.heightplayer = pg.Sprite(bm, W // 2, H - 40)Text HUDu formátuj jen při změně
Sekce “Text HUDu formátuj jen při změně”label.set() přeskočí překreslení nezměněného textu, ale samotný "%"-formát alokuje nový
řetězec při každém volání. Ve snímkové smyčce by tak zbytečně vznikal odpad pro garbage
collector. Nejdřív porovnej čísla a text formátuj jen při změně zobrazené hodnoty:
hud = picogame_font.Label(...) # nebo picogame_ui.HudBar; vytvořeno JEDNOU při načtení
if score != shown_score: # poslední hodnota zobrazená v popisku shown_score = score hud.set("SCORE %d" % score)Všechny možnosti HUDu a popisků: Text a UI.
Pokud to jde, nastav popisek přímo v místě, kde se hodnota mění, například při zásahu nebo přechodu do další úrovně. Plynulé hodnoty nejdřív převeď na zobrazovanou jednotku:
secs = int(time.monotonic() - t0) # celé sekundy = co label zobrazujeif secs != shown_secs: shown_secs = secs timer_lbl.set("TIME %d" % secs)Střely a další krátkodobé objekty ber z fondu
Sekce “Střely a další krátkodobé objekty ber z fondu”U často vznikajících objektů, jako jsou střely, nevytvářej při každém použití nový
pg.Sprite(...). Pool je připraví předem; spawn() a free() pak přepínají viditelnost:
bullets = Pool(scene, bullet_bm, 8, anchor=(0.5, 0.5)) # JEDNA alokace, při načtení
b = bullets.spawn() # None, když je všech osm střel aktivníchif b: b.move(ship.x + 14, ship.y)
for b in bullets.items: # pevný seznam -> smyčka nic nealokuje if not b.visible: continue b.x += 6 if b.x > W + 8: bullets.free(b) # zpátky do poolu k znovupoužitíHUD ikony s pevným počtem (životy, munice) Pool nepotřebují — obyčejný seznam spritů
s přepínáním visible stačí.
Restart jedním reset() a jedním nikdy nepřepisovaným st
Sekce “Restart jedním reset() a jedním nikdy nepřepisovaným st”Hodnoty jednoho herního pokusu drž v třídě State a při restartu je re-inicializuj na místě
metodou reset(). st vznikne jednou a nikdy se znovu nepřiřadí — nikdy nepřepisovaný singleton —
takže main() (i libovolný pomocník) ho může držet jako rychlý lokál bez rizika, že po restartu
ukazuje na zastaralou kopii. Objekty enginu a hodnoty společné pro všechny pokusy (nejlepší skóre)
zůstávají na úrovni modulu.
class State: def __init__(self): self.reset() # __init__ jen zavolá reset -> jedno místo pro defaulty def reset(self): self.score = 0 self.lives = 3 self.px = 60.0
st = State() # vytvořeno JEDNOU, při načtení
def new_game(): st.reset() # každé pole zpět na dokumentovaný default, NA MÍSTĚ bullets.free_all() player.move(60, GROUND)Pauza nebo menu nad živou scénou
Sekce “Pauza nebo menu nad živou scénou”pg.render vykresluje mimo stav uložený scénou. Bez invalidate() proto může další
refresh() nechat na obrazovce část překryvu. overlay() vykreslí překryv a zároveň označí
scénu k obnovení. Překryv vykresli jednou a během pauzy scénu neobnovuj:
if btn.just_pressed(btn.START): paused = not paused if paused: picogame_game.overlay(scene, board.DISPLAY, [pause_sd], bufA, 0, 0, W, H)if paused: clock.tick() # žádné update, žádný refresh -> overlay drží continuescene.refresh() # první refresh po odpauzování překreslí všeMíchání: na zařízení není random.shuffle
Sekce “Míchání: na zařízení není random.shuffle”random v CircuitPythonu nemá shuffle()/sample() (desktopový simulátor je má — běží na
CPythonu). Fisher-Yates přes randint je tentýž algoritmus:
for i in range(len(deck) - 1, 0, -1): j = random.randint(0, i) deck[i], deck[j] = deck[j], deck[i]Záblesk po zásahu na 1–3 snímky
Sekce “Záblesk po zásahu na 1–3 snímky”Krátký záblesk zvýrazní okamžik zásahu. Nastav flash při kolizi a ve smyčce odpočítej
zbývající snímky:
enemy.flash = WHITE # při zásahust.flash_t = 3
if st.flash_t > 0: # v herní smyčce st.flash_t -= 1 if st.flash_t == 0: enemy.flash = None(Na zařízení sdílí blit efekty flash/tint/dither/shadow jeden slot — vyhrává poslední
nastavený; simulátor je považuje za nezávislé, takže tohle nereprodukuje. Trvalý dither po konci
flashe nastav znovu. Širší sada nástrojů: Efekty.)
Pípnutí při události
Sekce “Pípnutí při události”K záblesku přidej zvuk. Tón sestav jednou při načtení a pak ho spusť při události. Vše schovej
za if audio:, aby deska bez zvuku (nebo selhaná inicializace) prostě běžela potichu:
audio = Nonetry: import picogame_audio audio = picogame_audio.Audio() # PWM výstup + mixer; na deskách bez zvuku může selhat boom = picogame_audio.tone(140, 200) # nízký úder, sestaven JEDNOU (RawSample v RAM)except Exception: # málo paměti, žádný audio pin, ... audio = None # -> běž potichu
if audio: # při zásahu / sebrání: audio.sfx(boom) # bez čekání, na volném hlasuPípnutí z tone() jako tohle ve webovém playgroundu hraje (WebAudio). Kde který zvukový kanál
zazní (playground / simulátor / zařízení) a hotovou sadu pojmenovaných efektů (hit(), coin(),
boom()) najdeš v Audiu.
Tlačítka: jeden virtuální ovladač, mapovaný v settings.toml
Sekce “Tlačítka: jeden virtuální ovladač, mapovaný v settings.toml”Programuj proti logickým tlačítkům enginu (UP DOWN LEFT RIGHT A B, volitelně X Y L1 L2 R1 R2 START SELECT) — nikdy proti pinům. Stejné code.py pak běží na jakékoli desce; mění se jen mapování
v settings.toml (bez reflashe):
btn = picogame_input.Buttons() # čte GPIO + klávesovou matici + USB, vše OR
btn.poll() # jednou za snímek, před dotazyif btn.is_pressed(btn.LEFT): px -= 2if btn.just_pressed(btn.A): jump()if btn.has(btn.X): show_x_hint() # jen když deska X namapovala# přímá GPIO tlačítka (Pico, které sis sám zapojil)PICOGAME_BUTTONS = "UP=GP2 DOWN=GP3 LEFT=GP4 RIGHT=GP5 A=GP6 B=GP7"
# …nebo skenovaná klávesová matice — NÁZEV=řádek,sloupecPICOGAME_MATRIX_ROWS = "GP0 GP1 GP2 GP3"PICOGAME_MATRIX_COLS = "GP4 GP5 GP6 GP7"PICOGAME_MATRIX_MAP = "UP=0,1 DOWN=2,1 LEFT=1,0 RIGHT=1,2 A=3,3 B=3,2"USB gamepad/klávesnice (desky s USB hostem) se připojí samy. Kompletní průvodce + USB remap klíče: Vstup a ovládání.
Tolerantnější skoky: čas po opuštění hrany a předčasný stisk
Sekce “Tolerantnější skoky: čas po opuštění hrany a předčasný stisk”Obě časová okna může hlídat picogame_input.Timer. Hráč pak může skočit krátce po opuštění
hrany nebo stisknout tlačítko těsně před dopadem:
coyote = picogame_input.Timer(6) # skok až 6 snímků po opuštění hranyjbuf = picogame_input.Timer(5) # přijmi stisk až 5 snímků před dopadem
coyote.feed(on_ground) # v herní smyčcejbuf.feed(btn.just_pressed(btn.A))if jbuf.consume() and coyote.is_active: jump()Panel, který se překreslí jen při změně
Sekce “Panel, který se překreslí jen při změně”StripDraw s always_dirty=False neuchovává pixelovou plochu panelu. Zůstane nečinný,
dokud nezavoláš invalidate():
panel = pg.StripDraw(draw_panel, x, y, w, h, always_dirty=False)scene.add(panel, fixed=True)
lines[1] = "HP %d" % hp # když se obsah změní:panel.invalidate() # překreslí se jednou při dalším refreshiPohyb s desetinnou přesností bez vlastní pozice
Sekce “Pohyb s desetinnou přesností bez vlastní pozice”Souřadnice x a y spritu jsou celá čísla. Pro plynulý pomalý pohyb použij vlastnosti
fx a fy s pevnou řádovou čárkou (24.8), místo abys vedl vlastní desetinnou pozici:
ball.fx = ball.fx + 0.4 # zlomkový posun bez poskakování mezi pixelyball.fy = ball.fy + vy * dt