Efekty & juice
Vrstva “juice”: třesení obrazovky, dither přechody, plynulé tweeny, sledovací kamera, rastrové pozadí a triky s paletou. Všechno je čistý Python nad enginem - bez změny firmware - a většina z toho nestojí skoro žádnou RAM, protože kreslí po scanlinech přes pg.StripDraw nebo mutuje paletu místo pixelů. Holé signatury najdeš v /cs/reference/; tato stránka popisuje chování jednotlivých symbolů a záludnosti.
picogame_fx
Sekce “picogame_fx”Pomocné efekty, které instancuješ k Scene a voláš na nich tick() každý frame. Sáhni po nich, když hra funguje, ale působí ploše: zásah musí víc praštit (Shake, InvertFlash), přechod potřebuje dýchat (Fade), hodnota by měla plynule dojíždět místo skočit (Tween), svět je větší než obrazovka (Camera) nebo pozadí chce hloubku (Sky, Scanlines).
import picogame_fx as fx
fx.Shake - třesení obrazovky na principu trauma
Sekce “fx.Shake - třesení obrazovky na principu trauma”Doznívající shake, který se skládá s tvou kamerou místo aby s ní bojoval. Přidávej trauma při nárazech, pak každý frame volej tick().
Shake(scene, max_offset=6, decay=0.03, seed=0x9E37)-max_offsetje maximální pixel offset (asi 6 sedí na 320x240; nad 10 to zakrývá dění).decayje trauma ztracené za frame (asi 0.03 vypadá jako “kopnutí”, ne “vibrování”)..add(amount)- přidá trauma v rozsahu 0..1, oříznuté na 1.0. Asi 0.6 na zásah nebo explozi, 0.15 na malý náraz. Trauma se před použitím umocní na druhou, takže malé události skoro netřesou a velké praští naplno..tick(cam_x=0, cam_y=0)- přidá doznívající náhodný offset k(cam_x, cam_y)a zavoláscene.set_views výsledkem. VracíTrue, dokud shake trvá. Předej sem offset kamery, aby shake a pohybující se kamera nevolalyset_viewzvlášť a nepřepisovaly si výsledky.
import picogame_fx as fx
shaker = fx.Shake(scene, max_offset=6)# ...při zásahu:shaker.add(0.8)# ...každý frame (bez kamery, takže dej 0,0):shaker.tick(0, 0)Záludnosti: Shake si bere scene.set_view jen pro sebe, dokud běží - pokud kamerou taky pohybuješ ručně, předej ten offset do tick(cam_x, cam_y) místo aby ses volal set_view zvlášť. Viz /cs/hardware/ pro velikost obrazovky, vůči které max_offset ladíš.
fx.Fade - dither přechod / ztmavení / záblesk
Sekce “fx.Fade - dither přechod / ztmavení / záblesk”Overlay přes StripDraw, který přes rect rastruje barvu řazeným (Bayerovým) ditherem - žádné alpha blending, klasický 1-bit/Game Boy look. V klidovém stavu se schoulí na rect 0x0, takže na dirty-rect rendereru nestojí nic.
Fade(scene, width, height, x=0, y=0, color=0, cell=8)- pokryje rect(x, y, width, height); výchozí hodnoty pokryjí celou obrazovku. Sub-rect ztmaví jen tu oblast (panel za dialogem, postranní panel).color=0je černá;cellje velikost dither bloku. Přidáno do scény sfixed=True, takže ignoruje kameru..set(level)- skočí okamžitě nalevel(0 = průhledné .. 16 = plné). Použij 16 pro start jako neprůhledný před fade-in..to(target, speed=2.0)- postupuje ktargetrychlostíspeedlevelů za frame. Vracíself..out(speed=2.0)/.into(speed=2.0)- zkratky proto(16)(k neprůhledné) ato(0)(k průhledné)..dim(level=8)- skočí na částečné ztmavení, např. 50% dim za menu..clear()skočí na 0..pulse(level=12, speed=2.0)- vystoupá nalevela pak automaticky zpět na 0; plynulý celoobrazovkový záblesk. Nechlevelpod 16, ať zůstane průhledný dither, nikdy plná zeď..tick()- posunelevelktargetospeed. VracíTrue, když je cíl dosažen..is_done(property) -True, kdyžlevel == target.
import picogame_fx as fx
fader = fx.Fade(scene, W, H)# ...spusť fade do černé:fader.out(speed=2)# ...každý frame, fade zpět, jakmile dosáhneme černé:if fading and fader.tick(): fader.into(speed=2)Záludnosti: level běží od 0 do 16, ne 0..255 nebo 0.0..1.0. Bílý Fade rychle pulzující (fx.Fade(scene, W, H, color=pg.rgb565(255, 255, 255)).pulse()) je levný záblesk při zásahu, ale InvertFlash níže je ještě levnější.
fx.Tween - plynulé dojíždění skaláru k cíli
Sekce “fx.Tween - plynulé dojíždění skaláru k cíli”Exponenciální ease-out pro jedinou hodnotu prováděný každý frame: UI posuny, pop-up škálování, číslo, které by mělo “dohánět” plynule. Žádné keyframes ani rozvrhy.
Tween(value=0.0, speed=0.2)-speedje podíl zbývající mezery, který se uzavře každý frame (0..1)..to(target, speed=None)- nastav nový cíl (a volitelně novou rychlost). Vracíself..set(value)- okamžitě snapne hodnotu i cíl navalue..tick()- posune hodnotu ospeedzlomek k cíli a vrátí novou hodnotu. Snapne přesně, když je rozdíl do 0.01..is_done(property) -True, jakmile se hodnota rovná cíli.
import picogame_fx as fx
y = fx.Tween(0)y.to(100) # posuň panel dolů na y=100# ...každý frame:panel.y = int(y.tick())Záludnosti: pouze ease-out, takže nikdy nepřestřelí ani neodskočí. tick() vrací hodnotu - čti návratovou hodnotu, ne .value, pokud chceš čerstvě posunuté číslo.
fx.Camera - plynulá sledovací kamera
Sekce “fx.Camera - plynulá sledovací kamera”Sleduje bod ve světě a produkuje offset pohledu scény, vystředěný a volitelně omezený na velikost světa. Použij ji, když je level větší než obrazovka.
Camera(scene, w, h, lerp=0.18, world_w=0, world_h=0)-w/hje velikost obrazovky;lerpje vyhlazování sledování za frame;world_w/world_h(pokud nejsou nula) omezí pohled, aby nepřesáhl okraj světa..follow(tx, ty, snap=False)- posouvá střed kamery k(tx, ty)olerp, nebo tam snapne ssnap=True. Vracíself, takže můžeš řetězit..apply()- vypočítá offset a přímo zavoláscene.set_view. Bez alokace; vracíNone. Použij, když není žádný shake..offset()- vypočítá a vrátí offset jako n-tici(ox, oy)(alokuje). Předej to doShake.tick(ox, oy)pro složení obou efektů.
import picogame_fx as fx
cam = fx.Camera(scene, W, 240, world_w=bounds_w)# ...každý frame:cam.follow(player.x, 120).apply()Záludnosti: apply() i Shake volají scene.set_view, takže nespouštěj obě naslepo. Pro kombinaci si vezmi ox, oy = cam.follow(...).offset() a předej to do shaker.tick(ox, oy), ať se shake skládá na kameru. Viz /cs/scene-format/, co set_view dělá s pohledem.
fx.Sky - vertikální gradientní pozadí
Sekce “fx.Sky - vertikální gradientní pozadí”Gradientní pásmo po scanlinách (obloha, backdrop, den-noc) kreslené přes StripDraw s nulovou RAM - žádný buffer. Přidej ho jako první; je to vrstva na pozadí.
Sky(scene, x, y, w, h, top, bottom)- vyplní rect, interpoluje každý scanline od wire-RGB565 barvytopkbottom. Přidáno sfixed=True. Měň.top/.bottomv čase pro cyklus den-noc.
import picogame as pgimport picogame_fx as fx
sky = fx.Sky(scene, 0, 0, W, HORIZON, pg.rgb565(60, 120, 240), pg.rgb565(200, 230, 255))Záludnosti: kreslí fill_rect za každý scanline, takže drž výšku pásma rozumnou; je to levné, ale ne zadarmo u vysokého celoobrazovkového gradientu.
fx.Scanlines - CRT scanline overlay
Sekce “fx.Scanlines - CRT scanline overlay”Ztmaví každý N-tý řádek pro CRT/LCD-grid vzhled. StripDraw, nulová RAM. Přidej ho jako poslední, ať leží navrchu.
Scanlines(scene, x, y, w, h, step=2, dark=pg.rgb565(0, 0, 0))-step=2ztmaví každý druhý řádek;darkje barva overlayu. Předpočítá 1px dither řádek a blituje ho jednou na každý ztmavený řádek (jeden blit místo smyčky na každý pixel).
import picogame_fx as fx
scanlines = fx.Scanlines(scene, 0, 0, W, H) # přidej JAKO POSLEDNÍ, navrch všehoZáludnosti: záleží na pořadí - přidej ho až po všech herních vrstvách, jinak bude přemalováno.
fx.InvertFlash - hardwarový hit-flash zadarmo
Sekce “fx.InvertFlash - hardwarový hit-flash zadarmo”Přepne celý panel na jeho negativ na pár framů pomocí hardwarové inverze barev řadiče (pg.invert). Žádný StripDraw, žádný buffer, žádný repaint - levnější než Fade. Potřebuje panel třídy ST7789/ST7735; na simulátoru je to tichý no-op.
InvertFlash(display, frames=3, normal=True)-displayje např.board.DISPLAY;framesje délka záblesku.normalje klidový stav inverze panelu. PicoPad posílá INVON při inicializaci, takže jeho klidový stav jenormal=True(výchozí); předejnormal=Falsejen pro panel, jehož init neprovádí inverzi..pulse(frames=None)- teď přepni z klidového stavu (volitelně na vlastní počet framů)..tick()- odpočítej a obnov klidový stav, jakmile záblesk skončí. VracíTrue, dokud záblesk trvá. Volej ho poscene.refresh(), ať INVON/INVOFF je poslední bus operace framu.
import boardimport picogame_fx as fx
flash = fx.InvertFlash(board.DISPLAY, frames=6)# ...při zásahu:flash.pulse()# ...po scene.refresh():flash.tick()Záludnosti: nastav normal správně, jinak panel zůstane invertovaný - na PicoPadu nech výchozí normal=True. Efekt existuje jen na reálném hardwaru; viz /cs/hardware/, které řadiče podporují INVON/INVOFF.
picogame_palette
Sekce “picogame_palette”Levné barevné efekty na PAL8 art mutací palety místo pixelů - klasický Game Boy trik. Sáhni po něm pro animovanou vodu/lávu (cycle), přebarvení sdílené bitmapy pro hráče 1/2 nebo normální/vystrašený stav (swap), nebo plynulé ztmavení/barevný přechod (fade) - vše za hrstku zápisů do pole a nulové extra art. Položky palety jsou wire-order RGB565 inty (to, co vrací pg.rgb565() a co nese pg.Bitmap jako svou array('H') paletu).
import picogame_palette as palette
snapshot(palette)- vrátí kopii palety jakoarray('H'). Ulož originál jednou, před jakýmkoliv fadingem nebo cyclingem, abys mohl fadovat relativně k němu nebo horestoreobnovit.restore(palette, base)- zkopírujebasezpět dopalettena místě.cycle(palette, lo, hi, step=1)- rotuje položky[lo..hi]včetně ostep(s přetečením), na místě bez alokace, takže je bezpečné volat každý frame. Rezervuj skupinu indexů pro tekoucí barvy, nakresli s nimi art a ty se samy animují.swap(dst_palette, src_palette)- zkopíruje jednu paletu přes druhou (do délky kratší). GBC-style přebarvení: drž jednu PAL8 bitmapu a předávej jí různé palety pro každou variantu. Levnější než druhá bitmapa.fade(palette, base, t, target=0, skip=None)- interpoluje každou položkupaletteod uloženébasek wire barvětargetpodlet(0.0 = base .. 1.0 = target).target=0(černá) způsobí fade out; bílý target zmizí do bílé.skipnechá jeden index beze změny (např. průhledný index).
import picogame_palette as palette
# ...každý frame, animuj rezervovaný pás vodních barev:palette.cycle(water_bmp.palette, 1, 6)water.touch() # řekni rendereru, že se paleta změnilaZáludnosti: dirty-rect renderer čte paletu při blitování, ale sám od sebe si změny palety nevšimne. Po každém cycle/swap/fade/restore zavolej sprite.touch() na spritech používajících tu bitmapu (nebo scene.invalidate() / přemaluj oblast tilemapu) - jinak se vizuálně nic nezmění. Cena je repaint dotčených spritů v daném framu, takže cyclinguj malý pás (pruh vody), ne celou obrazovku. Viz /cs/memory/, proč je to lepší než mít druhou přebarvenou bitmapu.