Efecte de sprite și text
Umbră, contur, glow și blur pe sprite-uri 2D și pe text, cu cât costă fiecare, ce face unui sprite în mișcare, și rețeta manuală pe care o înlocuiește.
Pentru ce este asta#
Până acum un sprite 2D putea purta o tentă și o imagine, și nimic altceva. Pentru a scoate un personaj din fundal trebuia să construiești efectul de mână: creezi un al doilea sprite, îl colorezi negru, îl decalezi, îl pui în spate, și îl ții sincronizat cu primul la fiecare cadru în parte. Funcționează. Este de asemenea patru linii în plus de contabilitate per personaj, iar în ziua în care uiți una dintre ele, umbra rămâne în urma proprietarului ei.
Patru comenzi fac asta pentru tine, pe sprite-uri și pe text:
hero = LoadSprite("hero.png")
SpritePosition(hero, 160, 120)
SpriteShadow(hero, 4, 4, 6, 128) ' offset x, offset y, softness, opacity
SpriteOutline(hero, 2, 255, 255, 255) ' thickness, colour
SpriteGlow(hero, 0, 240, 255, 8) ' colour, size
DO
Sync()
LOOP
Nimic altceva de întreținut. Mută sprite-ul, scalează-l, întoarce-l, redă-i animația: efectele urmează.
Cele patru efecte#
Umbra#
SpriteShadow(spriteID, offsetX, offsetY, softness, opacity)
SpriteShadowColor(spriteID, red, green, blue)
offsetX și offsetY sunt în unitățile tale proprii de ecran, numărate la
fel ca SpritePosition: Y pozitiv este în jos. softness întinde
umbra; opacity merge de la 0 la 255. Culoarea este neagră dacă nu spui
altceva.
SpriteShadow(hero, 3, 5, 4, 110)
SpriteShadowColor(hero, 20, 0, 40) ' a cold shadow, not a grey one
O umbră cu opacitatea 0 nu este nicio umbră. Așa o oprești.
Conturul#
SpriteOutline(spriteID, thickness, red, green, blue)
Conturul urmează silueta sprite-ului, nu dreptunghiul în care locuiește: un PNG transparent primește un contur în jurul desenului, nu în jurul fișierului. Grosimea este în unități de ecran, iar 1 până la 3 este intervalul util. Peste asta, cele opt direcții din care este construit conturul încep să se vadă ca opt lobi.
SpriteOutline(hero, 2, 255, 255, 255) ' the readable-on-anything outline
SpriteOutline(hero, 0, 0, 0, 0) ' off
Glow-ul#
SpriteGlow(spriteID, red, green, blue, size)
Un halou colorat, întinzându-se size unități dincolo de siluetă. Stă în
spatele sprite-ului și în fața umbrei, ceea ce îți permite să le folosești pe
amândouă.
SpriteGlow(pickup, 255, 210, 0, 10) ' this one is worth walking to
Blur-ul#
SpriteBlur(spriteID, radius)
Blur-ul este cel diferit dintre toate. Celelalte trei sunt desenate în jurul sprite-ului; blur-ul înlocuiește imaginea proprie a sprite-ului cu o copie neclară a ei. Asta înseamnă două lucruri care merită știute:
- nu costă nimic per cadru (imaginea neclară este calculată o singură dată), și
- blur-ul este tăiat la marginea imaginii. Un desen care atinge marginea PNG-ului lui pierde partea din haloul lui care ar cădea în afară. Lasă câțiva pixeli transparenți de margine în fișier dacă plănuiești să îl blurezi.
SpriteColor continuă să funcționeze pe un sprite blurat.
Oprirea totului, și repornirea#
SpriteEffects(spriteID, 0) ' all four off
SpriteEffects(spriteID, 1) ' all four back, exactly as they were
SpriteEffects nu îți șterge setările. Este un comutator, nu un reset, așa
că un personaj poate clipi fără să trebuiască să îi redeclari umbra după
aceea.
Aceleași patru pe text#
score = CreateText("SCORE 0")
TextPositionSet(score, 10, 10)
TextSizeSet(score, 6)
TextOutline(score, 3, 0, 0, 0) ' readable on any background
TextShadow(score, 2, 2, 4, 160)
TextGlow(score, 255, 80, 0, 6)
TextBlur(score, 0)
TextEffects(score, 1)
Semnăturile se potrivesc exact cu cele de sprite, așa că nu este nimic nou de învățat. Două diferențe de comportament:
- Un glow câștigă în fața softness-ului unei umbre. Amândouă sunt desenate prin același mecanism, iar un glow fără softness este invizibil, în timp ce o umbră fără softness este tot o umbră. Când ceri amândouă, glow-ul primește softness-ul.
- Acestea se aplică pe text vectorial, nu pe text bitmap-font. Un text
desenat cu
TextFontImageeste o compoziție de sprite-uri; comenzile sunt ignorate tăcut pe el. Folosește efectele de sprite pe sprite-urile din acel caz.
Un exemplu lucrat: lizibil pe orice#
Problema pe care aceste comenzi o rezolvă efectiv este contrastul pe care nu îl controlezi. Un scor alb este invizibil pe zăpadă; unul negru este invizibil noaptea. Un contur o rezolvă o dată, pentru orice fundal vei desena vreodată.
CreateText(1, "SCORE 0")
TextPositionSet(1, 10, 10)
TextSizeSet(1, 8)
TextColorSet(1, 255, 255, 255, 255)
TextOutline(1, 3, 0, 0, 0)
hero = LoadSprite("hero.png")
SpriteName(hero, "hero")
SpriteShadow(hero, 4, 6, 5, 120)
SpriteOutline(hero, 2, 255, 255, 255)
points = 0
DO
points = points + 1
TextString(1, "SCORE " + Str(points))
Sync()
LOOP
Citirea unui efect înapoi#
Fiecare parametru pe care îl scrii are un cititor, așa cum SpriteColor are
SpriteColorRed. Aceasta este regula dialectului, iar aceste comenzi o
urmează.
IF SpriteEffectsEnabled(hero) = 1
SpriteEffects(hero, 0)
ENDIF
alpha = SpriteShadowAlpha(hero) ' 0 to 255
size = SpriteGlowSize(hero) ' 0 means no glow
Setul complet, per familie: SpriteShadowOffsetX, SpriteShadowOffsetY,
SpriteShadowBlur, SpriteShadowAlpha, SpriteShadowColorRed,
SpriteShadowColorGreen, SpriteShadowColorBlue, SpriteGlowRed,
SpriteGlowGreen, SpriteGlowBlue, SpriteGlowSize,
SpriteOutlineThickness, SpriteOutlineRed, SpriteOutlineGreen,
SpriteOutlineBlue, SpriteBlurRadius, SpriteEffectsEnabled, plus
aceleași șaptesprezece cu un prefix Text.
Două reguli care merită știute:
- Un sprite căruia nu i s-a dat niciodată un efect se raportează drept ON. Aceasta este starea implicită; a răspunde 0 ar spune unui program că efectele lui sunt tăiate înainte să fi setat vreunul.
- Întrebarea despre un sprite care nu există răspunde 0, nu oprește
programul. Așa tratează dialectul întrebările peste tot:
SpriteHitrăspunde 0 când nimic nu este atins, iar programele citesc acel 0 fără să îl testeze.
Cât costă#
Umbra, conturul și glow-ul sunt desenate, deci costă apeluri de desenare: unul pentru umbră (trei când este moale), patru pentru un glow, opt pentru un contur. Un ecran cu două sute de sprite-uri conturate este un ecran care desenează o mie opt sute de patrulatere, iar asta se va vedea. Sunt gândite pentru cele câteva lucruri la care jucătorul chiar trebuie să se uite: personajul, obiectul de cules, unitatea selectată.
Blur-ul nu costă nimic per cadru.
O regulă practică: efecte pe ce se mișcă și contează, niciunul pe fundal.
Ce trece într-un joc exportat#
| Țintă | Umbră, contur, glow | Blur | Efecte de text |
|---|---|---|---|
| Editorul | da | da | da |
| Export HTML5 | da | da | da |
| Nativ și wasm (Bevy) | da | nu | nu |
Umbra, conturul și glow-ul sunt construite din sprite-uri obișnuite, așa că merg peste tot unde merg sprite-urile. Blur-ul și efectele de text au nevoie de o punte pe care țintele Bevy nu o au încă: raportul de conversie al exportului le numește în loc să le arunce tăcut, așa că un build îți spune ce nu a putut duce.
Unde nu sunt efectele#
Nu există un SpriteEffect(id, "glow", ...) care ia numele efectului ca
șir. Fiecare efect este propria lui comandă, astfel încât parametrii lui pot
fi denumiți, documentați și completați pe măsură ce tastezi. Pentru orice
dincolo de aceste patru, alte două comenzi preiau ștafeta:
SpriteBlendModes(id, src, dst) pentru amestecare aditivă sau multiplicativă
(un foc, un laser), și SpriteShader(id, shaderID) pentru a pune pe un
sprite un shader compilat de LoadSpriteShader. Amândouă au fost declarate
în catalog multă vreme fără o implementare, iar apelarea lor ridica o eroare;
acum funcționează.