Přeskočit na obsah

Text a UI

Tyhle moduly mění řetězce a stisky tlačítek na pixely: vykreslíš text z libovolného fontu, přidáš HUD label, vyvoláš dialogové okno nebo spustíš menu s kurzorem. Holé signatury najdeš v referenci; kompletní hry od začátku do konce ukazují tutoriály.

Vykreslí řetězec libovolným fontio fontem (typicky přibaleným terminalio.FONT) do picogame.Bitmap - žádné extra soubory s assety, žádný reflash. Sáhni po něm, když chceš ostrý text z fontu, který už máš, a nevadí ti HUD pozadí.

  • render_text(pg, font, text, fg, bg=None) - poskládá text do PAL8 bitmapy a vrátí n-tici (bmp, w, h) (bitmapu plus rozměr v pixelech). fg/bg jsou wire barvy z pg.rgb565(...). bg=None nechá pozadí průhledné (index palety 0); neprůhledné bg znamená, že překreslení úplně přepíše starý text, takže HUD aktualizace nepotřebují samostatný clear.
  • render_text_pal(pg, font, text, fg, bg=None) - to samé jako výše, ale vrátí (bmp, w, h, palette). Podrž si palette (array('H')) a měň palette[1] (barvu fg) pro živé barevné mihotání bez přestavování bitmapy - C Bitmap čte ten samý buffer.
  • Label(pg, font, x, y, fg, bg) - umístěný label vykreslený okamžitě (dobré pro HUD nad obrazovkou, kterou vykreslíš sám).
    • .set(text) - překreslí jen tehdy, když se text změnil; vrátí True, pokud ano, False, pokud přeskočil. Ne-řetězce převede přes str().
    • .move(x, y) - přemístí a vynutí překreslení na novém místě při dalším set/draw.
    • .draw(display, buffer) - překreslí jen obdélník labelu přes pg.render (jediný present).
    • .w, .h - rozměr posledního vykresleného textu v pixelech.
import picogame_font, terminalio
hud = picogame_font.Label(pg, terminalio.FONT, 4, 4,
pg.rgb565(255, 255, 255), BG)
# each frame, after you draw the screen:
hud.set("SCORE %06d" % score) # rebuilds only on change
hud.draw(board.DISPLAY, bufA) # repaints just its rect

Záludnosti: Bitmap.palette není na zařízení Python atribut, takže pro mihotání musíš měnit palette pole z render_text_pal, ne bmp.palette. Nad živou, scrollující scénou použij radši picogame_ui.SceneLabel - okamžitý Label se pere se scene.refresh().

Samostatný 8x8, 2bitový (4 odstíny, anti-aliasovaný/obtažený) bitmapový font s herními glyphy (kódy 0..31 jsou šipky/srdíčka/hvězda/nota/roury; 32+ je ASCII). Jeho vestavěné tmavé obtažení dává kontrast nad hrou bez HUD boxu, takže index 0 je průhledný a text je čitelný nad světem. Sáhni po něm pro plovoucí bojový text, “+10”, srdíčka nebo jakýkoli label, který chceš bez panelu na pozadí.

  • render_text(pg, text, fg=None, outline=None, mid=None, bg=None) - vykreslí text do PAL8 bitmapy a vrátí (bitmap, w, h). Čtyři odstíny se mapují na: 0 -> bg/průhledné, 1 -> outline, 2 -> mid, 3 -> fg. Výchozí barvy: fg bílá, outline černá, mid středně šedá; všechny jsou rgb565 wire barvy. Podporuje \n pro více řádků. Předej bg pro neprůhledné pozadí (jinak je index 0 průhledný).
  • Konstanty symbolů (jednoznakové řetězce, které zřetězíš do textu): ARROW_U, ARROW_D, ARROW_R, ARROW_L, BOXX, STAR, HEART, BALL, NOTE.
  • GLYPH_W, GLYPH_H - obě 8, velikost buňky na glyph.
import picogame_bitfont as bf
bmp, w, h = bf.render_text(pg, "LIVES " + bf.HEART * 3) # white, outlined, transparent
spr = pg.Sprite(bmp, x, y) # place anywhere
spr.scale = 2 # scale up for big text

Záludnosti: žádná Label/widget třída tady není, jen render_text -> Sprite si spravuješ sám. Tenhle font je ekosystémová vrstva (grafika odvozená z circuitpython-stage, MIT), ne čistý bundle; pokud je RAM nadoraz, vrať se k picogame_font + terminalio. Viz memory pro RAM kompromis.

