7 days of WordPress themes, graphics & videos - for free!* Unlimited asset downloads! Start 7-Day Free Trial
Advertisement
  1. Game Development
  2. Programming

Erstellung von einem Neon-Vektor-Shooter in XNA: Partikeleffekte

Read Time: 18 mins
This post is part of a series called Cross-Platform Vector Shooter: XNA.
Make a Neon Vector Shooter in XNA: Bloom and Black Holes
Make a Neon Vector Shooter in XNA: The Warping Grid

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

In dieser Reihe von Tutorials zeige ich Ihnen, wie Sie in XNA einen Neon-Twin-Stick-Shooter wie Geometry Wars erstellen. Das Ziel dieser Tutorials ist es nicht, Ihnen eine exakte Nachbildung von Geometry Wars zu hinterlassen, sondern die erforderlichen Elemente zu erläutern, mit denen Sie Ihre eigene hochwertige Variante erstellen können.


Überblick

In der bisherigen Serie haben wir das Gameplay eingerichtet und Bloom hinzugefügt. Dann werden wir Partikeleffekte hinzufügen.

Partikeleffekte entstehen durch die Herstellung einer großen Anzahl kleiner Partikel. Sie sind sehr vielseitig und können verwendet werden, um fast jedem Spiel Flair zu verleihen. In Shape Blaster werden wir Explosionen mit Partikeleffekten machen. Wir werden auch Partikeleffekte verwenden, um Abgasfeuer für das Schiff des Spielers zu erzeugen und den Schwarzen Löchern visuelles Flair zu verleihen. Außerdem schauen wir uns an, wie Partikel mit der Schwerkraft der Schwarzen Löcher interagieren.

Die ParticleManager klasse

Zuerst erstellen wir eine ParticleManager-Klasse, in der alle Partikel gespeichert, aktualisiert und gezeichnet werden. Wir werden diese Klasse so allgemein gestalten, dass sie problemlos in anderen Projekten wiederverwendet werden kann. Um den ParticleManager allgemein zu halten, ist er nicht dafür verantwortlich, wie die Partikel aussehen oder sich bewegen. Wir werden das woanders erledigen.

Partikel neigen dazu, schnell und in großer Zahl erzeugt und zerstört zu werden. Wir werden einen Objektpool verwenden, um zu vermeiden, dass große Mengen Müll entstehen. Dies bedeutet, dass wir eine große Anzahl von Partikeln im Voraus zuweisen und diese Partikel dann weiterhin wiederverwenden. Wir werden auch dafür sorgen, dass der ParticleManager eine feste Kapazität hat. Dies vereinfacht dies und stellt sicher, dass wir unsere Leistungs- oder Speicherbeschränkungen nicht überschreiten, indem wir zu viele Partikel erzeugen. Wenn die maximale Anzahl von Partikeln überschritten wird, werden die ältesten Partikel durch neue ersetzt.

Wir machen den ParticleManager zu einer generischen Klasse. Auf diese Weise können wir benutzerdefinierte Statusinformationen für die Partikel speichern, ohne sie fest im ParticleManager selbst zu codieren. Wir werden auch eine verschachtelte Particle klasse erstellen.

Die Particle klasse verfügt über alle Informationen, die zum Anzeigen eines Partikels und zum Verwalten seiner Lebensdauer erforderlich sind. Der generische Parameter T State enthält zusätzliche Daten, die wir möglicherweise für unsere Partikel benötigen. Welche Daten benötigt werden, hängt von den gewünschten Partikeleffekten ab. Es kann verwendet werden, um Geschwindigkeit, Beschleunigung, Rotationsgeschwindigkeit oder alles andere zu speichern, was Sie benötigen.

Um die Partikel besser verwalten zu können, benötigen wir eine Klasse, die als kreisförmiges Array fungiert. Dies bedeutet, dass Indizes, die normalerweise außerhalb der Grenzen liegen, stattdessen am Anfang des Arrays umbrochen werden. Dies macht es einfach, die ältesten Partikel zuerst zu ersetzen, wenn wir keinen Platz mehr für neue Partikel in unserem Array haben. Wir fügen Folgendes als verschachtelte Klasse in ParticleManager hinzu.

Wir können die Start-Eigenschaft so einstellen, dass angepasst wird, wo der Index Null in unserem CircularParticleArray im zugrunde liegenden Array entspricht, und Count wird verwendet, um zu verfolgen, wie viele aktive Partikel in der Liste enthalten sind. Wir werden sicherstellen, dass das Partikel am Index Null immer das älteste Partikel ist. Wenn wir das älteste Teilchen durch ein neues ersetzen, erhöhen wir einfach Start, wodurch das kreisförmige Array im Wesentlichen gedreht wird.

