Přeskočit na obsah

Sestavování scén

Tyto čtyři moduly pokrývají statickou “světovou” část hry: načítání připravené scény, dotazování na vlastnosti dlaždic, tvorbu grafiky bez PNG assetů a recyklaci fixní sady spritů. Čistě signatury najdeš v /cs/reference/; ukázky níže vysvětlují chování a záludnosti.

Jednorázový loader, který přemění připravený SCENE dict (viz /cs/scene-format/) na živý pg.Scene a pojmenované handle. Sáhni po něm, když je tvůj level sestavený editorem nebo scene_build.py, a chceš, aby stejný loader běžel na zařízení i v simulátoru. Načítání není horká cesta, takže žije v Pythonu.

load(pg, scene, display=None, strip_h=24, font=None, bank=None) sestaví a vrátí View. Nastaví display.auto_refresh = False, vyčistí root_group, alokuje dva strip buffery (bufA/bufB, každý width * strip_h * 2), zkonstruuje pg.Scene a přidá každou vrstvu (tilemap, sprite, group, particles, hudlabel). display se defaultně bere z board.DISPLAY. Předej font (např. terminalio.FONT), pokud je některá vrstva HUD label. Detaily k displeji najdeš v /cs/hardware/ a náklady na strip buffer v /cs/memory/.

load_bank(pg, bank) sestaví sdílené bitmapy/zvuky/animace JEDNOU; výsledek předej jako load(..., bank=...) pro každý level, aby se nezměněná grafika nepřestavovala znovu.

Vrácený View je tvůj handle na vše:

  • view.scene - živý pg.Scene. Volej view.scene.refresh() každý frame a view.scene.set_view(ox, oy) pro posouvání.
  • view.named[name] - dict name -> sprite / particles / HUD label pro každou vrstvu, které byl přiřazen name.
  • view.group(tag) - list spritů pro group vrstvu (vrací [], pokud tag neznáš, takže je bezpečné iterovat).
  • view.tick(dt) - posuň všechny automaticky animované sprity; volej jednou za frame s dt v sekundách.
  • view.tile_xy(px, py) - světové pixely -> buňka (tx, ty) primárního (prvního) tilemapu.
  • view.is_solid(tx, ty) - zkratka pro tile_has(tx, ty, "solid").
  • view.tile_has(tx, ty, prop) - True, pokud má dlaždice primárního tilemapu v dané buňce pojmenovanou vlastnost (z připraveného tileprops).
  • view.point(name) - (x, y) pojmenovaného bodu, nebo None.
  • view.in_zone(x, y, tag=None) - první zóna (tag, x, y, w, h) obsahující daný bod (volitelně filtrovaná podle tagu), jinak None.
  • view.play(sound_id) - přehraj připravený sfx podle id (nic nedělá, pokud audio/sample chybí).
  • view.tilemap / view.camera / view.zones / view.points / view.anims - primární tilemap objekt, camera tuple a raw kolekce.
import board, terminalio
import picogame as pg
import picogame_scene as pgs
import world1_scene
view = pgs.load(pg, world1_scene.SCENE, font=terminalio.FONT)
player = view.named["player"]
enemies = view.group("enemies")
while True:
view.tick(1 / 30) # advance auto-animations
tx, ty = view.tile_xy(player.x, player.y)
if not view.is_solid(tx, ty):
player.move(player.x + 2, player.y)
view.scene.refresh()

Záludnosti: PRVNÍ vrstva tilemapu je “primární” - tile_xy, is_solid a tile_has dotazují pouze tuto mapu. view.named obsahuje jen vrstvy, kterým byl přiřazen název; nepojmenovaná vrstva se přidá do scény, ale nejde přes ni dostat. Zvuky se načítají best-effort: na simulátoru (bez wavs) jsou položky view.sounds None a play() tiše nedělá nic.

Bitfield metadat na dlaždici, klíčovaný indexem dlaždice. Flagy patří grafice dlaždice, takže je sdílejí všechny buňky nakreslené touto dlaždicí. Sáhni po něm, když ručně stavíš pg.Tilemap (ne přes picogame_scene) a chceš kolizní/coin/exit logiku jako one-linery místo boční tabulky. Pokud načítáš scény pomocí picogame_scene, použij raději view.is_solid / view.tile_has - tento modul je nízkoúrovňová alternativa.

Osm pojmenovaných bitů a jejich masek: B_SOLID, B_HAZARD, B_LADDER, B_PLATFORM, B_WATER, B_COIN, B_EXIT, B_CUSTOM jsou bitové indexy 0..7; SOLID, HAZARD, LADDER, PLATFORM, WATER, COIN, EXIT, CUSTOM jsou odpovídající masky 1 << bit. Při sestavování tabulky používej masky, při dotazování B_* indexy.

TileFlags(flags=None, tile_px=8) sestaví tabulku. flags je buď dict {tile_index: bitfield}, nebo list/bytes indexovaný indexem dlaždice; tile_px je velikost dlaždice používaná pixelovým pomocníkem.

  • tf.get(tile, bit=None) - celý bitfield dlaždice, nebo jeden bool flag, pokud zadáš bit (index B_*).
  • tf.set(tile, bit, value=True) - nastav (nebo smaž) bit na dlaždici za běhu.
  • tf.at(tilemap, tx, ty, bit) - flag bit dlaždice v buňce (tx, ty).
  • tf.at_px(tilemap, px, py, bit) - flag bit dlaždice pod MAP-LOCAL pixelem (px, py); běžná kolizní sonda.