Lešení pro UI: na kameře nezávislý HUD label, víceřádkové textové boxy a menu s kurzorem. Klíčové rozdělení je scene-layer vs. okamžité:

  • Scene* widgety (SceneLabel, SceneBox, SceneMenu) žijí ve scéně jako fixed (na kameře nezávislé) vrstvy, takže je scene.refresh() vykreslí každý frame bez extra draw volání. Tyhle použij nad živou, scrollující scénou.
  • Okamžité widgety (Label v picogame_font, TextBox, Menu, HudBar) kreslí přes pg.render a jsou pro statickou obrazovku, kterou vykreslíš celou sám - nad živou scénou se perou se scene.refresh() a blikají.

Widgety založené na tick() vrací: vybraný index/buňku na A (potvrdit), ui.CANCEL (-2) na B (zpět), nebo None během navigace. Viz scene-format pro fixed vrstvu a hardware pro tlačítka.

SceneLabel(scene, pg, font, x, y, fg, bg) - jeden řádek textu připnutý nad scrollujícím světem.

  • .set(text) - překreslí jen při změně (vymění bitmapu spritu; dirty-rect ošetří staré/nové hranice). Prázdný řetězec sprite skryje, nezbyde žádná zbytková záplata pozadí.

SceneBox(scene, pg, font, x, y, w, h, fg, bg, nlines=3, key=None, border=None) - víceřádkový dialogový/stavový panel (Canvas + řádky SceneLabel) nad živou scénou, v jednom present bez blikání. key je barva, která je při skrytí průhledná (výchozí magenta); předej border pro 3D rám.

  • .show(lines) - naplní panel a nastaví text, pak ho zobrazí. Volej jednou, ne každý frame.
  • .hide() - udělá panel úplně průhledný a vymaže řádky.
  • .set_line(i, text) - aktualizuje jeden řádek na místě (bez překreslení Canvasu/rámu).

HudBar(pg, display, buffer, x, y, w, h, bg) - HUD proužek vykreslený mimo scénu, v oblasti, kterou si scéna rezervuje přes Scene(..., top=/bottom=). Scéna tam nikdy nekreslí, takže to každý frame nic nestojí; redraw() voláš jen při změnách HUD. buffer je libovolný scratch proužkový buffer (např. scénin bufA); bg je plochá barva.

  • .add(sprite) - uloží sprite (srdíčka, ukazatele) do baru; vrátí ho.
  • .label(font, x, y, fg, text=" ") - vytvoří textový sprite uložený v baru; vrátí Sprite. Pamatuje si svůj font/fg pro set_text.
  • .set_text(sprite, text) - znovu vykreslí label sprite vytvořený přes .label().
  • .redraw() - překreslí proužek (plochý bg + viditelné sprity) v jednom pg.render.
hud = ui.HudBar(pg, board.DISPLAY, bufA, 0, 0, W, BAR, pg.rgb565(10, 12, 24))
hud_l = hud.label(terminalio.FONT, 4, 3, INK, "SCORE 0 LIVES 3")
hud.redraw()
# later, only when it changes:
hud.set_text(hud_l, "SCORE %d LIVES %d" % (score, lives))
hud.redraw()

TextBox(pg, font, x, y, w, h, fg, bg, maxlines=6) - víceřádkový box v prostoru obrazovky (vyplněný obdélník + řádky textu) pro statické dialogové/bojové/menu obrazovky.

  • .draw(display, buffer, lines, force=False) - přeskočí překreslení, když se lines nezměnily; když už kreslí, jdou bg a každý řádek ven v jednom pg.render (bez probliknutí prázdné výplně). Předej force=True poté, co se obrazovka pod ním vymazala (např. celoobrazovkový pg.render).
  • .draw_line(display, buffer, i, text) - překreslí jeden řádek na místě, atomicky.

Menu(pg, font, x, y, items, fg, bg, *, title=None, rows=None, width=None, paged=True) - menu s kurzorem nad TextBox (okamžité; pro statické obrazovky). UP/DOWN navigují s auto-repeatem; draw() vykreslí značku > . rows=None zobrazí všechny položky (bez scrollu); nastav to pro scrollování dlouhého seznamu. paged=True (výchozí) skáče na okrajích o celou stránku (levné; plné překreslení jen na hranici stránky) - mnohem svižnější než řádkový scroll paged=False u téměř celoobrazovkových seznamů, protože tenhle hardware neumí panel hardwarově scrollovat. Klíčové argumenty jsou keyword-only (po *).

  • .tick(btn) - vrátí vybraný index na A, ui.CANCEL na B, jinak None.
  • .draw(display, buffer, force=False) - překreslí jen to, co se změnilo (nic / 2 dotčené řádky při pohybu kurzoru / celý box při scrollu). force=True překreslí bezpodmínečně po vymazání.
bmenu = ui.Menu(pg, terminalio.FONT, 8, H - 72,
["ATTACK", "MAGIC", "HEAL", "FLEE"], WHITE, NAVY)
# each frame:
act = bmenu.tick(btn) # index on A, ui.CANCEL on B, else None
bmenu.draw(board.DISPLAY, bufA)