Nachdem wir unsere Hilfsklassen haben, können wir die ParticleManager-Klasse ausfüllen. Wir benötigen einige Mitgliedsvariablen und einen Konstruktor.

Die erste deklarierte Variable, updateParticle, ist eine benutzerdefinierte Methode, mit der die Partikel entsprechend dem gewünschten Effekt aktualisiert werden. Ein Spiel kann mehrere ParticleManagers haben, die bei Bedarf unterschiedlich aktualisiert werden. Wir erstellen auch eine CircularParticleList und füllen sie mit leeren Partikeln. Der Konstruktor ist der einzige Ort, an dem der ParticleManager Speicher zuweist.

Dann fügen wir die CreateParticle() -Methode hinzu, mit der ein neues Partikel mit dem nächsten nicht verwendeten Partikel im Pool oder dem ältesten Partikel erstellt wird, wenn keine nicht verwendeten Partikel vorhanden sind.

Partikel können jederzeit zerstört werden. Wir müssen diese Partikel entfernen und gleichzeitig sicherstellen, dass die anderen Partikel in derselben Reihenfolge bleiben. Wir können dies tun, indem wir die Liste der Partikel durchlaufen und gleichzeitig verfolgen, wie viele zerstört wurden. Während wir gehen, bewegen wir jedes aktive Teilchen vor alle zerstörten Teilchen, indem wir es gegen das erste zerstörte Teilchen austauschen. Sobald alle zerstörten Partikel am Ende der Liste sind, deaktivieren wir sie, indem wir die Variable Count der Liste auf die Anzahl der aktiven Partikel setzen. Zerstörte Partikel verbleiben im zugrunde liegenden Array, werden jedoch nicht aktualisiert oder gezeichnet.

ParticleManager.Update() aktualisiert jedes Partikel und entfernt zerstörte Partikel aus der Liste.

Das letzte, was in ParticleManager implementiert werden muss, ist das Zeichnen der Partikel.

Die ParticleState struktur

Dann erstellen Sie eine benutzerdefinierte Klasse oder Struktur, um das Aussehen der Partikel in Shape Blaster anzupassen. In Shape Blaster gibt es verschiedene Arten von Partikeln, die sich geringfügig unterscheiden. Daher erstellen wir zuerst eine enum für den Partikeltyp. Wir benötigen auch Variablen für die Geschwindigkeit und die Anfangslänge des Partikels.

Jetzt können wir die Aktualisierungsmethode des Partikels schreiben. Es ist eine gute Idee, diese Methode schnell zu machen, da sie möglicherweise für eine große Anzahl von Partikeln aufgerufen werden muss.

Wir fangen einfach an. Fügen Sie die folgende Methode zu ParticleState hinzu.

Feindliche Explosionen

Wir werden gleich zurückkommen und diese Methode verbessern. Lassen Sie uns zuerst einige Partikeleffekte erstellen, damit wir unsere Änderungen tatsächlich testen können. Deklarieren Sie in GameRoot einen neuen ParticleManager und rufen Sie dessen Methoden Update() und Draw() auf.

Deklarieren Sie außerdem eine neue Texture2D mit dem Namen LineParticle für die Textur des Partikels in der Art-Klasse und laden Sie die Textur wie für die anderen Sprites.

Lassen wir jetzt Feinde explodieren. Ändern Sie die Enemy.WasShot() -Methode.

Dadurch entstehen 120 Partikel, die mit unterschiedlichen Geschwindigkeiten in alle Richtungen nach außen schießen. Die zufällige Geschwindigkeit wird so gewichtet, dass sich Partikel eher in der Nähe der Höchstgeschwindigkeit bewegen. Dies führt dazu, dass sich mehr Partikel am Rand der Explosion befinden, wenn sie sich ausdehnt. Die Partikel halten 190 Frames oder etwas mehr als drei Sekunden.

Sie können das Spiel jetzt ausführen und beobachten, wie Feinde explodieren. Es müssen jedoch noch einige Verbesserungen für die Partikeleffekte vorgenommen werden.

Das erste Problem ist, dass die Partikel nach Ablauf ihrer Dauer abrupt verschwinden. Es wäre schöner, wenn sie reibungslos ausblenden könnten. Aber gehen wir noch ein bisschen weiter und lassen die Partikel heller leuchten, wenn sie sich schnell bewegen. Es sieht auch gut aus, wenn wir sich schnell bewegende Partikel verlängern und sich langsam bewegende Teilchen verkürzen.

