Hallo allerseits! Seit 4 Jahren habe ich nicht mehr in Habr geschrieben. In meiner letzten
Reihe von Beiträgen ging es um verschiedene Tools und Tricks, die wir in unserem letzten Spiel verwendet haben (Entwicklung auf Unity). Seitdem haben wir das Spiel erfolgreich veröffentlicht und auch ein neues veröffentlicht. Jetzt können Sie ein bisschen ausatmen und einige neue Artikel schreiben, die für jemanden nützlich sein können.
Heute möchte ich über die grafischen Tricks und Tricks sprechen, mit denen wir das Bild erstellt haben, das Sie oben im GIF sehen.
Wir reagieren sehr sensibel auf die Grafik unserer Spiele und haben daher viel Zeit und Mühe in verschiedene Effekte und andere Extras investiert, die unsere Pixelkunst so attraktiv wie möglich machen würden. Vielleicht findet jemand etwas Nützliches für sich.
Zunächst werde ich kurz auflisten, wie das Bild in unserem Spiel aussehen wird:
- Variables Umgebungslicht - eine banale Änderung der Beleuchtung je nach Tageszeit.
- LUT-Farbkorrektur - ist dafür verantwortlich, den Ton des Bildes abhängig von der Tageszeit (oder der Art der Zone) zu ändern.
- Dynamische Lichtquellen - Taschenlampen, Öfen, Lampen.
- Normale Karten - sind dafür verantwortlich, Objekten Volumen zu verleihen, insbesondere beim Bewegen von Lichtquellen.
- Mathematik der 3D-Lichtverteilung - ist dafür verantwortlich, dass die Lichtquelle in der Mitte des Bildschirms ein höheres Objekt korrekt beleuchtet, ein niedrigeres Objekt jedoch nicht (d. H. Mit der nicht beleuchteten Seite in Richtung Kamera gedreht).
- Schatten - von Sprites erzeugt, drehen und reagieren auf die Position der Lichtquellen.
- Simulation der Höhe von Objekten - für die korrekte Anzeige von Nebel.
- Andere Dekorateure: Regen, Wind, Animationen (einschließlich Shader-Animationen von Laub und Gras) usw.
Jetzt genauer.
Variables Umgebungslicht
Hier im Prinzip nichts Besonderes. Nachts - dunkler, tagsüber - heller. Die Farbe des Lichts wird durch den Zeitgradienten festgelegt. Bei Nacht wird die Lichtquelle nicht nur dunkler, sondern erhält einen blauen Farbton.
Es sieht so aus:
LUT-Farbkorrektur
LUT (Nachschlagetabelle) - Farbtausch-Tabellen. Grob gesagt handelt es sich um ein dreidimensionales RGB-Array, bei dem an jedem Knoten ein Farbwert vorhanden ist, der durch den entsprechenden ersetzt werden sollte. Wenn sich also ein roter Punkt an den Koordinaten (1, 1, 1) befindet, bedeutet dies, dass alle weißen Farben im Bild durch rote ersetzt werden. Wenn die Koordinaten (1, 1, 1) weiß sind (R = 1, G = 1, B = 1), gibt es keine Änderung. Dementsprechend hat die LUT ohne Änderungen eine Farbe für jede Koordinate, die denselben Koordinaten entspricht. Das heißt, am Punkt (0,4, 0,5, 0,8) ist die Farbe (R = 0,4, G = 0,5, B = 0,8).
Nun, es ist erwähnenswert, dass sie der Einfachheit halber eine 3D-Textur als zweidimensional darstellen. So sieht beispielsweise die „Standard“ -LUT aus (ohne die Farbwiedergabe zu ändern):

