Spuštění picogame na reálném hardwaru (PicoPad / RP2040)
Simulátor (sim/) má neomezenou RAM, shovívavé čistě Pythonové API a je
nejrychlejší způsob, jak iterovat — ale skrývá několik věcí, které vás na zařízení
nepříjemně překvapí. Tento dokument je checklist všeho, co potřebujete, aby hra běžela
na skutečném PicoPadu, naučeno tou těžší cestou. Přečtěte si ho, než budete flashovat.
TL;DR nástrah: žádný celoobrazovkový
Canvas; HUD =Label, ne pruh z Canvasu; display backend si postavte sami; nasazujte.mpy, ne velké.py; velká RGB565 data ukládejte jako PAL8bytes, ne jako literálarray;gc.collect()mezi scénami; a firmware musí danou funkci, kterou voláte, opravdu obsahovat.
Limity hodin / SPI / rychlosti displeje (jak jádrový clock řídí display SPI, strop ST7789, overclocking RP2350, jak to testovat) najdete v HARDWARE_LIMITS.md.
1. Rozpočet RAM (to je to skutečné omezení)
Sekce “1. Rozpočet RAM (to je to skutečné omezení)”RP2040 má 264 KB SRAM. Firmware spotřebuje ~72 KB staticky (~27 %), takže zbývá zhruba ~150–190 KB Python heapu pro vaši hru. To se zaplní rychle:
| Položka | Cena |
|---|---|
strip buffery z picogame_game.setup() (2 × 320×strip_h×2) | strip_h=24 → 30 KB, strip_h=12 → 15 KB |
celoobrazovkový Canvas(320, 240) | 150 KB ⚠️ v podstatě celý heap |
Canvas(320, 130) (např. pseudo-3D silnice) | 83 KB — samostatně OK (viz microrace), ale ne navíc k mnoha dalšímu |
stavový řádek Canvas(320, 20) | 12,8 KB |
| Bitmap dlaždice/sprite | šířka×výška×snímky × (1 B PAL8 / 2 B RGB565) |
Důsledky:
- Nikdy nealokujte celoobrazovkový Canvas. Pokud potřebujete vlastní raster (silnice, pole
tvarů), držte ho tak malý, jak je obsah (rozdělte ho na pruhy), nebo použijte
Tilemappro velké rolovací plochy (1 bajt/buňka místo 2 bajtů/pixel — 320×960 šumové nebe je 600 KB jako Canvas, ale ~5 KB jako odstínový Tilemap). - HUD =
picogame_font.Label/picogame_ui.SceneLabel(text), ne celoširoký pruh z Canvasu. HW ověřené hry to dělají takto. - Snižte
strip_hna 12 v napjatých scénách pro ~15 KB rezervy (o něco více refresh průchodů, stále v pohodě). gc.collect()mezi scénami/úrovněmi, aby se buffery předchozí scény uvolnily, než další alokuje.- Pokud je hra příliš velká jako jeden program, rozdělte ji (viz §4).
MemoryError: memory allocation failed, allocating N bytes = překročili jste rozpočet;
poznamenejte si, která scéna/řádek, a zmenšete tam ten největší buffer (obvykle Canvas).
Fragmentace, ne jen celkový volný prostor. Dlouhá session, která alokuje a uvolňuje velké
buffery, fragmentuje heap: gc.mem_free() může ukazovat ~90 KB, zatímco alokace 51 KB stále
selže (žádný souvislý úsek). Pokud monolit umírá na velkém Canvasu, i když „je tam spousta volného
místa”, je to právě tohle. Řešením je předalokovaná arena (lib/picogame_arena.py
- firmwarový argument
Canvas(..., buffer=)) — naberte jeden velký buffer předem a krájejte ho. Obecný popis (se sondou na největší souvislý blok a síťovým příkladem) je v MEMORY.md.
2. Rozdíly v API mezi zařízením a simulátorem
Sekce “2. Rozdíly v API mezi zařízením a simulátorem”Simulátor je tolerantní; C firmware je přísnější. Tohle všechno „funguje v simu, spadne na zařízení”:
| Sim přijme | Zařízení potřebuje | Projev na zařízení |
|---|---|---|
scene0.display (sim Scene ho má) | postavte backend: DISP = pg.Display(board.DISPLAY) | AttributeError: 'Scene' object has no attribute 'display' |
C Scene nemá .display. Scene.add má příznak fixed jen jako keyword
(scene.add(item, fixed=True)) v simu i na zařízení. Konstruktory
noise/Canvas/Particles keyword argumenty BEROU (mp_arg_parse_all).
Jak si postavit display backend sami (co picogame_game.setup dělá interně):
import board, picogame as pgd = board.DISPLAYd.auto_refresh = Falsetry: d.root_group = Noneexcept Exception: passDISP = pg.Display(d) # backend, který Scene(...) chce jako první argumentbufA = bytearray(320 * 12 * 2); bufB = bytearray(320 * 12 * 2)scene = pg.Scene(DISP, bufA, bufB, background=background)3. Pasti při importu / kompilaci
Sekce “3. Pasti při importu / kompilaci”- Velký
.pyjakocode.py→ MemoryError při importu. CircuitPython kompiluje zdrojcode.pypři bootu; parse strom velkého souboru je velký krátkodobý nárůst RAM. Nasazujte vše jako.mpy(zkompilované přesmpy-crossodpovídající firmwaru — aktuálně mpy v6.3 / CircuitPython 10.3.0-alpha) a použijte malinký launchercode.py(import my_scene)..mpyje také menší a šetrnější k RAM. - Obří list literály →
RuntimeError: pystack exhausted. Literálarray.array('H', [7168, ...])tlačí tisíce prvků na VM stack (a vytvoří ~28 KB krátkodobý list). Pro velké RGB565 tilesety je bakejte jako PAL8 sDATA = b'...'(jedinábyteskonstanta: poloviční velikost, žádný list a bajtová data jsou zarovnáním bezpečná na Cortex-M0+). Rezervujte index palety 0 = průhledná.
Kompilace modulu:
circuitpython/mpy-cross/build/mpy-cross mymodule.py -o mymodule.mpy4. Vzor nasazení: jeden program na scénu
Sekce “4. Vzor nasazení: jeden program na scénu”Demo typu „ukaž všechno”, které drží všechna assets + veškerý kód scén v jednom modulu, se nevejde. Vzor, který funguje:
dj_common.mpy— sdílené lešení + pomocníci (nastavení displeje,new_scene,status_barjako Label,play()bez Canvasu kryjícího přechod, bitmap pomocníci).import *z něj.scene_<name>.mpy— jeden program na scénu; importuje jen assets, které potřebuje, takže v RAM žije vždy jen jedna scéna. Spouští vlastníwhile True: seg(); gc.collect().code.py— jednořádkový launcher (import scene_intro); upravte/přejmenujte pro přepínání scén, nebo postavte malé tlačítkové menu.
Sim/video build může zůstat jediným monolitem (má na to RAM); HW rozdělení držte
oddělené. Konkrétní příklad: examples/journey_hw/ (dj_common.py + scene_*.py,
plus journey_mono.py — varianta v jednom souboru se StripDraw, nulový pixel buffer / bez areny) vs. sim/video
monolit examples/picogame_demo_journey.py (se zvukem + přechodem). Viz
examples/journey_hw/README.md.
Rozložení na zařízení:
CIRCUITPY/ code.py # import scene_<name> scene_*.mpy # jedna na scénu dj_common.mpy # sdílení pomocníci <assets>.mpy # dj_hero, dj_town, ... (jen to, co scény importují) lib/picogame_*.mpy # Python pomocníci enginu(Žádný zvuk na zařízení, pokud nezapojíte picogame_audio; chiptune dema je
pouze offline, zapečený do nahraného videa.)
5. Firmware musí obsahovat funkci, kterou voláte
Sekce “5. Firmware musí obsahovat funkci, kterou voláte”Projevy jako AttributeError: ... has no attribute 'X' nebo can't set attribute 'X'
obvykle znamenají, že naflashovaný firmware je starší než kód, který X používá. Např. starý
build měl Sprite.scale jen pro čtení → can't set attribute 'scale'.
- Build: viz
PICOGAME.md§„Building the firmware” a[[picopad-build-env]](toolchain ARM GCC ≥ 14 + venv;make BOARD=pajenicko_picopad -j$(nproc)). Výstup:circuitpython/ports/raspberrypi/build-pajenicko_picopad/firmware.uf2. - Ověření, že je symbol přítomen, bez flashování:
arm-none-eabi-nm build-.../firmware.elf | grep sprite_set_scale. - Flash: vstupte do bootloaderu RP2040 (disk RPI-RP2), zkopírujte
firmware.uf2. Souborový systém CIRCUITPY (vaše.mpysoubory) zůstane zachován i přes flash firmwaru.
Aktuální firmware obsahuje: settery Sprite.scale/angle/shadow, kompletní sadu primitiv Canvas
(triangle/ellipse/ring/fill_round_rect/frame3d) a C noise
(value2d/value1d/fbm2d/fbm1d + varianty _fx v pevné řádové čárce).
6. Poznámky k výkonu
Sekce “6. Poznámky k výkonu”- Noise je v C a v pevné řádové čárce. Pomalou částí byl čistě Pythonový noise (~1–2 s zádrhel
při generování nebe); C
fbm2dto dělá zanedbatelným. Po benchmarku byla varianta v pevné řádové čárce ~1,8× rychlejší než float na zařízení (float1,186 svs. fixed0,649 spro 5000fbm2d), proto byla float verze vyřazena (uvolnila ~1,8 KB flash) —value2d/value1d/fbm2d/fbm1djsou teď implementace v pevné řádové čárce (souřadnice Q16.16, hodnoty Q0.16). Staré názvy*_fxzmizely (není s čím kontrastovat). Float referenci ponecháváme vypnutou za#if 0ve zdroji C pro případ, že by byla někdy znovu potřeba. - Kreslení Canvasu je v C (
fill_rect,frame3d, …) — Python jen vydává volání. Co stojí, je buffer Canvasu (RAM), ne kreslení. StripDraw= immediate mode, nulový buffer. Pro celosnímkové animované plochy (pseudo-3D silnice, gradientní nebe, procedurální pozadí) použijtepg.StripDraw(callback, …)místo Canvasu: kreslí přímo do každého render stripu, takže stojí 0 bajtů RAM plochy (vs. 150 KB pro celoobrazovkový Canvas). Překresluje se každý snímek, takže je pro animovaný obsah, ne pro statickou grafiku. Přidáno do firmwaru za +216 B flash (znovu využívá primitiva Canvasu jako pohled na jednotlivé stripy). VizPICOGAME.md→StripDrawaexamples/picogame_stripdraw_demo.py. To je skutečné řešení problému fragmentace velkých bufferů (MEMORY.md) — buffer prostě neexistuje.- Dirty-rect znamená, že převážně statická scéna je levná; celoobrazovkové rolování překresluje všechno každý snímek. ~50 pohybujících se spritů ≈ 25 FPS na RP2040.
Viz také: PICOGAME.md (API), ENGINE_ERGONOMICS.md (zdůvodnění návrhu),
SCENE_FORMAT.md (deklarativní úrovně), tutorials/ (krok za krokem), examples/
(žánrové porty — microrace dokazuje, že 83 KB Canvas je na zařízení v pořádku).