Glow und 2D-Lichter für alte 2D-Spiele in Unity URP

So bekommt ein altes 2D-Spiel in Unity URP selektiven Glow mit Bloom und HDR, dynamische 2D-Lichter und eine scharfe UI. Am Beispiel des Spiels Dreamica.

Drei Szenen aus Dreamica: ein Geist jagt eine Blase, ein Eichhörnchen mit Kanone und leuchtende Laser im Wald

Ein altes 2D-Spiel in Unity auf der Built-in Render Pipeline wirkt auf einem heutigen Smartphone flach: Es gibt kein Licht, der Glow fehlt oder lässt das ganze Bild samt UI leuchten, und die Sprites der Oberfläche sind unscharf. In Unity URP lösen Sie das mit drei Dingen: Bloom mit Schwelle 1 und HDR-Farbe nur auf Objekten, die leuchten sollen, 2D-Lichtern aus dem 2D Renderer und einer UI mit größeren Sprites, die den sicheren Bereich des Displays beachtet.

Wir zeigen das Vorgehen an unserem Spiel Dreamica (App Store, Google Play). Es ist ein Fall-Abenteuer, in dem Sie mit dem Finger Linien zeichnen und damit eine Blase mit einem schlafenden Kind durch vier Welten lenken. Ursprünglich entstand es in Unity 5.3, die UI lief im Plugin NGUI, den Glow lieferte das Plugin MK Glow und die Grafik war für das iPhone 7 mit seinem Display von 1334 × 750 Pixeln gemacht. Das Gameplay blieb gleich, geändert hat sich nur die Grafik.

Ein altes Spiel öffnet man oft wegen des Stores, nicht wegen der Grafik. Google Play verlangt seit August 2019 64-Bit-Versionen, und ein Spiel mit nur einem 32-Bit-Build fliegt aus dem Store. Genau so erging es Dreamica. Wenn Sie das Projekt ohnehin nach Unity 6 bringen, ist die Grafik ein relativ günstiger Bonus.

Warum der alte Vollbild-Glow nicht reicht

Ältere Glow-Plugins arbeiten als Vollbild-Effekt. Sie lassen alles Helle leuchten, auch die Icon-Buttons im Menü. In Dreamica verschwanden die Icons unter MK Glow in Unity 6 fast. Das offizielle Post Processing Stack v2 ist nur ein Ersatz für die Built-in Pipeline und greift in URP nicht mehr. Wenn Sie einen Glow wollen, den Sie pro Material steuern, und dazu dynamische 2D-Lichter, wechseln Sie zu URP und nutzen Sie das dort eingebaute Bloom.

Schritt 1: Projekt auf URP umstellen

Nach dem Wechsel zu URP stößt ein altes 2D-Spiel fast sicher auf zwei Fallen. Beide zeigen sich so, dass das Bild falsch oder genau wie vorher aussieht, obwohl die Shader in Ordnung sind.

Graues Bild: Camera Stack einrichten

Zeigt die Game-Ansicht im Editor nur eine graue Fläche, während die Scene-Ansicht und ein einmaliger Kamera-Render richtig aussehen, liegt der Fehler nicht in den Shadern, sondern im Zusammensetzen der Kameras. Alte Spiele haben oft zwei Kameras, eine für die Welt und eine für die UI, und die UI-Kamera löscht nur die Tiefe. So werden Kameras in der Built-in Pipeline geschichtet, und URP unterstützt das nicht. Es braucht einen expliziten Camera Stack: Eine Kamera ist die Basis (Base), die anderen sind Überlagerungen (Overlay) in ihrem Stack. Richten Sie ihn in jeder Szene ein, in der Kameras übereinanderliegen.

Sprites ignorieren Licht: auf den 2D Renderer wechseln

Sprites mit beleuchtetem Shader reagieren auf kein Licht, wenn der normale URP-Renderer aktiv ist. Er berechnet 2D-Lichter überhaupt nicht. Wechseln Sie auf den 2D Renderer. Auch dieser zeichnet keine Beleuchtung, solange die Szene kein einziges 2D-Licht enthält. Wenn etwas nach einer Änderung genau wie vorher aussieht, prüfen Sie zuerst, welcher Renderer läuft.

Partikel und alte Plugins

Partikel mit alten Shadern werden im 2D Renderer schwarz oder gar nicht gezeichnet. Geben Sie ihnen einen neuen, einfachen Shader.

