Sprite- und Texteffekte
Schatten, Outline, Glow und Blur auf 2D-Sprites und auf Text, mit dem, was jedes kostet, was es mit einem sich bewegenden Sprite macht, und dem handgestrickten Rezept, das es ersetzt.
Wofür das gut ist#
Bis jetzt konnte ein 2D-Sprite eine Tönung und ein Bild tragen, und sonst nichts. Um eine Figur von ihrem Hintergrund abzuheben, mussten Sie den Effekt von Hand bauen: ein zweites Sprite erzeugen, es schwarz tönen, versetzen, dahinter platzieren, und es in jedem einzelnen Frame im Gleichschritt mit dem ersten halten. Es funktioniert. Es sind aber auch vier zusätzliche Zeilen Buchhaltung pro Figur, und an dem Tag, an dem Sie eine davon vergessen, hinkt der Schatten seinem Besitzer hinterher.
Vier Befehle erledigen das für Sie, auf Sprites und auf 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
Nichts sonst zu pflegen. Bewegen Sie das Sprite, skalieren Sie es, spiegeln Sie es, spielen Sie seine Animation ab: Die Effekte folgen.
Die vier Effekte#
Shadow#
SpriteShadow(spriteID, offsetX, offsetY, softness, opacity)
SpriteShadowColor(spriteID, red, green, blue)
offsetX und offsetY liegen in Ihren eigenen Bildschirmeinheiten vor,
gezählt wie bei SpritePosition: positives Y ist unten. softness
weitet den Schatten; opacity läuft von 0 bis 255. Die Farbe ist Schwarz,
sofern Sie nichts anderes angeben.
SpriteShadow(hero, 3, 5, 4, 110)
SpriteShadowColor(hero, 20, 0, 40) ' a cold shadow, not a grey one
Ein Schatten mit Opacity 0 ist kein Schatten. So schalten Sie ihn aus.
Outline#
SpriteOutline(spriteID, thickness, red, green, blue)
Die Outline folgt der Silhouette des Sprites, nicht dem Rechteck, in dem es lebt: Ein transparentes PNG bekommt eine Outline um die Zeichnung, nicht um die Datei. Thickness liegt in Bildschirmeinheiten vor, und 1 bis 3 ist der nützliche Bereich. Darüber beginnen die acht Richtungen, aus denen die Outline aufgebaut ist, als acht Lappen sichtbar zu werden.
SpriteOutline(hero, 2, 255, 255, 255) ' the readable-on-anything outline
SpriteOutline(hero, 0, 0, 0, 0) ' off
Glow#
SpriteGlow(spriteID, red, green, blue, size)
Ein farbiger Heiligenschein, der sich size Einheiten über die Silhouette
hinaus ausbreitet. Er sitzt hinter dem Sprite und vor dem Schatten, das ist
es, was die Nutzung beider zusammen erlaubt.
SpriteGlow(pickup, 255, 210, 0, 10) ' this one is worth walking to
Blur#
SpriteBlur(spriteID, radius)
Blur ist der Sonderfall. Die anderen drei werden um das Sprite herum gezeichnet; Blur ersetzt das eigene Bild des Sprites durch eine unscharfe Kopie davon. Das bedeutet zwei Dinge, die man wissen sollte:
- es kostet nichts pro Frame (das unscharfe Bild wird einmal berechnet), und
- der Blur wird am Bildrand abgeschnitten. Eine Zeichnung, die den Rand ihres PNGs berührt, verliert den Teil ihres Heiligenscheins, der außerhalb fallen würde. Lassen Sie ein paar transparente Pixel Rand in der Datei, wenn Sie planen, sie zu weichzeichnen.
SpriteColor funktioniert weiterhin auf einem geweichzeichneten Sprite.
Alles aus- und wieder einschalten#
SpriteEffects(spriteID, 0) ' all four off
SpriteEffects(spriteID, 1) ' all four back, exactly as they were
SpriteEffects löscht Ihre Einstellungen nicht. Es ist ein Schalter, kein
Reset, sodass eine Figur aufblitzen kann, ohne dass Sie danach ihren Schatten
neu deklarieren müssen.
Dieselben vier auf 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)
Die Signaturen entsprechen genau denen der Sprites, es gibt also nichts Neues zu lernen. Zwei Unterschiede im Verhalten:
- Ein Glow gewinnt gegen die Softness eines Schattens. Beide werden vom selben Mechanismus gezeichnet, und ein Glow ohne Softness ist unsichtbar, während ein Schatten ohne Softness immer noch ein Schatten ist. Fordern Sie beide an, bekommt der Glow die Softness.
- Das gilt für Vektortext, nicht für Bitmap-Font-Text. Ein mit
TextFontImagegezeichneter Text ist eine Komposition aus Sprites; die Befehle werden dabei stillschweigend ignoriert. Verwenden Sie in diesem Fall die Sprite-Effekte auf den Sprites.
Ein durchgerechnetes Beispiel: lesbar auf allem#
Das Problem, das diese Befehle tatsächlich lösen, ist Kontrast, den Sie nicht kontrollieren. Ein weißer Punktestand ist auf Schnee unsichtbar; ein schwarzer ist nachts unsichtbar. Eine Outline löst das ein für alle Mal, für jeden Hintergrund, den Sie je zeichnen werden.
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
Einen Effekt zurücklesen#
Jeder Parameter, den Sie schreiben, hat einen Leser, so wie SpriteColor
SpriteColorRed hat. Das ist die Regel des Dialekts, und diese Befehle
folgen ihr.
IF SpriteEffectsEnabled(hero) = 1
SpriteEffects(hero, 0)
ENDIF
alpha = SpriteShadowAlpha(hero) ' 0 to 255
size = SpriteGlowSize(hero) ' 0 means no glow
Das vollständige Set, pro Familie: SpriteShadowOffsetX,
SpriteShadowOffsetY, SpriteShadowBlur, SpriteShadowAlpha,
SpriteShadowColorRed, SpriteShadowColorGreen, SpriteShadowColorBlue,
SpriteGlowRed, SpriteGlowGreen, SpriteGlowBlue, SpriteGlowSize,
SpriteOutlineThickness, SpriteOutlineRed, SpriteOutlineGreen,
SpriteOutlineBlue, SpriteBlurRadius, SpriteEffectsEnabled, dazu
dieselben siebzehn mit einem Text-Präfix.
Zwei Regeln, die man kennen sollte:
- Ein Sprite, dem nie ein Effekt gegeben wurde, meldet sich selbst als ON. Das ist der Standardzustand; 0 zu antworten, würde einem Programm sagen, seine Effekte seien gekappt, bevor es überhaupt welche gesetzt hat.
- Die Frage nach einem nicht existierenden Sprite beantwortet 0, sie
stoppt das Programm nicht. So behandelt der Dialekt Fragen überall:
SpriteHitantwortet 0, wenn nichts berührt wird, und Programme lesen diese 0, ohne sie vorher zu prüfen.
Was es kostet#
Shadow, Outline und Glow werden gezeichnet, sie kosten also Draw Calls: einen für den Schatten (drei, wenn er weich ist), vier für einen Glow, acht für eine Outline. Ein Bildschirm mit zweihundert umrandeten Sprites ist ein Bildschirm, der achtzehnhundert Quads zeichnet, und man wird es merken. Sie sind für die Handvoll Dinge gedacht, die der Spieler ansehen soll: die Figur, das Pickup, die ausgewählte Einheit.
Blur kostet nichts pro Frame.
Eine praktische Regel: Effekte auf das, was sich bewegt und zählt, keine auf den Hintergrund.
Was in ein exportiertes Spiel übernommen wird#
| Ziel | Shadow, Outline, Glow | Blur | Texteffekte |
|---|---|---|---|
| Der Editor | ja | ja | ja |
| HTML5-Export | ja | ja | ja |
| Native und Wasm (Bevy) | ja | nein | nein |
Shadow, Outline und Glow sind aus gewöhnlichen Sprites gebaut, sie reisen also überallhin, wohin Sprites reisen. Blur und die Texteffekte brauchen eine Brücke, die die Bevy-Ziele noch nicht haben: Der Konvertierungsbericht des Exports nennt sie, statt sie stillschweigend fallen zu lassen, sodass ein Build Ihnen sagt, was er nicht mittragen konnte.
Wo die Effekte nicht sind#
Es gibt kein SpriteEffect(id, "glow", ...), das den Effektnamen als String
entgegennimmt. Jeder Effekt ist sein eigener Befehl, damit seine Parameter
benannt, dokumentiert und beim Tippen vervollständigt werden können. Für
alles über diese vier hinaus übernehmen zwei andere Befehle:
SpriteBlendModes(id, src, dst) für additives oder multiplikatives Blending
(ein Feuer, ein Laser), und SpriteShader(id, shaderID), um einen von
LoadSpriteShader kompilierten Shader auf ein Sprite zu setzen. Beide waren
lange im Katalog deklariert, ohne eine Implementierung, und ihr Aufruf löste
einen Fehler aus; jetzt funktionieren sie.