Sprite- och texteffekter

Skugga, kontur, glöd och oskärpa på 2D-sprites och på text, med vad var och en kostar, vad den gör med en sprite i rörelse, och receptet den ersätter.

Vad det här är till för#

Fram tills nu kunde en 2D-sprite bära en tonfärg och en bild, och inget annat. För att lyfta en karaktär från sin bakgrund var du tvungen att bygga effekten för hand: skapa en andra sprite, tona den svart, förskjut den, lägg den bakom, och håll den i takt med den första på varje enda bildruta. Det fungerar. Det är också fyra extra rader bokföring per karaktär, och den dag du glömmer en av dem släpar skuggan efter sin ägare.

Fyra kommandon gör det åt dig, på sprites och på 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

Inget annat att underhålla. Flytta spriten, skala den, vänd den, spela dess animation: effekterna följer med.

De fyra effekterna#

Skugga#

SpriteShadow(spriteID, offsetX, offsetY, softness, opacity)
SpriteShadowColor(spriteID, red, green, blue)

offsetX och offsetY är i dina egna skärmenheter, räknat på samma sätt som SpritePosition: positiv Y är nedåt. softness sprider skuggan; opacity går från 0 till 255. Färgen är svart om du inte säger annat.

SpriteShadow(hero, 3, 5, 4, 110)
SpriteShadowColor(hero, 20, 0, 40)      ' a cold shadow, not a grey one

En skugga med opacitet 0 är ingen skugga. Så stänger du av den.

Kontur#

SpriteOutline(spriteID, thickness, red, green, blue)

Konturen följer spritens siluett, inte rektangeln den lever i: en transparent PNG får en kontur runt ritningen, inte runt filen. Tjocklek är i skärmenheter, och 1 till 3 är det användbara intervallet. Över det börjar de åtta riktningarna konturen byggs från synas som åtta lober.

SpriteOutline(hero, 2, 255, 255, 255)   ' the readable-on-anything outline
SpriteOutline(hero, 0, 0, 0, 0)         ' off

Glöd#

SpriteGlow(spriteID, red, green, blue, size)

En färgad gloria, som sprider sig size enheter förbi siluetten. Den ligger bakom spriten och framför skuggan, vilket är vad som låter dig använda båda.

SpriteGlow(pickup, 255, 210, 0, 10)     ' this one is worth walking to

Oskärpa#

SpriteBlur(spriteID, radius)

Oskärpa är den udda av de fyra. De andra tre ritas runt spriten; oskärpa ersätter spritens egen bild med en suddig kopia av den. Det betyder två saker värda att veta:

  • den kostar ingenting per bildruta (den suddiga bilden beräknas en gång), och
  • oskärpan klipps vid bildens kant. En ritning som rör vid kanten på sin PNG förlorar den del av sin gloria som skulle hamna utanför. Lämna några transparenta pixlar marginal i filen om du planerar att sudda den.

SpriteColor fortsätter fungera på en suddig sprite.

Att stänga av allt, och sätta på igen#

SpriteEffects(spriteID, 0)   ' all four off
SpriteEffects(spriteID, 1)   ' all four back, exactly as they were

SpriteEffects raderar inte dina inställningar. Det är en omkopplare, inte en återställning, så att en karaktär kan blinka utan att du behöver deklarera om dess skugga efteråt.

Samma fyra på 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)

Signaturerna matchar sprite-versionerna exakt, så det finns inget nytt att lära sig. Två skillnader i beteende:

  • En glöd vinner över en skuggas mjukhet. Båda ritas med samma mekanism, och en glöd utan mjukhet är osynlig medan en skugga utan mjukhet fortfarande är en skugga. När du ber om båda får glöden mjukheten.
  • Dessa gäller vektortext, inte bitmapfont-text. En text ritad med TextFontImage är en komposition av sprites; kommandona ignoreras tyst på den. Använd sprite-effekterna på sprites i det fallet.

Ett genomarbetat exempel: läsbart på vad som helst#

Problemet dessa kommandon faktiskt löser är kontrast du inte styr. Ett vitt poäng är osynligt i snö; ett svart är osynligt på natten. En kontur löser det en gång, för varje bakgrund du någonsin kommer att rita.

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

Att läsa tillbaka en effekt#

Varje parameter du skriver har en läsare, på samma sätt som SpriteColor har SpriteColorRed. Det är dialektens regel, och de här kommandona följer den.

IF SpriteEffectsEnabled(hero) = 1
  SpriteEffects(hero, 0)
ENDIF

alpha = SpriteShadowAlpha(hero)      ' 0 to 255
size  = SpriteGlowSize(hero)         ' 0 means no glow

Hela uppsättningen, per familj: SpriteShadowOffsetX, SpriteShadowOffsetY, SpriteShadowBlur, SpriteShadowAlpha, SpriteShadowColorRed, SpriteShadowColorGreen, SpriteShadowColorBlue, SpriteGlowRed, SpriteGlowGreen, SpriteGlowBlue, SpriteGlowSize, SpriteOutlineThickness, SpriteOutlineRed, SpriteOutlineGreen, SpriteOutlineBlue, SpriteBlurRadius, SpriteEffectsEnabled, plus samma sjutton med ett Text-prefix.

Två regler värda att känna till:

  • En sprite som aldrig fått en effekt rapporterar sig själv som PÅ. Det är standardtillståndet; att svara 0 skulle tala om för ett program att dess effekter är avstängda innan det har ställt in några.
  • Att fråga om en sprite som inte finns svarar 0, det stoppar inte programmet. Så behandlar dialekten frågor överallt: SpriteHit svarar 0 när inget rörs vid, och program läser den 0:an utan att testa den.

Vad det kostar#

Skugga, kontur och glöd är ritade, så de kostar draw calls: en för skuggan (tre när den är mjuk), fyra för en glöd, åtta för en kontur. En skärm med tvåhundra konturerade sprites är en skärm som ritar arton hundra quad, och det kommer synas. De är avsedda för den handfull saker spelaren är avsedd att titta på: karaktären, föremålet, den markerade enheten.

Oskärpa kostar ingenting per bildruta.

En praktisk regel: effekter på det som rör sig och spelar roll, inga på bakgrunden.

Vad som förs över till ett exporterat spel#

MålSkugga, kontur, glödOskärpaTexteffekter
Editornjajaja
HTML5-exportjajaja
Native och wasm (Bevy)janejnej

Skugga, kontur och glöd är byggda av vanliga sprites, så de reser överallt sprites gör. Oskärpa och texteffekterna behöver en bro Bevy-målen inte har än: exportens konverteringsrapport namnger dem i stället för att tyst släppa dem, så att ett bygge berättar vad det inte kunde ta med.

Var effekterna inte finns#

Det finns ingen SpriteEffect(id, "glow", ...) som tar effektnamnet som en sträng. Varje effekt är sitt eget kommando så att dess parametrar kan namnges, dokumenteras och kompletteras medan du skriver. Bortom dessa fyra tar två andra kommandon över: SpriteBlendModes(id, src, dst) för additiv eller multiplicerad blandning (en eld, en laser), och SpriteShader(id, shaderID) för att sätta en shader kompilerad av LoadSpriteShader på en sprite. Båda deklarerades i katalogen under lång tid utan en implementation, och att anropa dem gav ett fel; nu fungerar de. </content>