Vorsicht auch beim Asset Store. Beim Aktualisieren der Assets kann der Editor von sich aus ein „Update“ eines alten Plugins anwenden, das Metadaten hinzufügt und Shader umbenennt. Materialien finden ihren Shader über den Namen und gehen dadurch unbemerkt kaputt. Sehen Sie sich nach jeder größeren Asset-Aktualisierung in einem alten Projekt alle geänderten Dateien in der Versionsverwaltung an, auch die, an denen Sie gerade nicht arbeiten.

Schritt 2: Selektiver Glow mit HDR-Farbe und Bloom-Schwelle 1

Ziel ist, dass ausgewählte Objekte leuchten, in Dreamica die gezeichnete Linie, Laser, Funken, Glühwürmchen und Runen, aber nicht Hintergründe und UI. Bloom in URP verwischt und addiert alles, was heller ist als seine Schwelle. Setzen Sie die Schwelle auf 1 und heben Sie die Farbe der Materialien, die leuchten sollen, über 1.

Linien-Shader:
    Farbe = Textur × Vertexfarbe × Tönung × Intensität    (Intensität = 6)

Bloom:
    Schwelle = 1, Stärke = 0,3
    es leuchtet nur, was nach der Berechnung heller als 1 ist

Hintergründe und Buttons haben Farben zwischen 0 und 1, kommen nicht über die Schwelle und bleiben scharf. Damit verschwinden auch die ausgebleichten Icons. Voraussetzung ist eine Kamera mit HDR. Ohne HDR wird die Farbe auf 1 begrenzt und nichts leuchtet. Der 2D Renderer zeigt den HDR-Schalter im Kamera-Inspector nicht an, schalten Sie HDR deshalb per Skript ein.

Niedrige Schwelle, ganzes BildVollbild-Glow: Linie, Button-Rand und helle Teile des Hintergrunds leuchten
Schwelle 1, HDR-MaterialSelektiver Glow: Nur die gezeichnete Linie leuchtet, Button und Hintergrund bleiben scharf
Illustration des Prinzips. Links Vollbild-Glow mit niedriger Schwelle, rechts leuchtet nur das Material mit Intensität über 1.

Wenn der Glow so unter Kontrolle ist, muss er nicht optional sein. In Dreamica haben wir den Glow-Schalter aus den Einstellungen entfernt, der Glow gehört zum Look.

Emission Map: nur ein Teil des Sprites leuchtet

Soll nur ein Teil eines Sprites leuchten, geben Sie ihm eine Emission Map: eine zweite, schwarz-weiße Textur, die festlegt, welcher Teil des Sprites leuchtet. Nur dieser Teil bekommt eine Farbe über 1, sodass bei einer Rune der Kern strahlt und nicht der ganze Umriss.

Eine Rune, ihre schwarz-weiße Emission Map und das Ergebnis mit leuchtendem Kern
Sprite der Rune, Emission Map (Weiß leuchtet) und das Ergebnis nach dem Bloom.

Pulsieren in Belichtungsstufen

Soll der Glow pulsieren, multiplizieren Sie die Farbe nicht linear. Ein HDR-Material kann eine Helligkeit um 2 hoch 8 haben, und ein linearer Faktor von 0 bis 10 ist darauf überhaupt nicht zu sehen. Rechnen Sie den Puls in Belichtungsstufen (EV), genau wie der Intensitätsregler in Unitys HDR-Farbwähler:

Farbe = Tönung × 2 ^ ev,   ev schwingt zwischen einem Minimum und einem Maximum

Schritt 3: Dynamisches 2D-Licht um den Spieler

Setzen Sie ein 2D-Punktlicht auf den Spieler und um ihn einen weichen Schein. In Dreamica steuert eine einzige Sinuskurve gleichzeitig die Helligkeit des Scheins, den Radius des Lichts und seine Intensität, sodass alles gemeinsam pulsiert. Die Lichtfarbe kann die Umgebung bestimmen: im Wald weiß, im Sumpf orange, im Winter violett, in der Hölle gelb. Stellen Sie Hintergründe und Hindernisse auf beleuchtete Shader um, sonst reagieren sie nicht auf das Licht.

Kurze Lichter beleben Ereignisse. Wenn das Eichhörnchen mit der Kanone schießt, blitzt ein bernsteinfarbenes Licht mit Intensität 3 auf und erlischt in 0,3 Sekunden, und die Funken aus dem Rohr leuchten. Nicht alles muss leuchten: Der Staub beim Verschwinden der Eichel ist absichtlich braun und leuchtet nicht.

Derselbe Shader auf angrenzenden Flächen

Wenn zwei Hintergründe aneinandergrenzen, die Quellbilder gleich hell sind und trotzdem eine sichtbare Naht bleibt, prüfen Sie die Shader. In Dreamica hatten der Starthintergrund und der endlose Waldhintergrund jeweils einen anderen Shader. Einer ignorierte die Materialtönung von 0,733, der andere wandte sie an und dunkelte den Wald auf 73 % ab. Die Lösung ist derselbe Shader auf beiden Flächen.

