Unlimited WordPress themes, graphics, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Game Development
  2. Programming

Erstellen Sie einen Neon Vector Shooter für iOS: Partikeleffekte

by
Difficulty:IntermediateLength:LongLanguages:
This post is part of a series called Cross-Platform Vector Shooter: iOS.
Make a Neon Vector Shooter for iOS: Virtual Gamepads and Black Holes
Make a Neon Vector Shooter for iOS: The Warping Grid

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

In dieser Reihe von Tutorials zeige ich Ihnen, wie Sie einen von Geometry Wars inspirierten Twin-Stick-Shooter mit Neongrafiken, verrückten Partikeleffekten und großartiger Musik für iOS mit C++ und OpenGL ES 2.0 erstellen. In diesem Teil werden wir Explosionen und visuelles Flair hinzufügen.

Überblick

In der bisherigen Serie haben wir das Gameplay eingerichtet und virtuelle Gamepad-Steuerelemente hinzugefügt. Als nächstes werden wir Partikeleffekte hinzufügen.


Warnung: Laut!

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.

Wechseln Sie zu Release Builds für Geschwindigkeitsgewinne

Bis jetzt haben Sie wahrscheinlich Shape Blaster mit allen Standard-debug-Builds des Projekts erstellt und ausgeführt. Während dies in Ordnung und großartig ist, wenn Sie Ihren Code debuggen, deaktiviert das Debuggen die meisten Geschwindigkeits- und mathematischen Optimierungen, die durchgeführt werden können, und aktiviert alle Zusicherungen im Code.

Wenn Sie den Code von nun an im Debug-Modus ausführen, werden Sie feststellen, dass die Bildrate dramatisch sinkt. Dies liegt daran, dass wir auf ein Gerät abzielen, das im Vergleich zu einem Desktop-Computer oder sogar einem Laptop weniger RAM, weniger CPU-Takt und weniger 3D-Hardware aufweist.

An diesem Punkt können Sie optional das Debuggen deaktivieren und den "Release" -Modus aktivieren. Der Release-Modus bietet uns eine vollständige Compiler- und Mathematikoptimierung sowie das Entfernen von nicht verwendetem Debugging-Code und Zusicherungen.

Wenn Sie das Projekt geöffnet haben, wählen Sie das Menü Produkt, Schema und dann Schema bearbeiten....

menumenumenu

Das folgende Dialogfenster wird geöffnet. Wählen Sie auf der linken Seite des Dialogfelds Ausführen und ändern Sie unter Build Configuration das Popup-Element von Debug auf Release.

ios-vector-shooter-build-dialogios-vector-shooter-build-dialogios-vector-shooter-build-dialog

Sie werden die Geschwindigkeitsgewinne sofort bemerken. Der Vorgang kann leicht rückgängig gemacht werden, wenn Sie das Programm erneut debuggen müssen: Wählen Sie einfach Debug statt Release, und Sie sind fertig.

Tipp: Beachten Sie jedoch, dass für jede solche Schemaänderung eine vollständige Neukompilierung des Programms erforderlich ist.

Die Partikelverwaltungsklasse

Zunächst 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, aber dennoch von Projekt zu Projekt angepasst werden muss. Um den ParticleManager so allgemein wie möglich 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 in das zu codieren
ParticleManager selbst.

Wir werden auch eine Particle klasse erstellen:

Die Particle klasse verfügt über alle Informationen, die zum Anzeigen eines Partikels und zum Verwalten seiner Lebensdauer erforderlich sind. Der ParticleState dient dazu, zusätzliche Daten zu speichern, 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. Dazu fügen wir im ParticleManager Folgendes als verschachtelte Klasse hinzu:

Wir können das mStart-Mitglied so einstellen, dass angepasst wird, wo der Index Null in unserem CircularParticleArray im zugrunde liegenden Array entspricht, und mCount 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 mStart, wodurch das kreisförmige Array im Wesentlichen gedreht wird.

Nachdem wir unsere Hilfsklassen haben, können wir die ParticleManager-Klasse ausfüllen. Wir brauchen eine neue Mitgliedsvariable und einen Konstruktor.

Wir erstellen mParticleList und füllen es mit leeren Partikeln. Der Konstruktor ist der einzige Ort, an dem der ParticleManager Speicher zuweist.

Als Nächstes fügen wir die Methode createParticle() hinzu, mit der ein neues Partikel unter Verwendung des nächsten nicht verwendeten Partikels im Pool oder des ältesten Partikels 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 mCount 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 Partikelstatusklasse

Als Nächstes 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 zunächst 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 update() -Methode 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 wir ParticleState die folgende Methode hinzu:

Wir werden gleich zurückkommen und diese Methode verbessern. Lassen Sie uns zunächst einige Partikeleffekte erstellen, damit wir unsere Änderungen tatsächlich testen können.

Feindliche Explosionen

Deklarieren Sie in GameRoot einen neuen ParticleManager und rufen Sie dessen Methoden update() und draw() auf:

Außerdem deklarieren wir eine neue Instanz der tTexture-Klasse in der Art-Klasse mit dem Namen mLineParticle für die Textur des Partikels. Wir laden es wie die Sprites des anderen Spiels:

Lassen wir jetzt Feinde explodieren. Wir werden die Enemy::wasShot() -Methode wie folgt ändern:

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 maximalen Geschwindigkeit 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 sanft ausblenden könnten, aber gehen wir etwas 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() wie folgt (Änderungen werden hervorgehoben).

Die Explosionen sehen jetzt viel besser aus, aber sie haben alle die gleiche Farbe.

explosion-mono
Monochromatische Explosionen sind ein guter Anfang, aber können wir es besser machen?

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.

Ändern wir Bullet::update() wie folgt:

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 einen Hinweis aus Geometry Wars und lassen alle Partikel von den Wänden abprallen, sodass alle Partikel, die sich außerhalb des Bildschirms befinden, zurückgeworfen werden.

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

Schiffsexplosion des Spielers

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

Dies ähnelt den feindlichen Explosionen, aber wir verwenden mehr Partikel und verwenden immer das gleiche Farbschema. Der Partikeltyp ist ebenfalls auf ParticleState::kNone 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 wir 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 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 der distance^2 viel größer als 10.000. Daher hat das Hinzufügen von 10.000 zum 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: vel += n
  • 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:
    1. Dadurch drehen sich die Partikel im Uhrzeigersinn in Richtung des Schwarzen Lochs.
    2. 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 es um 90 ° gegen den Uhrzeigersinn zu drehen, nimm (-V.Y, V.X).

Partikel produzieren

Ein Schwarzes Loch erzeugt zwei Arten von Partikeln. Erstens sprüht es regelmäßig Partikel aus, die um es 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 größtenteils 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, lässt aber dennoch Variationen zu.

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:

Jetzt fügen wir der BlackHole::update() -Methode Folgendes hinzu.

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

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 herausfeuert, 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, wird das Schiff zusätzliche Partikel abgeben, die so aussehen:

glow

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 halbtransparentes, weißes LineParticle und ein farbiges Glow-Partikel dahinter. Anruf
MakeExhaustFire() am Ende von PlayerShip.Update(), unmittelbar bevor die Schiffsgeschwindigkeit auf Null gesetzt wird.

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
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.