Sprite- en teksteffecten

Schaduw, contour, gloed en vervaging op 2D-sprites en op tekst, met wat elk kost, wat het doet met een bewegende sprite, en het handgebouwde recept dat het vervangt.

Waar dit voor dient#

Tot nu toe kon een 2D-sprite een tint en een afbeelding dragen, en verder niets. Om een personage van zijn achtergrond te laten loskomen, moest u het effect met de hand bouwen: een tweede sprite maken, hem zwart tinten, hem verschuiven, hem erachter zetten, en hem elke afzonderlijke frame in de pas houden met de eerste. Het werkt. Het is ook vier extra regels boekhouding per personage, en de dag dat u er één vergeet, sleept de schaduw achter zijn eigenaar aan.

Vier commando's doen dat voor u, op sprites en op tekst:

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

Verder niets te onderhouden. Verplaats de sprite, schaal hem, spiegel hem, speel zijn animatie af: de effecten volgen mee.

De vier effecten#

Schaduw#

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

offsetX en offsetY staan in uw eigen schermeenheden, geteld op dezelfde manier als SpritePosition: positieve Y is omlaag. softness verspreidt de schaduw; opacity loopt van 0 tot 255. De kleur is zwart tenzij u anders zegt.

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

Een schaduw met dekking 0 is geen schaduw. Zo schakelt u hem uit.

Contour#

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

De contour volgt het silhouet van de sprite, niet de rechthoek waarin hij leeft: een transparante PNG krijgt een contour rond de tekening, niet rond het bestand. Dikte staat in schermeenheden, en 1 tot 3 is het bruikbare bereik. Erboven beginnen de acht richtingen waaruit de contour is opgebouwd zich te tonen als acht kwabben.

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

Gloed#

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

Een gekleurde halo, die zich size eenheden voorbij het silhouet verspreidt. Ze zit achter de sprite en voor de schaduw, wat het mogelijk maakt allebei te gebruiken.

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

Vervaging#

SpriteBlur(spriteID, radius)

Vervaging is de uitzondering. De andere drie worden rond de sprite getekend; vervaging vervangt de eigen afbeelding van de sprite door een vervaagde kopie ervan. Dat betekent twee dingen die het weten waard zijn:

  • ze kost niets per frame (de vervaagde afbeelding wordt eenmalig berekend), en
  • de vervaging wordt afgesneden aan de rand van de afbeelding. Een tekening die de rand van zijn PNG raakt, verliest het deel van zijn halo dat buiten zou vallen. Laat een paar transparante pixels marge in het bestand als u van plan bent het te vervagen.

SpriteColor blijft werken op een vervaagde sprite.

Alles uit- en weer aanzetten#

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

SpriteEffects wist uw instellingen niet. Het is een schakelaar, geen reset, zodat een personage kan opflitsen zonder dat u zijn schaduw achteraf opnieuw hoeft te declareren.

Dezelfde vier op tekst#

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)

De signaturen komen exact overeen met die van de sprites, dus er is niets nieuws te leren. Twee verschillen in gedrag:

  • Een gloed wint van de zachtheid van een schaduw. Beide worden door hetzelfde mechanisme getekend, en een gloed zonder zachtheid is onzichtbaar, terwijl een schaduw zonder zachtheid nog altijd een schaduw is. Als u om allebei vraagt, krijgt de gloed de zachtheid.
  • Deze gelden voor vectortekst, niet voor bitmap-lettertype-tekst. Tekst getekend met TextFontImage is een compositie van sprites; de commando's worden er stilzwijgend op genegeerd. Gebruik in dat geval de spriteeffecten op de sprites.

Een uitgewerkt voorbeeld: leesbaar op alles#

Het probleem dat deze commando's echt oplossen, is contrast dat u niet controleert. Een witte score is onzichtbaar op sneeuw; een zwarte is onzichtbaar in de nacht. Een contour lost het eenmalig op, voor elke achtergrond die u ooit zult tekenen.

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

Een effect teruglezen#

Elke parameter die u schrijft, heeft een lezer, zoals SpriteColor SpriteColorRed heeft. Dat is de regel van het dialect, en deze commando's volgen ze.

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

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

De volledige set, per familie: SpriteShadowOffsetX, SpriteShadowOffsetY, SpriteShadowBlur, SpriteShadowAlpha, SpriteShadowColorRed, SpriteShadowColorGreen, SpriteShadowColorBlue, SpriteGlowRed, SpriteGlowGreen, SpriteGlowBlue, SpriteGlowSize, SpriteOutlineThickness, SpriteOutlineRed, SpriteOutlineGreen, SpriteOutlineBlue, SpriteBlurRadius, SpriteEffectsEnabled, plus dezelfde zeventien met een Text-voorvoegsel.

Twee regels die het weten waard zijn:

  • Een sprite die nooit een effect heeft gekregen, meldt zichzelf als AAN. Dat is de standaardstatus; 0 antwoorden zou een programma vertellen dat zijn effecten uitgeschakeld zijn voordat het er ook maar één heeft ingesteld.
  • Vragen naar een sprite die niet bestaat, antwoordt 0, het stopt het programma niet. Zo behandelt het dialect vragen overal: SpriteHit antwoordt 0 wanneer niets wordt geraakt, en programma's lezen die 0 zonder ze te testen.

Wat het kost#

Schaduw, contour en gloed worden getekend, dus ze kosten draw calls: één voor de schaduw (drie wanneer ze zacht is), vier voor een gloed, acht voor een contour. Een scherm met tweehonderd omlijnde sprites is een scherm dat achttienhonderd quads tekent, en dat zal te zien zijn. Ze zijn bedoeld voor het handjevol dingen waar de speler naar zou moeten kijken: het personage, het oppakbare voorwerp, de geselecteerde eenheid.

Vervaging kost niets per frame.

Een praktische regel: effecten op wat beweegt en ertoe doet, geen op de achtergrond.

Wat een geëxporteerde game overleeft#

DoelSchaduw, contour, gloedVervagingTeksteffecten
De editorjajaja
HTML5-exportjajaja
Native en wasm (Bevy)janeenee

Schaduw, contour en gloed zijn opgebouwd uit gewone sprites, dus ze reizen overal waar sprites reizen. Vervaging en de teksteffecten hebben een brug nodig die de Bevy-doelen nog niet hebben: het exportconversierapport noemt ze bij naam in plaats van ze stilzwijgend te laten vallen, zodat een build u vertelt wat het niet kon meedragen.

Waar de effecten niet zijn#

Er is geen SpriteEffect(id, "glow", ...) die de effectnaam als string aanneemt. Elk effect is zijn eigen commando, zodat de parameters ervan benoemd, gedocumenteerd en aangevuld kunnen worden terwijl u typt. Voor alles voorbij deze vier nemen twee andere commando's het over: SpriteBlendModes(id, src, dst) voor additieve of vermenigvuldigde blending (een vuur, een laser), en SpriteShader(id, shaderID) om een shader gecompileerd door LoadSpriteShader op een sprite te plaatsen. Beide werden lange tijd in de catalogus vermeld zonder implementatie, en ze aanroepen gaf een fout; nu werken ze.