Es ist elementar implementiert, es funktioniert schnell und bequem.
Es ist auch sehr einfach einzurichten - Sie geben dem Künstler ein Bild aus dem Spiel und sagen "Farbton, damit es sich wie Abend anfühlt". Wenden Sie danach alle Ebenen der Farbkorrektur auf die Standard-LUT an und erhalten Sie die Abend-LUT.
In unserem Fall blieb der Künstler ein wenig hängen und erstellte bis zu 10 verschiedene LUTs für verschiedene Tageszeiten (Nacht, Dämmerung, Abend usw.). So sieht ihr Setup aus:
Je nach Tageszeit sieht derselbe Ort daher unterschiedlich aus:

Hier ändert sich auch die Transparenz der Lichtsprites aus den Fenstern je nach Tageszeit.
Dynamische Lichtquellen und normale Karten
Lichtquellen werden von Unity als absolut gewöhnlich verwendet. Darüber hinaus werden für jedes Sprite normale Karten gezeichnet, sodass Sie ein Gefühl für die Lautstärke bekommen.
Solche Normalen werden ganz einfach gezeichnet. Der Künstler malt grob ein Licht mit einem Pinsel von 4 Seiten:
Und dann geht dieses Skript zur normalen Karte:
Wenn Sie nach einem Shader (und einer Software) suchen, der dies tut, können Sie in Richtung Sprite Lamp schauen.
3D-Lichtsimulation
Das ist etwas komplizierter. Sie können Sprites nicht einfach aufnehmen und hervorheben. Wir müssen überlegen, ob sich das Sprite „hinter“ der Lichtquelle oder „vor“ befindet.
Achten Sie auf dieses Bild:
Beide Bäume befinden sich im gleichen Abstand von der Lichtquelle, der entfernte Baum wird jedoch beleuchtet und der nächste Baum nicht (da sein unbeleuchteter Teil in Richtung Kamera gedreht ist).
Ich habe dieses Problem ganz einfach gelöst. Der Shader berechnet den Abstand entlang der vertikalen y-Achse zwischen der Lichtquelle und dem Sprite. Und wenn es positiv ist (die Lichtquelle vor dem Sprite), dann beleuchten wir das Sprite wie gewohnt, aber wenn es negativ ist (das Sprite blockiert die Lichtquelle), nimmt die Lichtintensität aus einer Entfernung mit einem sehr großen Koeffizienten sehr stark ab. Es ist genau der Koeffizient, der erstellt wurde, und nicht nur „nicht beleuchtet“. Wenn sich die Lichtquelle bewegt und plötzlich hinter dem Sprite erscheint, wird das Sprite nicht sofort schwarz, sondern allmählich. Aber immer noch ziemlich schnell.
Schatten
Schatten entstehen durch Sprites, die sich um einen Punkt drehen. Ich habe versucht, ihnen mehr Komprimierung (Skew) hinzuzufügen, aber es stellte sich als unnötig heraus.
Insgesamt kann jedes Objekt maximal 4 Schatten haben. Einer stammt von der Sonne und drei von dynamischen Lichtquellen. Das Bild unten zeigt das Prinzip:

Die Aufgabe „Finde die nächsten 3 Lichtquellen und berechne den Abstand / Winkel der Schatten zu ihnen“ wird durch ein Skript gelöst, das sich in Update dreht. Ja, es funktioniert nicht sehr schnell, weil du musst viel rechnen. Wenn ich jetzt schreiben würde, würde ich die neuen Systeme paralleler Jobs in Unity verwenden. Aber das war noch nicht so, also habe ich nur gewöhnliche Skripte so weit wie möglich optimiert.
Das einzige, was zählt, ist, dass ich die Sprite-Rotation nicht transformiert habe, sondern innerhalb des Vertex-Shaders. Das heißt, Drehung bewegt sich nicht. Es ist nur so, dass im Sprite ein Parameter festgelegt ist (ich habe die Farbe dafür verwendet, da alle Schatten trotzdem schwarz sind) und der Shader bereits für die Drehung des Sprites verantwortlich ist. Das geht schneller, weil Sie müssen die Geometrie in Unity nicht ziehen.
Ein weiteres Minus dieses Ansatzes ist, dass die Schatten für jedes Objekt einzeln eingerichtet (und manchmal gemalt) werden müssen. Es stimmt, wir haben es wahrscheinlich mit einem Dutzend verschiedener mehr oder weniger universeller Sprites (dünn, dick, oval usw.) geschafft.
Der zweite Nachteil ist, dass es manchmal schwierig ist, einen Schatten für ein Objekt zu erzeugen, dessen Kontaktpunkt mit der Erde sehr langgestreckt ist. Schauen Sie sich zum Beispiel den Schatten vom Zaun an:
Nicht perfekt. So sieht es aus, wenn Sie das Sprite des Zauns selbst durchscheinend machen:
Hier ist jedoch zu beachten, dass das Sprite vertikal immer noch stark deformiert ist (das ursprüngliche Schattensprite sieht fast wie ein Kreis aus). Deshalb sieht seine Wendung nicht so sehr als Wendung aus, sondern als Verzerrung.
Nebel- und Höhensimulationen
Es gibt Nebel im Spiel. Es sieht so aus (oben ist die normale Version, unten ist ein extremer 100% Nebel, um den Effekt zu demonstrieren).

Wie Sie sehen können, ragen die Spitzen von Häusern und Bäumen aus dem Nebel heraus. Tatsächlich war es ziemlich einfach, diesen Effekt zu erzielen. Der Nebel besteht aus vielen horizontalen Wolken, die über die Tiefe der Bühne verteilt sind. Infolgedessen stellt sich heraus, dass der obere Teil aller Sprites durch weniger Nebel-Sprites blockiert ist:

Der Wind
Pixel Art Wind ist eine andere Geschichte. Es gibt nicht viele Möglichkeiten. Entweder mit den Händen animieren (was mit unserer Menge an Kunst fast unmöglich ist) oder einen deformierenden Shader schreiben, aber dann muss man manchmal hässliche Verzerrungen ertragen. Sie können natürlich überhaupt nicht animieren, aber dann sieht das Bild unbelebt aus.
Wir haben die Verzerrungsoption mit dem Shader gewählt. Es sieht so aus:
Wenn Sie diesen Shader auf eine karierte Textur anwenden, wird klar, was passiert:
Es ist auch erwähnenswert, dass wir nicht die gesamte Krone animieren, sondern nur einzelne Blätter:
Wir schütteln auch Weizen im Wind, aber alles ist einfach - der Vertex-Shader verformt die x-Koordinaten unter Berücksichtigung der y-Komponente. Je höher der Punkt, desto stärker die Staffelung. Dies geschieht so, dass nur die oberen taumeln, die Wurzel jedoch nicht. Plus - Die Wobble-Phase ändert sich von den x / y-Koordinaten, so dass verschiedene Sprites auf dem Bildschirm zufällig schwingen.
Der gleiche Shader wird auch verwendet, um den Effekt zu erzeugen, dass Weizen und Gras geschaukelt werden, wenn ein Spieler durch sie hindurchgeht.
Das ist wahrscheinlich alles für jetzt. Ich habe mich absichtlich nicht mit dem Thema der Konstruktion der Szene und ihrer Geometrie befasst, weil Dies ist Material für einen separaten Artikel. Im Übrigen sprach er über die wichtigsten Lösungen, die in der Entwicklung verwendet wurden.
PS: Wenn sich jemand für einige technische Aspekte interessiert, schreibe in die Kommentare. Vielleicht werde ich in einem separaten Artikel erzählen. Es sei denn natürlich notwendig.
PPS: Ich nutze diese Gelegenheit, um zu sagen, dass wir jetzt mehrere kompetente Leute im Team finden wollen (Programmierer, PM, KM, Künstler). Details finden Sie auf der Studio-Website. Ich hoffe, dieser Satz hat nicht gegen die Regeln verstoßen.