Birthday Sale! Up to 40% off unlimited courses & creative assets Birthday Sale! Save up to 40%!
Advertisement
  1. Game Development
  2. Programming
Gamedevelopment

Codieren eines benutzerdefinierten Sequenzgenerators zum Rendern einer Sternenlandschaft

by
Difficulty:IntermediateLength:LongLanguages:

German (Deutsch) translation by Valentina (you can also view the original English article)

In meinem vorherigen Artikel habe ich den Unterschied zwischen einem Pseudozufallszahlengenerator und einem Sequenzgenerator erläutert und die Vorteile eines Sequenzgenerators gegenüber einem PRNG untersucht. In diesem Tutorial werden wir einen ziemlich einfachen Sequenzgenerator codieren. Es generiert eine Folge von Zahlen, manipuliert und interpretiert diese Sequenz und zeichnet damit eine sehr einfache Sternenlandschaft.

Hinweis: Obwohl dieses Tutorial mit Java geschrieben wurde, sollten Sie in fast jeder Spieleentwicklungsumgebung dieselben Techniken und Konzepte verwenden können.


Erstellen und Initialisieren des Images

Als erstes müssen wir das Bild erstellen. Für diesen Sequenzgenerator erstellen wir ein 1000 × 1000px-Bild, um die Zahlengenerierung so einfach wie möglich zu halten. Verschiedene Sprachen tun dies unterschiedlich. Verwenden Sie daher den erforderlichen Code für Ihre Entwicklungsplattform.