import picogame as pg
import picogame_tiles as tiles
TILE = 8
tf = tiles.TileFlags({1: tiles.SOLID, 2: tiles.COIN, 3: tiles.EXIT}, tile_px=TILE)
def blocked(level, tx, ty): # level is a pg.Tilemap
return tf.at(level, tx, ty, tiles.B_SOLID)
if tf.at_px(level, px, py, tiles.B_SOLID): # is the tile under pixel (px, py) solid?
stop()

Záludnosti: at_px předpokládá, že mapa je na souřadnicích (0, 0) - pokud je posunutá, odečti před voláním počátek mapy od px/py sám. Dlaždice chybějící v tabulce se čtou jako 0 (žádné flagy). Sestavuj pomocí MASK konstant (tiles.SOLID), ale dotazuj se pomocí BIT indexů (tiles.B_SOLID); záměna tiše kontroluje nesprávný bit.

Generátory, které PEČOU jednobarevné PAL8 pg.Bitmapy v kódu - zástupné sprity a tilesety bez PNG assetů a bez změn firmware. Sáhni po nich pro prototypy, geometrické grafické prvky (kuličky, cihly, lodě) nebo cokoliv, co bys jinak řešil ručním packing loopm. Poznámka: toto NENÍ C Canvas (který kreslí do živého povrchu) - shapes vrací znovupoužitelný Bitmap, který předáš Sprite nebo Tilemap. Index 0 je průhledný, 1 je barva.

  • rect(w, h, color) - vyplněný obdélník w x h.
  • circle(d, color) - vyplněný disk průměru d.
  • ring(d, color, thickness=2) - obrys kružnice průměru d.
  • from_mask(mask, color) - bitmapa z listu stringů; #, X nebo 1 nastaví pixel. Velikost odpovídá masce.
  • atlas(frames_data, w, h, color) - zabalí list w*h bufferů 0/1 do jedné horizontální víceframové bitmapy (jedna barva). Obecný builder “frame sheetu”.
  • color_frames(w, h, colors) - víceframová bitmapa, kde frame i je plná výplň colors[i]; frame 0 je už barevný. Index 0 je průhledný.
  • tileset_colors(w, h, colors) - tileset, kde frame 0 je PRÁZDNÝ (průhledný) a frame i je plná výplň colors[i-1]. Tilemap tak čte hodnotu dlaždice 0 jako prázdnou a 1..N jako barevné dlaždice.
  • poly_frames(size, points, nframes, color, fill=True) - upeče nframes rotací polygonu (body kolem středu, +y dolů) do atlasu size x size. Engine nemá za běhu rotaci, takže toto je vzor předrotovaných framů pro lodě/asteroidy. Nastav fill=False pro obrys.
import picogame as pg
import picogame_shapes as shp
ball = shp.circle(4, pg.rgb565(255, 255, 120))
bricks = shp.tileset_colors(16, 8, [pg.rgb565(220, 70, 70),
pg.rgb565(80, 140, 240)]) # value 0 empty, 1..2 coloured
ship = shp.poly_frames(16, [(0, -8), (6, 7), (0, 4), (-6, 7)], 16,
pg.rgb565(200, 220, 255)) # 16 pre-rotated frames
sprite = pg.Sprite(ball, 100, 60)

Záludnosti: frame 0 v color_frames je viditelná barva, ale v tileset_colors je frame 0 průhledný (prázdný) - vyber ten, který odpovídá tomu, jak tvůj Tilemap zachází s hodnotou 0. circle vyplňuje bitmapu od okraje k okraji, takže při zvětšení vypadá jako by měla ploché hrany. poly_frames s nframes=1 upeče jediný neotočený frame (použij pro tvar s pevným směrem).

Znovupoužitelný sprite pool fixní velikosti - vzor pre-alokace / spawn / free / iterace, který si každá hra se spawnováním (střely, nepřátelé, orby) doteď dělala ručně. Jako příznak živosti používá sprite.visible (žádné paralelní data["on"]) a sprite.data pro stav každé entity. Sáhni po něm kdykoliv máš omezený počet přechodných spritů a chceš nulovou alokaci za frame. Proč na zařízení záleží pre-alokace, se dočteš v /cs/memory/.

Pool(scene, bitmap, capacity, anchor=None, fixed=False) předalokuje capacity skrytých spritů sdílejících bitmap, nastaví každému anchor (pokud je zadán) a data = None a přidá je všechny do scene (fixed= se předává přímo do scene.add).

  • pool.items - underlying list spritů; iteruj ho přímo pro zero-alloc aktualizace.
  • pool.spawn() - udělá první volný (skrytý) sprite viditelným a vrátí ho, nebo None, pokud je pool plný.
  • pool.free(s) - skryje sprite s (vrátí ho do poolu).
  • pool.free_all() - skryje všechny sprity (použij při resetu levelu).
  • pool.count() - počet živých (viditelných) spritů; levné, ale pro samotné sprity iteruj items.
import picogame as pg
import picogame_pool
bullets = picogame_pool.Pool(scene, bullet_bm, 6, anchor=(0.5, 0.5))
b = bullets.spawn() # a now-visible sprite, or None if full
if b:
b.data = {"vx": 6}
b.move(x, y)
for s in bullets.items: # zero-alloc iteration
if not s.visible:
continue
s.move(s.x + s.data["vx"], s.y)
if off_screen(s):
bullets.free(s)

Záludnosti: při iteraci vždy přeskakuj skryté sloty (if not s.visible: continue) - items obsahuje každý slot, živý i mrtvý. spawn() znovupoužívá naposledy uvolněný slot, takže pokud ve stejném kroku zavoláš free(s) a spawn(), přečti si stav z s.data PŘED uvolněním - nový spawn ho přepíše. Plný pool vrátí z spawn() None; zkontroluj to. Všechny sprity sdílejí jednu bitmapu, takže frame/animaci pro každou entitu musíš nastavit na každém spritu po spawn().