SceneMenu(scene, pg, font, x, y, items, fg, bg, title=None, rows=None, width=None, border=None, paged=True) - to samé menu, ale postavené na SceneBox, pro použití nad živou scénou (bojové akce, popup ve hře). Stejná navigace/stránkování jako Menu.

  • .show(sel=0) - zobrazí ho (resetuje kurzor). Od té chvíle ho vykresluje scéna - žádné volání draw().
  • .hide() - skryje ho.
  • .tick(btn) - stejný kontrakt návratu jako Menu; překreslí jen řádky, které se změnily.

GridCursor(cols, rows, tx=0, ty=0, wrap=False) - čistě logický 2D kurzor pro bojiště, inventář dlaždic nebo match-3. Vlastní pohyb (auto-repeat D-padu) a potvrzení/zrušení; mřížku a zvýraznění na (cursor.tx, cursor.ty) kreslíš ty. wrap=True na okrajích zalomí, jinak omezí (clamp).

  • .tick(btn) - vrátí n-tici (tx, ty) na A, ui.CANCEL na B, jinak None.
  • .tx, .ty - aktuální buňka.
  • .index (property) - ty * cols + tx, šikovné pro indexování plochého seznamu.
cur = ui.GridCursor(N, N) # N x N board
# each frame:
pick = cur.tick(btn) # (tx, ty) on A, ui.CANCEL on B, else None
# you draw the highlight yourself at (cur.tx, cur.ty)

Záludnosti: největší past je míchání těchhle dvou rodin - okamžité Menu/TextBox nad živou scénou se vymaže tím, jak Display posílá proužky přes něj (prostě “zmizí”); použij tam jeho Scene* dvojče. Výchozí heuristika width u menu (~11 px/znak) nadhodnocuje velikost u úzkého fontu a může utéct mimo obrazovku - předej width= pro dlouhé labely nebo celoobrazovkové seznamy. SceneBox.show() se volá jednou, ne každý frame.

Menu nastavení/hodnot postavené na picogame_ui.SceneBox, pro vzor nastavení / obchod / nábor. Sáhni po něm, když obyčejné ui.Menu (vyber index) nestačí, protože každý řádek nese upravitelnou hodnotu: volbu obtížnosti, krokovač hlasitosti, přepínač zvuku plus prostou akci jako “Start”. Je to scene-layer widget, takže ho použij nad živou scénou; úpravy hodnot se zobrazují okamžitě. Je záměrně provizorní - držený mimo zamrzlé ui jádro, aby se mohl vyvíjet.

  • OptionsMenu(scene, pg, font, x, y, w, rows, fg, bg, title=None, border=None) - rows je seznam slovníků, každý s kind:
    • choice - {"key", "label", "kind": "choice", "choices": [...]}; cykluje seznamem. (Neprázdné choices je povinné - jinak rovnou vyhodí ValueError.)
    • stepper - {"key", "label", "kind": "stepper", "value", "min", "max"}; ctí i volitelný "step" (výchozí 1). Omezí na min/max.
    • toggle - {"key", "label", "kind": "toggle", "value": True/False}.
    • action - {"key", "label", "kind": "action"}; žádná hodnota, jen vrátí svůj key na A.
  • .show(sel=0) - zobrazí a vykreslí (volej jednou); scene.refresh() ho pak vykresluje.
  • .hide() - skryje panel.
  • .tick(btn) - UP/DOWN posunou kurzor; LEFT/RIGHT mění hodnotu vybraného řádku živě (steppery/choices auto-repeatují při držení; toggle se překlopí jen na čerstvém stisku, takže nemůže oscilovat). Vrátí key vybraného řádku na A, ui.CANCEL na B, jinak None.
  • .value(key) - kdykoli přečte aktuální hodnotu řádku: choice vrátí vybraný řetězec, stepper int, toggle bool; None, pokud takový key není.
import picogame_options as opt
menu = opt.OptionsMenu(scene, pg, font, 40, 40, 240, [
{"key": "diff", "label": "Difficulty", "kind": "choice", "choices": ["Easy", "Normal", "Hard"]},
{"key": "vol", "label": "Volume", "kind": "stepper", "value": 7, "min": 0, "max": 10},
{"key": "snd", "label": "Sound", "kind": "toggle", "value": True},
{"key": "done", "label": "Start", "kind": "action"},
], WHITE, NAVY, title="OPTIONS")
menu.show()
while True:
btn.poll()
k = menu.tick(btn)
if k == "done":
diff = menu.value("diff") # read live values on the action row
elif k == opt.CANCEL:
menu.hide()
scene.refresh() # paints the menu - no draw() call

Záludnosti: importuje CANCEL z picogame_ui, takže opt.CANCEL a ui.CANCEL jsou to samé -2. Protože je to SceneBox widget, potřebuje pod sebou živý scene.refresh() - na statické obrazovce vykreslené čistě přes pg.render se nevykreslí.