Wenn Sie das Bild erfolgreich erstellt haben, ist es Zeit, ihm eine Hintergrundfarbe zu geben. Da es sich um einen Sternenhimmel handelt, ist es sinnvoller, mit einem schwarzen Hintergrund (#000000) zu beginnen und dann die weißen Sterne hinzuzufügen, anstatt umgekehrt.


Erstellen eines Sternprofils und eines Sternfelds

Bevor wir mit der Arbeit am Sequenzgenerator beginnen, sollten Sie herausfinden, wohin Sie damit wollen. Dies bedeutet zu wissen, was Sie erstellen möchten und wie unterschiedliche Samen und Zahlen variieren, was Sie erstellen möchten - in diesem Fall die Sterne.

Dazu müssen wir ein Beispielsternprofil erstellen, das Klassenvariablen enthält, die einige der Eigenschaften der Sterne angeben. Um die Dinge einfach zu halten, beginnen wir mit nur drei Attributen:

  • x-Koordinate
  • y-Koordinate
  • Größe

Jedes der drei Attribute hat Werte zwischen 0 und 999, was bedeutet, dass jedem Attribut drei Ziffern zugewiesen sind. All dies wird in einer Star-Klasse gespeichert.

Zwei wichtige Methoden in der Star-Klasse sind getSize() und getRadiusPx(). Die Methode getSize() gibt die Größe des Sterns zurück, die auf eine Dezimalzahl zwischen Null und Eins verkleinert ist, und die Methode getRadiusPx() gibt zurück, wie groß der Radius des Sterns im endgültigen Bild sein soll.

Ich habe festgestellt, dass 4 Pixel einen guten maximalen Radius in meiner Demo ergeben, daher gibt getRadiusPx() einfach den Wert von getSize() multipliziert mit vier zurück. Wenn die Methode getSize() beispielsweise einen Radius von 0,4 zurückgibt, gibt die Methode getRadiusPx() einen Radius von 1,6 Pixel an.

Wir sollten auch eine sehr einfache Klasse bilden, deren Aufgabe es ist, alle Sterne in jeder Folge von Sternen im Auge zu behalten. Die Starfield-Klasse besteht nur aus Methoden, mit denen Sterne zu einer ArrayList hinzugefügt, entfernt oder abgerufen werden. Es sollte auch in der Lage sein, die ArrayList zurückzugeben.


Planen des Sequenzgenerators

Nachdem wir das Sternprofil fertiggestellt und das Bild initialisiert haben, kennen wir einige wichtige Punkte über den Sequenzgenerator, den wir erstellen möchten.

Zunächst wissen wir, dass die Breite und Höhe des Bildes 1000px beträgt. Dies bedeutet, dass der Bereich der x- und y-Koordinaten in den Bereich 0-999 fallen muss, um die vorhandenen Ressourcen zu nutzen. Da zwei der erforderlichen Zahlen in den gleichen Bereich fallen, können wir den gleichen Bereich auf die Größe des Sterns anwenden, um die Gleichmäßigkeit zu gewährleisten. Die Größe wird später verkleinert, wenn wir die Zahlenreihe interpretieren.

Wir werden eine Reihe von Klassenvariablen verwenden. Dazu gehören: s_seed, eine einzelne Ganzzahl, die die gesamte Sequenz definiert; s_start und s_end, zwei Ganzzahlen, die durch Aufteilen des Startwerts in zwei generiert werden; und s_current, eine Ganzzahl, die die zuletzt generierte Zahl in der Sequenz enthält.

Creating a sequence
Siehe dieses Bild aus meinem vorherigen Artikel. 1234 ist der Startwert und 12 und 34 sind die Anfangswerte von s_start und s_end.
Tipp: Beachten Sie, dass jede generierte Zahl aus dem Startwert stammt. Es gibt keinen Aufruf von random(). Das bedeutet, dass derselbe Samen immer die gleiche Sternenlandschaft erzeugt.

Wir werden auch s_sequence verwenden, einen String, der die Gesamtsequenz enthält. Die letzten beiden Klassenvariablen sind s_image (vom Typ Image - eine Klasse, die wir später erstellen werden) und s_starfield (vom Typ Starfield, die Klasse, die wir gerade erstellt haben). Der erste speichert das Bild, während der zweite das Sternenfeld enthält.

Der Weg, den wir zur Erstellung dieses Generators einschlagen werden, ist recht einfach. Zuerst müssen wir einen Konstruktor erstellen, der einen Startwert akzeptiert. Wenn dies erledigt ist, müssen wir eine Methode erstellen, die eine Ganzzahl akzeptiert, die die Anzahl der Sterne darstellt, die sie erstellen muss. Diese Methode sollte dann den eigentlichen Generator aufrufen, um die Zahlen zu ermitteln. Und jetzt beginnt die eigentliche Arbeit... die Erstellung des Sequenzgenerators.


Codierung des Sequenzgenerators

Das erste, was ein Sequenzgenerator tun muss, ist, einen Startwert zu akzeptieren. Wie bereits erwähnt, werden wir den Samen in zwei Teile aufteilen: die ersten beiden Ziffern und die letzten beiden Ziffern. Aus diesem Grund müssen wir prüfen, ob der Startwert vierstellig ist, und ihn mit Nullen auffüllen, wenn dies nicht der Fall ist. Wenn dies erledigt ist, können wir die Startzeichenfolge in zwei Variablen aufteilen: s_start und s_end. (Beachten Sie, dass die Samen selbst nicht Teil der tatsächlichen Sequenz sind.)

Damit:

  • seed=1234 bedeutet s_start=12 und s_end=34
  • seed=7 bedeutet s_start=00 und s_end=07
  • seed=303 bedeutet s_start=03 und s_end=03

Weiter in der Zeile: Erstellen Sie eine andere Methode, die anhand der beiden Zahlen die nächste Zahl in der Sequenz generiert.

Die richtige Formel zu finden ist ein müder Prozess. Dies bedeutet normalerweise stundenlanges Ausprobieren, um eine Sequenz zu finden, die nicht zu viele Muster im resultierenden Bild enthält. Daher wäre es klüger, die beste Formel zu finden, wenn wir das Bild tatsächlich sehen können, als jetzt. Was uns gerade interessiert, ist die Suche nach einer Formel, die eine mehr oder weniger zufällige Sequenz erzeugt. Aus diesem Grund verwenden wir dieselbe Formel wie in der Fibonacci-Sequenz: Addition der beiden Zahlen.

Wenn dies erledigt ist, können wir nun fortfahren und mit der Erstellung der Sequenz beginnen. Bei einer anderen Methode manipulieren wir den anfänglichen Startwer, um einen ganzen Strom von Zahlen zu generieren, die dann als Attribute des Sternprofils interpretiert werden können.

Wir wissen, dass wir für einen bestimmten Stern neun Ziffern benötigen: Die ersten drei definieren die x-Koordinate, die mittleren drei definieren die y-Koordinate und die letzten drei definieren die Größe. Wie bei der Fütterung des Samens ist es daher wichtig, dass jede generierte Zahl dreistellig ist, um die Gleichmäßigkeit zu gewährleisten. In diesem Fall müssen wir die Zahl auch abschneiden, wenn sie größer als 999 ist.

Dies ist ziemlich ähnlich zu dem, was wir zuvor gemacht haben. Wir müssen nur die Nummer in einem temporären String speichern, temp, und dann die erste Ziffer entsorgen. Wenn die Zahl keine drei Ziffern hat, sollten wir sie wie zuvor mit Nullen auffüllen.

Nachdem dies abgeschlossen ist, sollten wir jetzt eine andere Methode erstellen, die jedes Mal, wenn wir drei Zahlen generieren, ein Sternprofil erstellt und zurückgibt. Mit dieser Methode können wir den Stern dann zur ArrayList of Stars hinzufügen.


Alles zusammenfügen

Nachdem dies abgeschlossen ist, können wir den Generator zusammenbauen. Es sollte die Anzahl der Sterne akzeptieren, die es erzeugen muss.

Wir wissen bereits, dass wir für einen Stern neun Ziffern benötigen, daher muss dieser Generator die Anzahl der Zeichen in den Zeichenfolgen zählen. Der Zähler s_counter speichert die maximale Länge der Sequenz. Daher multiplizieren wir die Anzahl der Sterne mit neun und entfernen eins, da ein String mit dem Index Null beginnt.

Wir müssen auch die Anzahl der Zeichen zählen, die wir seit der letzten Generierung eines Sterns erstellt haben. Für diese Aufgabe verwenden wir s_starcounter. In einer for-Schleife, die wiederholt wird, bis die Länge der Serie s_counter entspricht, können wir jetzt die bisher erstellten Methoden aufrufen.

Tipp: Wir dürfen nicht vergessen, s_start und s_end zu ersetzen, sonst generieren wir immer wieder dieselbe Nummer!

Sterne zeichnen

Jetzt, da der schwierige Teil vorbei ist, ist es endlich Zeit, zur Image-Klasse überzugehen und mit dem Zeichnen von Sternen zu beginnen.

Bei einer Methode, die ein Starfield akzeptiert, erstellen wir zuerst eine Instanz Color und rufen dann die Anzahl der Sterne ab, die wir zeichnen müssen. In einer for-Schleife zeichnen wir alle Sterne. Nachdem Sie eine Kopie des aktuellen Sterns erstellt haben, ist es wichtig, dass wir den Radius des Sterns abrufen. Da die Anzahl der Pixel eine Ganzzahl ist, sollten wir den Radius addieren, um ihn zu einer Ganzzahl zu machen.

Um den Stern zu zeichnen, verwenden wir einen radialen Gradienten.

An example of a radial gradient
Ein Beispiel für einen radialen Gradienten

Die Opazität eines radialen Gradienten hängt vom Abstand eines Pixels von der Mitte ab. Der Mittelpunkt des Kreises hat Koordinaten (0,0). Unter Verwendung der gebräuchlichsten Konvention hat jedes Pixel links von der Mitte eine negative x-Koordinate, und jedes Pixel darunter hat eine negative y-Koordinate.

Aus diesem Grund beginnen die verschachtelten for-Schleifen mit einer negativen Zahl. Mit dem Satz von Pythagoras berechnen wir den Abstand vom Mittelpunkt des Kreises und verwenden ihn, um die Deckkraft abzurufen. Bei Sternen mit dem kleinstmöglichen Radius (1 Pixel) hängt ihre Deckkraft ausschließlich von ihrer Größe ab.

Zum Abschluss müssen wir eine Methode erstellen, die einen String akzeptiert und damit das Bild unter diesem Dateinamen speichert. Im Generator sollten wir zuerst das Bild erstellen. Dann sollten wir diese beiden letzten Methoden vom Sequenzgenerator aufrufen.

In der Main-Klasse sollten wir eine Instanz des Sequenzgenerators erstellen, ihm einen Startwert zuweisen und eine gute Anzahl von Sternen erhalten (400 sollten ausreichen). Versuchen Sie, das Programm auszuführen, Fehler zu beheben und den Zielpfad zu überprüfen, um festzustellen, welches Image erstellt wurde.

The resulting image with a seed 1234
Das resultierende Bild mit einem Samen von 1234

Verbesserungen

Es gibt noch einige Änderungen, die wir vornehmen können. Das erste, was Sie zum Beispiel bemerkt hätten, ist, dass die Sterne in der Mitte gruppiert sind. Um dies zu beheben, müssten Sie eine gute Formel entwickeln, die alle Muster beseitigt. Alternativ können Sie eine Reihe von Formeln erstellen und mithilfe eines Zählers zwischen diesen wechseln. Die Formeln, die wir verwendeten, waren diese:

Es gibt noch eine einfache Verbesserung, die wir implementieren können. Wenn Sie in den Himmel schauen, sehen Sie ein paar große und viele weitere kleine Sterne. In unserem Fall entspricht die Anzahl der kleinen Sterne jedoch ungefähr der Anzahl der großen Sterne. Um dies zu beheben, müssen wir nur zur Methode getSize() in der Star-Klasse zurückkehren. Nachdem wir die Größe auf einen Bruchteil von eins gesetzt haben, müssen wir diese Zahl auf die Potenz einer ganzen Zahl erhöhen - zum Beispiel vier oder fünf.

Wenn Sie das Programm ein letztes Mal ausführen, erhalten Sie ein zufriedenstellendes Ergebnis.

The final result – a whole starscape procedurally generated by our Sequence Generator!
Das Endergebnis - eine ganze Sternenlandschaft, die prozedural von unserem Sequenzgenerator erzeugt wird!

Abschluss

In diesem Fall haben wir einen Sequenzgenerator verwendet, um prozedural einen Hintergrund zu generieren. Ein Sequenzgenerator wie dieser könnte viel mehr Verwendungszwecke haben - zum Beispiel könnte dem Stern eine Z-Koordinate hinzugefügt werden, sodass er anstelle des Zeichnens eines Bildes Sterne als Objekte in einer 3D-Umgebung erzeugen könnte.

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.