Ändern Sie die Methode ParticleState.UpdateParticle() (Änderungen werden hervorgehoben).

Die Explosionen sehen jetzt viel besser aus, aber sie haben alle die gleiche Farbe. Wir können ihnen mehr Abwechslung geben, indem wir zufällige Farben wählen. Eine Methode zur Erzeugung zufälliger Farben besteht darin, die roten, blauen und grünen Komponenten zufällig auszuwählen. Dies führt jedoch zu vielen stumpfen Farben, und wir möchten, dass unsere Partikel ein neonlichtes Aussehen haben. Wir können mehr Kontrolle über unsere Farben haben, indem wir sie im HSV-Farbraum angeben. HSV steht für Farbton, Sättigung und Wert. Wir möchten Farben mit einem zufälligen Farbton, aber einer festen Sättigung und einem festen Wert auswählen. Wir brauchen eine Hilfsfunktion, die aus HSV-Werten eine Farbe erzeugen kann.

Jetzt können wir Enemy.WasShot() so ändern, dass zufällige Farben verwendet werden. Um die Explosionsfarbe weniger eintönig zu machen, wählen wir für jede Explosion zwei nahegelegene Schlüsselfarben aus und interpolieren sie für jedes Partikel linear um einen zufälligen Betrag.

Die Explosionen sollten wie in der folgenden Animation aussehen.

Enemy Explosion

Sie können mit der Farbgenerierung nach Ihren Wünschen herumspielen. Eine alternative Technik, die gut funktioniert, besteht darin, eine Reihe von Farbmustern für Explosionen von Hand auszuwählen und zufällig aus Ihren vorgewählten Farbschemata auszuwählen.

Kugelexplosionen

Wir können die Kugeln auch explodieren lassen, wenn sie den Bildschirmrand erreichen. Wir werden im Wesentlichen dasselbe tun, was wir für feindliche Explosionen getan haben.

Fügen Sie der Bullet-Klasse ein statisches Random Mitglied hinzu.

Ändern Sie dann Bullet.Update() wie im Beispiel.

Möglicherweise stellen Sie fest, dass es verschwenderisch ist, den Partikeln eine zufällige Richtung zu geben, da mindestens die Hälfte der Partikel sofort vom Bildschirm abweicht (mehr, wenn die Kugel in einer Ecke explodiert). Wir könnten zusätzliche Arbeit leisten, um sicherzustellen, dass Partikel nur Geschwindigkeiten erhalten, die der Wand gegenüberliegen, der sie zugewandt sind. Stattdessen nehmen wir ein Stichwort aus Geometry Wars und lassen alle Partikel von den Wänden abprallen. Alle Partikel, die sich außerhalb des Bildschirms befinden, werden zurückgeworfen.

Fügen Sie die folgenden Zeilen zu ParticleState.UpdateParticle() zwischen der ersten und der letzten Zeile hinzu.

Schiffsexplosion des Spielers

Wir werden eine wirklich große Explosion machen, wenn der Spieler getötet wird. Ändern Sie PlayerShip.Kill() wie hier:

Dies ähnelt den feindlichen Explosionen, aber wir verwenden mehr Partikel und verwenden immer das gleiche Farbschema. Der Partikeltyp ist ebenfalls auf ParticleType.None festgelegt.

In der Demo verlangsamen sich Partikel von feindlichen Explosionen schneller als Partikel vom explodierenden Schiff des Spielers. Dadurch hält die Explosion des Spielers etwas länger an und sieht etwas epischer aus.


Schwarze Löcher erneut besucht

Nachdem wir Partikeleffekte haben, lassen Sie uns die Schwarzen Löcher erneut betrachten und sie mit Partikeln interagieren lassen.

Wirkung auf Partikel

Schwarze Löcher sollten neben anderen Entitäten auch Partikel betreffen. Daher müssen wir ParticleState.UpdateParticle() ändern. Fügen Sie die folgenden Zeilen hinzu.

Hier ist n der Einheitsvektor, der auf das Schwarze Loch zeigt. Die Anziehungskraft ist eine modifizierte Version der inversen Quadratfunktion. Die erste Modifikation ist, dass der Nenner ist \(distance^ 2 + 10.000\). Dies bewirkt, dass sich die Anziehungskraft einem Maximalwert nähert, anstatt gegen unendlich zu tendieren, wenn der Abstand sehr klein wird. Wenn der Abstand viel größer als 100 Pixel ist, wird \(distance^2\) viel größer als 10.000. Daher hat das Hinzufügen von 10.000 zu \(distance^2\) einen sehr geringen Effekt, und die Funktion nähert sich einer normalen inversen Quadratfunktion an. Wenn der Abstand jedoch viel kleiner als 100 Pixel ist, hat der Abstand einen geringen Einfluss auf den Wert des Nenners, und die Gleichung wird ungefähr gleich:

Die zweite Modifikation ist das Hinzufügen einer Seitwärtskomponente zur Geschwindigkeit, wenn die Partikel nahe genug an das Schwarze Loch heranreichen. Dies dient zwei Zwecken. Erstens drehen sich die Partikel im Uhrzeigersinn in Richtung des Schwarzen Lochs. Zweitens, wenn die Partikel nahe genug kommen, erreichen sie ein Gleichgewicht und bilden einen leuchtenden Kreis um das Schwarze Loch.

Tipp: Um einen Vektor V um 90° im Uhrzeigersinn zu drehen, nehmen Sie (V.Y, -V.X). Um 90° gegen den Uhrzeigersinn zu drehen, nehmen Sie (-V.Y, V.X).

Partikel produzieren

Schwarze Löcher produzieren zwei Arten von Partikeln. Erstens sprühen sie regelmäßig Partikel aus, die um sie herum kreisen. Zweitens, wenn ein Schwarzes Loch geschossen wird, sprüht es spezielle Partikel aus, die nicht von seiner Schwerkraft beeinflusst werden.

Fügen Sie der BlackHole.WasShot() -Methode den folgenden Code hinzu.

Dies funktioniert meistens genauso wie die anderen Partikelexplosionen. Ein Unterschied besteht darin, dass wir den Farbton basierend auf der insgesamt verstrichenen Spielzeit auswählen. Wenn Sie mehrmals hintereinander auf das Schwarze Loch schießen, wird sich der Farbton der Explosionen allmählich drehen. Dies sieht weniger chaotisch aus als die Verwendung zufälliger Farben, während dennoch Variationen möglich sind.

Für das umlaufende Partikelspray müssen wir der BlackHole-Klasse eine Variable hinzufügen, um die Richtung zu verfolgen, in die wir derzeit Partikel sprühen.

Fügen Sie nun der BlackHole.Update() -Methode Folgendes hinzu.

Dies führt dazu, dass die Schwarzen Löcher Spritzer von lila Partikeln sprühen, die einen kühlen, leuchtenden Ring bilden, der das Schwarze Loch umkreist, wie unten:


Schiffsabgasfeuer

Gemäß den Gesetzen der geometrischen Neonphysik treibt sich das Schiff des Spielers an, indem es einen Strom feuriger Partikel aus seinem Auspuffrohr spritzt. Mit unserer Partikel-Engine ist dieser Effekt einfach zu erzielen und verleiht der Schiffsbewegung ein visuelles Flair.

Während sich das Schiff bewegt, erzeugen wir drei Partikelströme: einen Mittelstrom, der direkt aus dem Schiffsrücken abfeuert, und zwei Seitenströme, deren Winkel sich relativ zum Schiff hin und her drehen. Die beiden Seitenströme schwenken in entgegengesetzte Richtungen, um ein sich kreuzendes Muster zu bilden. Die Seitenströme haben eine rötlichere Farbe, während der Mittelstrom eine heißere gelb-weiße Farbe hat. Die folgende Animation zeigt den Effekt.

ShapeBlaster Exhaust FireShapeBlaster Exhaust FireShapeBlaster Exhaust Fire

Damit das Feuer heller leuchtet als allein durch die Blüte, wird das Schiff zusätzliche Partikel emittieren, die so aussehen:

Glow Particle

Diese Partikel werden getönt und mit den regulären Partikeln gemischt. Der Code für den gesamten Effekt ist unten dargestellt.

In diesem Code ist nichts hinterhältiges los. Wir verwenden eine Sinusfunktion, um den Schwenkeffekt in den Seitenströmen zu erzeugen, indem wir ihre Seitengeschwindigkeit über die Zeit variieren. Für jeden Stream erstellen wir zwei überlappende Partikel pro Frame: ein halbtransparent-weißes LineParticle und ein farbiges Glow-Partikel dahinter. Rufen Sie MakeExhaustFire() am Ende von PlayerShip.Update() auf, bevor Sie die Schiffsgeschwindigkeit auf Null setzen.


Abschluss

Mit all diesen Partikeleffekten sieht Shape Blaster ziemlich cool aus. Im letzten Teil dieser Serie werden wir einen weiteren großartigen Effekt hinzufügen: das Warping-Hintergrundgitter.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Game Development tutorials. Never miss out on learning about the next big thing.
Advertisement
Scroll to top
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.