Zwei ShaderGrenze zwischen zwei Waldhintergründen, der untere Teil ist deutlich dunkler
Ein ShaderDieselbe Grenze, beide Hintergründe haben dieselbe Helligkeit
Grenze zwischen Starthintergrund und Wald. Links ist der untere Teil durch die Tönung 0,733 abgedunkelt, die der obere Shader ignoriert.

Lichtblitz: zum Höhepunkt überblenden statt addieren

Beim Übergang in die nächste Welt flammen Licht und Schein auf: 0,6 Sekunden Anstieg, kurzes Halten, 1,4 Sekunden Abklingen, und der Schein dreht sich dabei einmal. Wenn Sie den Blitz einfach zum Puls addieren, hängt der Höhepunkt davon ab, wo die Sinuskurve gerade steht, und jeder Blitz ist unterschiedlich stark. Blenden Sie stattdessen fließend vom Puls zu einem festen Höhepunkt über:

Gewicht = Blitzkurve (0 bis 1)
Wert = Puls + (Höhepunkt - Puls) × Gewicht
AddiertIntensitätsdiagramm: Puls und zwei Blitze mit unterschiedlich hohem Höhepunkt
ÜberblendetIntensitätsdiagramm: Puls und zwei Blitze mit gleichem Höhepunkt auf der gepunkteten Linie
Zwei Blitze in unterschiedlicher Phase des Pulses (orange und blau). Links unterscheiden sich die Höhepunkte, rechts erreichen beide denselben Wert.

Schritt 4: Scharfe UI für heutige Displays

Eine UI, die für das iPhone 7 gezeichnet wurde, hat kleine Sprites. Der Menü-Button in Dreamica hatte ein Sprite mit 128 × 64 Pixeln und wurde auf einem Smartphone mit dreifacher Pixeldichte um ein Vielfaches gestreckt. Zeichnen Sie die Sprites in höherer Auflösung neu: Der neue Button hat 800 × 434, die Fortschrittsbalken wuchsen von 200 × 26 auf 800 × 104.

128 × 64Nahaufnahme eines unscharfen Button-Rands
800 × 434Nahaufnahme eines scharfen Button-Rands mit sichtbarer Textur
Derselbe Button in derselben Größe auf dem Display. Links das ursprüngliche Sprite, rechts das neue.

Wenn die UI auf NGUI basiert, ziehen Sie eine Neuentwicklung in Betracht. Die Anker in NGUI werden stückweise über mehrere Frames berechnet, lange Ankerketten pendeln sich manchmal falsch ein, und der Maßstab unterscheidet sich zwischen Editor, Smartphone und iPad. In Dreamica haben Korrekturen das nicht zuverlässig gelöst, deshalb haben wir die UI in uGUI und TextMeshPro neu geschrieben. Gehen Sie Bildschirm für Bildschirm vor und lassen Sie die alte Version daneben als Vorlage laufen. Skalieren Sie die UI nach der Höhe gegenüber einer Referenzauflösung (bei uns 434 × 941 Punkte) und beachten Sie den sicheren Bereich des Displays, also Notch und abgerundete Ecken. Entfernen Sie NGUI danach vollständig aus dem Projekt, damit nicht zwei UI-Systeme übrig bleiben.

Grenzen: unkomprimierte Texturen

Dieses Vorgehen ändert nur die Grafik. In Dreamica haben wir die Texturen bewusst nicht komprimiert, weil wir volle Schärfe wollten. Der Preis ist die Größe: Schon das ursprüngliche Spiel hatte im Editor 313 MB Texturen im Speicher, im App Store hat es 299 MB. Komprimierung würde die Texturen nach unserer Schätzung auf etwa 40 MB verkleinern. Wenn Ihnen die Spielgröße wichtig ist, sparen Sie hier zuerst.

Ein anderes Unity-Spiel von der ersten Codezeile bis zur Veröffentlichung beschreibt der Artikel über Space Impact Watch, um Performance in Unity geht es im Artikel über Horden-Navigation in Unity DOTS. Liegt das Spiel noch in Unity 4 vor, öffnen Sie es nicht direkt in Unity 6; den sicheren Weg beschreibt die Anleitung, wie Sie ein Unity-4-Projekt auf Unity 6 umstellen. Wenn Sie ein altes Unity-Spiel haben, das aus den Stores verschwunden ist, und es zurückbringen möchten, melden Sie sich bei uns.

← Mehr in Dreamica