In diesem Tutorial werde ich Ihnen die Stardust Particle Engine vorstellen. Zuerst werde ich Ihnen zeigen, wie Sie Stardust einrichten, und dann werde ich die grundlegenden Verantwortlichkeiten der Stardust-Klasse abdecken und wie sie zusammenarbeiten, damit Stardust als Ganzes funktioniert.
Als Nächstes betrachten wir den allgemeinen Arbeitsablauf eines Stardust und erstellen einen Partikeleffekt, bei dem Sterne aus dem Mauszeiger herausschießen. Die Sterne verlangsamen sich allmählich, werden nach der Geburt größer und schrumpfen beim Sterben.
Abschließend möchte ich die Flexibilität von Stardust demonstrieren, indem ich verschiedene Variationen des bereits vollständigen Beispiels verwende, einschließlich der Verwendung von animierten Movieclips als Partikel, einer Zeitskala mit variablen Partikelsimulationen und dem Herausnehmen von Anzeigeobjekten verschiedener Klassen mit einem einzigen Emitter.
Dieses Tutorial richtet sich an Personen, die bereits mit der objektorientierten Programmierung von ActionScript 3.0 (OOP) vertraut sind. Ich gehe also davon aus, dass Sie bereits sehr gut wissen, was Klassen, Objekte, Vererbung und Schnittstelle bedeuten. Kein Problem mit OOP? Dann lass uns ein paar Sterne schießen!
Wie der Name schon sagt, wird Stardust zur Erzeugung von Partikeleffekten verwendet. Wenn Sie ein erfahrener ActionScripter sind, haben Sie möglicherweise oft Partikeleffekte von Grund auf neu erstellt und sagen "Ich bin total cool, Partikeleffekte von Grund auf zu erzeugen. Warum brauche ich dann eine Partikelmaschine?" Nun, Stardust hilft Ihnen dabei, sich mehr auf das tatsächliche Verhalten des Partikelverhaltens zu konzentrieren, als sich um die langweiligen Dinge zu kümmern, die sich auf niedriger Ebene befinden, wie etwa das Speichermanagement. Anstatt Code für die Partikeldaten zu schreiben, Ressourcen zu initialisieren und zu entsorgen, können Sie mit Stardust diese langweiligen Routinen überspringen und entscheiden, wie sich Ihre Partikel verhalten sollen.
Die Klassenstruktur von Stardust wurde von FLiNT Particle System, einer weiteren ActionScript 3.0-Partikel-Engine, inspiriert. Daher haben sie einige ähnliche Grundfunktionen.
Neben diesen grundlegenden Funktionen bietet Stardust auch einige erweiterte Funktionen für erfahrene Benutzer.
Bei Partikeleffekten ist es sehr wichtig, mit massiven Partikeldaten effizient umzugehen. Stardust nutzt häufig Objektpools und verknüpfte Listen, um die Leistung zu verbessern:
Bevor wir mit der eigentlichen Codierung beginnen, müssen wir uns eine Kopie von Stardust Particle Engine besorgen. Es ist unter der MIT-Lizenz veröffentlicht, was bedeutet, dass es völlig kostenlos ist, unabhängig davon, ob Sie es in einem kommerziellen oder nicht-kommerziellen Projekt verwenden möchten.
Hier ist die Projekt-Homepage von Stardust: http://code.google.com/p/stardust-particle-engine/
Sie können Stardust hier herunterladen: http://code.google.com/p/stardust-particle-engine/downloads/list
Zum Zeitpunkt des Schreibens ist 1.1.132 Beta die neueste Version, die von der Downloadliste heruntergeladen werden kann. Sie können immer die neueste Version aus dem SVN-Repository beziehen (die jedoch möglicherweise nicht stabil ist)..
Auf der Projekthomepage finden Sie auch weiteres Zubehör wie die API-Dokumentation und eine Kopie des PDF-Handbuchs. Es gibt sogar Video-Tutorials auf YouTube.
Ich werde hier kurz auf die Kernklassen von Stardust und ihre Verantwortlichkeiten eingehen.
Diese Klasse ist die Oberklasse aller Kernklassen, die Eigenschaften und Methoden speziell für die XML-Serialisierung definiert.
Im Allgemeinen geht es bei Partikeleffekten darum, eine Anzahl von Objekten mit ähnlichem, aber randomisiertem Aussehen und Verhalten zu steuern. Die Random-Klasse dient zum Generieren von Zufallszahlen, die in Stardust zur Randomisierung von Partikeleigenschaften verwendet werden können. Zum Beispiel ist die UniformRandom-Klasse eine Unterklasse der Random-Klasse. Der Name sagt alles aus: Die von einem UniformRandom-Objekt generierte Zufallszahl ist gleichmäßig verteilt, und ich werde diese Klasse insbesondere für das gesamte Lernprogramm verwenden.
Es gibt Zeiten, in denen eine eindimensionale Zufallszahl nicht ausreicht. Manchmal benötigen wir zweidimensionale Zufallszahlen, die im Wesentlichen Paare von Zufallszahlen sind, für Eigenschaften wie Position und Geschwindigkeit. Die Zone-Klasse dient zum Erzeugen von zweidimensionalen Zufallszahlenpaaren. Diese Klasse modelliert ein Zufallszahlenpaar als zufälligen Punkt in einer 2D-Zone. Beispielsweise generiert die CircleZone Zufallszahlenpaare (x, y) aus zufälligen Punkten innerhalb eines kreisförmigen Bereichs. Die Klassen Random und Zone werden hauptsächlich von der Klasse Initializer verwendet, die später behandelt wird. Die Zone3D-Klasse ist das 3D-Gegenstück dieser Klasse für 3D-Partikeleffekte.
In der Emitter-Klasse werden im Grunde alle Low-Level-Sachen gekapselt. Ein Emitter initialisiert neu erstellte Partikel, bevor sie in die Simulation eingefügt werden, aktualisiert die Partikeleigenschaften in jeder Iteration der Hauptschleife und entfernt abgestorbene Partikel aus der Simulation. Die Emitter.step () -Methode ist das, was Sie wiederholt aufrufen möchten, um Stardust am Laufen zu halten.
Die Clock-Klasse bestimmt die Rate der Erzeugung neuer Partikel für Emitter. Ein Emitter-Objekt enthält genau eine Referenz auf ein Clock-Objekt. Am Anfang jedes Emitter.step () - Methodenaufrufs fragt der Emitter das Uhrobjekt, wie viele neue Partikel es erstellen soll. Nehmen Sie zum Beispiel die SteadyClock-Klasse. Sie weist die Emitter an, mit konstanter Geschwindigkeit neue Partikel zu erstellen.
Diese Klasse dient zum Initialisieren von neu erstellten Partikeln. Ein Initialisierungsobjekt muss zu einem Emitter hinzugefügt werden, damit es funktioniert. Grundsätzlich initialisiert eine Initializer-Unterklasse nur eine Partikeleigenschaft. Beispielsweise initialisiert die Masseninitialisierungsklasse die Masse neuer Partikel. Einige Initialisierer akzeptieren ein Random-Objekt als Konstruktorparameter zum Initialisieren von Partikeln mit randomisierten Werten. Der folgende Code erstellt einen Lebensinitialisierer, der die Lebensdauer der Partikel auf Werte zentriert, die bei 50 mit einer Variation von 10, nämlich im Bereich von 40 bis 60, zentriert sind.
neues Leben (neues UniformRandom (50, 10));
Aktionsobjekte aktualisieren die Partikeleigenschaften in jeder Iteration der Hauptschleife (die Methode Emiter.step ()). Beispielsweise aktualisiert die Aktionsklasse Move die Partikelpositionen entsprechend der Geschwindigkeit. Ein Aktionsobjekt muss zu einem Emitter hinzugefügt werden, damit es funktioniert.
Nun, da Sie wissen, wie die Kernklassen zusammenarbeiten, werfen wir einen Blick auf den allgemeinen Arbeitsablauf für Stardust.
Sie beginnen mit der Erstellung eines Senders. Verwenden Sie die Emitter2D-Klasse für 2D-Partikeleffekte und die Emitter3D-Klasse für 3D-Effekte.
Var-Emitter: Emitter = neuer Emitter2D ();
Um die Rate der Partikelerzeugung festzulegen, benötigen wir eine Uhr. Dies kann entweder durch die Emitter.clock -Eigenschaft festgelegt werden, oder durch Übergabe einer Uhr als ersten Parameter an den Konstruktor des Emitters.
// Eigenschaftsansatz emitter.clock = new SteadyClock (1); // Konstruktoransatz var emitter: Emitter = new Emitter2D (new SteadyClock (1));
Fügen Sie dem Emitter über die Emitter.addInitializer () - Methode Initialisierer hinzu.
emitter.addInitializer (neues Leben (neues UniformRandom (50, 10))); emitter.addInitializer (neue Skala (neues UniformRandom (1, 0.2)));
Fügen Sie dem Emitter über die Emitter.addAction () - Methode Aktionen hinzu.
emitter.addAction (new Move ()); emitter.addAction (neuer Spin ());
Erstellen Sie einen Renderer, und fügen Sie den Emitter über die Renderer.addEmitter () - Methode dem Renderer hinzu.
var-Renderer: Renderer = neuer DisplayObjectRenderer (Container); // "container" ist unser container sprite renderer.addEmitter (emitter);
Rufen Sie schließlich wiederholt die Emitter.step () -Methode auf, um die Partikelsimulation aufrechtzuerhalten. Möglicherweise möchten Sie dazu das Enter-Frame-Ereignis oder einen Timer verwenden. In einem einzigen Aufruf der Methode Emitter.step () bestimmt die Uhr, wie viele neue Partikel erstellt werden sollen. Diese neuen Partikel werden durch Initialisierer initialisiert, alle Partikel werden durch Aktionen aktualisiert, tote Partikel werden entfernt, und der Renderer rendert den Renderer der Partikeleffekt.
// Enter-Frame-Ereignisansatz addEventListener (Event.ENTER_FRAME, mainLoop); // timer approach timer.addEventListener (TimerEvent.TIMER, mainLoop); Funktion mainLoop (e: Event): void emitter.step ();
In Ordung. Das ist so ziemlich alles für Stardust Primer. Nun ist es Zeit, die Flash IDE zu öffnen und sich die Hände schmutzig zu machen.
Erstellen Sie ein neues Flash-Dokument mit einer Größe von 640X400, einer Bildrate von 60 Bildern pro Sekunde und einem dunklen Hintergrund. Hier habe ich einen dunkelblauen Hintergrund mit Farbverlauf gemacht. Stardust funktioniert übrigens gut mit Flash Player 9 und 10, also ist es in Ordnung, egal, ob Sie Flash CS3 oder CS4 verwenden. In diesem Tutorial verwende ich Flash CS3.
Wir erzeugen einen Partikeleffekt mit Sternen. Daher müssen wir einen Stern zeichnen und in ein Symbol konvertieren, das natürlich für ActionScript exportiert wird. Dieses Symbol wird später verwendet, um unseren Partikeleffekt wiederzugeben. Benennen Sie das Symbol und die exportierte Klasse "Stern"..
Erstellen Sie eine neue Dokumentenklasse und nennen Sie sie StarParticles.
package import flash.display.Sprite; öffentliche Klasse StarParticles erweitert Sprite öffentliche Funktion StarParticles ()
Wie im allgemeinen Arbeitsablauf erwähnt, besteht der erste Schritt darin, einen Emitter zu erstellen. Der nächste Schritt besteht darin, dem Emitter Initialisierer und Aktionen hinzuzufügen. Obwohl dies im Dokumentklassenkonstruktor möglich ist, empfehle ich dringend, dies in einer separaten Emitter-Unterklasse durchzuführen. Es ist immer besser, das Partikelverhalten vom Hauptprogramm zu trennen. Dadurch ist der Code in Zukunft wesentlich sauberer und leichter zu ändern, ohne mit dem Hauptprogramm verwechselt zu werden.
Wir werden einen 2D-Partikeleffekt erzeugen, daher ist Emitter2D die Emitterklasse, die wir erweitern möchten. Erweitern Sie die Emitter2D-Klasse und nennen Sie sie StarEmitter, da wir später Sterne herausschießen lassen. Der Emitterkonstruktor akzeptiert einen Clock-Parameter. Daher deklarieren wir einen Konstruktorparameter, der eine Clock-Objektreferenz an den Konstruktor der Superklasse weitergibt.
Paket import idv.cjcat.stardust.twoD.emitters.Emitter2D; public class StarEmitter erweitert Emitter2D public function StarEmitter (clock: Clock) // das Clock-Objekt an den Konstruktor der Superklasse übergeben super (clock);
Ein besserer Ansatz zum Erstellen einer Emitter-Unterklasse besteht darin, Partikelparameter als statische Konstanten an einem Ort zu gruppieren. Wenn Sie also die Parameter anpassen möchten, wissen Sie immer, wo Sie die Deklarationen finden. Die Bedeutung dieser Konstanten wird später erläutert, wenn sie verwendet werden.
// durchschnittliche Lebensdauer privates statisches const LIFE_AVG: Number = 30; // Lebensdaueränderung private statische const LIFE_VAR: Number = 10; // durchschnittliche Skala private statische Konstante SCALE_AVG: Number = 1; // Skalierungsvariation privates statisches const SCALE_VAR: Number = 0.4; // Skalierungswachstumszeit private static const GROWING_TIME: Number = 5; // Skalierzeit für die Verkleinerung private statische const SHRINKING_TIME: Number = 10; // durchschnittliche Geschwindigkeit private static const SPEED_AVG: Number = 10; // Geschwindigkeitsänderung private static const SPEED_VAR: Number = 8; // Durchschnitt Omega (Winkelgeschwindigkeit) private statische Konstante OMEGA_AVG: Number = 0; // Omega-Variation private statische Konstante OMEGA_VAR: Number = 5; // Dämpfungskoeffizient private statische Konstante DAMPING: Number = 0.1;
Welche Initialisierer brauchen wir, um unseren Partikeleffekt zu erzeugen? Schauen wir uns die folgende Liste an:
Und hier ist der Code:
Punkt = neuer SinglePoint (); addInitializer (new DisplayObjectClass (Star)); addInitializer (new Life (neues UniformRandom (LIFE_AVG, LIFE_VAR))); addInitializer (new Scale (neues UniformRandom (SCALE_AVG, SCALE_VAR))); addInitializer (neue Position (Punkt)); addInitializer (neue Geschwindigkeit (neue LazySectorZone (SPEED_AVG, SPEED_VAR)))); addInitializer (neue Rotation (neues UniformRandom (0, 180))); addInitializer (neues Omega (neues UniformRandom (OMEGA_AVG, OMEGA_VAR)));
Okay, wir sind mit den Initialisierern fertig. Jetzt ist es Zeit, dem Emitter Aktionen hinzuzufügen. Nachfolgend finden Sie eine Liste von Aktionen, die wir benötigen:
Das ist es. Unser Emitter ist fertig. Hier ist der Code für diesen Emitter in seiner Gesamtheit, einschließlich der notwendigen Importanweisungen.
package import idv.cjcat.stardust.common.actions.Age; idv.cjcat.stardust.common.actions.deathLife importieren; import idv.cjcat.stardust.common.actions.ScaleCurve; import idv.cjcat.stardust.common.clocks.Clock; import idv.cjcat.stardust.common.initializers.Life; idv.cjcat.stardust.common.initializers.Scale importieren; import idv.cjcat.stardust.common.math.UniformRandom; idv.cjcat.stardust.twoD.actions.Damping importieren; import idv.cjcat.stardust.twoD.actions.Move; import idv.cjcat.stardust.twoD.actions.Spin; import idv.cjcat.stardust.twoD.emitters.Emitter2D; import idv.cjcat.stardust.twoD.initializers.DisplayObjectClass; import idv.cjcat.stardust.twoD.initializers.Omega; import idv.cjcat.stardust.twoD.initializers.Position; importieren Sie idv.cjcat.stardust.twoD.initializers.Rotation; import idv.cjcat.stardust.twoD.initializers.Velocity; import idv.cjcat.stardust.twoD.zones.LazySectorZone; import idv.cjcat.stardust.twoD.zones.SinglePoint; public class StarEmitter erweitert Emitter2D / ** * Konstanten * / private static const LIFE_AVG: Number = 30; private statische const LIFE_VAR: Number = 10; private statische Konstante SCALE_AVG: Number = 1; private statische Konstante SCALE_VAR: Number = 0,4; private static const GROWING_TIME: Number = 5; private statische Konstante SHRINKING_TIME: Number = 10; private statische const SPEED_AVG: Number = 10; private statische const SPEED_VAR: Number = 8; private statische Konstante OMEGA_AVG: Number = 0; private statische Konstante OMEGA_VAR: Number = 5; private statische Konstante DÄMPFUNG: Zahl = 0,1; öffentlicher Varpoint: SinglePoint; öffentliche Funktion StarEmitter (Uhr: Uhr) Super (Uhr); Punkt = neuer SinglePoint (); // Initialisierer addInitializer (new DisplayObjectClass (Star)); addInitializer (new Life (neues UniformRandom (LIFE_AVG, LIFE_VAR))); addInitializer (new Scale (neues UniformRandom (SCALE_AVG, SCALE_VAR))); addInitializer (neue Position (Punkt)); addInitializer (neue Geschwindigkeit (neue LazySectorZone (SPEED_AVG, SPEED_VAR)))); addInitializer (neue Rotation (neues UniformRandom (0, 180))); addInitializer (neues Omega (neues UniformRandom (OMEGA_AVG, OMEGA_VAR))); // Aktionen addAction (neues Zeitalter ()); addAction (neues DeathLife ()); addAction (new Move ()); addAction (neuer Spin ()); addAction (neue Dämpfung (DÄMPFUNG)); addAction (new ScaleCurve (GROWING_TIME, SHRINKING_TIME));
Nun ist es Zeit, zur Dokumentenklasse zurückzukehren und sie abzuschließen. Sehen wir uns die verbleibenden Aufgaben an.
Nachfolgend finden Sie den vollständigen Code für die Dokumentenklasse, einschließlich der erforderlichen Importanweisungen.
package import flash.display.Sprite; import flash.display.StageScaleMode; import flash.events.Event; import flash.geom.Rectangle; import idv.cjcat.stardust.common.clocks.SteadyClock; import idv.cjcat.stardust.common.renderers.Renderer; import idv.cjcat.stardust.twoD.renderers.DisplayObjectRenderer; public class StarParticles erweitert Sprite private var emitter: StarEmitter; public function StarParticles () // instanziiert den StarEmitter-Emitter = new StarEmitter (new SteadyClock (0.5)); // der Container sprite var container: Sprite = new Sprite (); // Der Renderer, der den Partikeleffekt rendert. var Renderer: Renderer = new DisplayObjectRenderer (container); renderer.addEmitter (Sender); // füge den Container der Anzeigeliste über dem Hintergrund hinzu addChildAt (container, 1); // das Enter-Frame-Ereignis verwenden addEventListener (Event.ENTER_FRAME, mainLoop); private Funktion mainLoop (e: Event): void // aktualisiere die SinglePoint-Position auf die Mausposition emitter.point.x = mouseX; emitter.point.y = mouseY; // Aufruf der Hauptschleife emitter.step ();
Endlich sind wir fertig! Lassen Sie uns nun das Ergebnis betrachten. Drücken Sie in Flash die STRG + EINGABETASTE, um den Film zu testen. Das Ergebnis wird angezeigt.
Wir sind noch nicht fertig! Lassen Sie uns noch ein paar Variationen machen. Der erste verwendet animierte Filmclips für unsere Partikel.
Diese erste Variante ist recht einfach und erfordert keine zusätzliche Codierung. Es ist so einfach wie das Erstellen einer einfachen Timeline-Animation. Bearbeiten Sie das Sternsymbol in Flash IDE, erstellen Sie einen anderen Keyframe und ändern Sie die Farbe des Sterns in diesem Frame in Rot. Dies führt im Wesentlichen dazu, dass die Sterne zwischen gelb und rot blinken. Möglicherweise möchten Sie einige leere Bilder dazwischen einfügen, da eine Bildrate von 60 Bildern pro Sekunde für ein Blinken mit zwei Bildern zu schnell ist.
Testen Sie jetzt den Film und überprüfen Sie das Ergebnis. Der blinkende Sterneffekt wirkt cartoonartig; Dies kann für klassische Schwindel-Sterne-Effekte verwendet werden, die häufig in Cartoons zu sehen sind.
Wie ich bereits erwähnt habe, ist eine der Stardust-Funktionen "einstellbare Simulationszeitskala", dh die von Stardust für die Partikelsimulation verwendete Zeitskala kann dynamisch angepasst werden. Alles wird durch Ändern der Emitter.stepTimeInterval-Eigenschaft vorgenommen, die standardmäßig 1 ist. Das folgende Codefragment ändert diesen Wert in 2, wodurch sich die Partikel doppelt so schnell bewegen und der Emitter neue Partikel mit doppelter Geschwindigkeit erzeugt.
emitter.stepTimeInterval = 2;
In dieser Variante erstellen wir einen Schieberegler auf der Bühne und verwenden ihn, um die Simulationszeitskala dynamisch anzupassen.
Ziehen Sie eine S