Hauptinhalt
Programmierung
Kurs: Programmierung > Lerneinheit 5
Lektion 8: Partikelsysteme- Einführung in die Partikel-Systeme
- Ein einzelnes Partikel
- Challenge: Fallender Blätter
- Ein Partikelsystem
- Challenge: Fischbläschen
- Systeme von Partikelsystemen
- Challenge: Feuerteufel
- Partikeltypen
- Challenge: Hexenkessel
- Partikelsysteme mit Kräften
- Challenge: Felsen im Fluss
- Projekt: Kolonien von Kreaturen
© 2023 Khan AcademyNutzungsbedingungenDatenschutzerklärungCookie-Meldung
Ein Partikelsystem
Bisher haben wir ein einzelnes Partikel erschaffen, das wir bei seinem Tod wieder neu erzeugen. Nun möchten wir einen kontinuierlichen Partikelstrom, bei dem mit jedem Zyklus über
draw()
ein neues Partikel erschaffen wird. Wir könnten einfach ein Array erzeugen und mit jedem Mal ein Partikel hineinschieben:var particles = [];
draw = function() {
background(133, 173, 242);
particles.push(new Particle(new PVector(width/2, 50)));
for (var i = 0; i < particles.length; i++) {
var p = particles[i];
p.run();
}
};
Wenn du das jetzt ausprobierst und den Code für ein paar Minuten laufen lässt, würdest du wahrscheinlich sehen, wie sich die Framerate immer weiter verlangsamt, bis das Programm komplett stehen bleibt. Dies liegt daran, dass wir immer mehr Partikel erzeugt haben, die verarbeitet und angezeigt werden müssen, aber keine entfernt haben. Wenn die Partikel sterben, werden sie nutzlos. Also können wir unserem Programm genausogut die unnötige Arbeit ersparen und die Partikel entfernen.
Um Elemente aus einem Array in JS zu entfernen, können wir die Methode
splice()
verwenden, bei welcher der gewünsche Index und die gewünschte Anzahl (nur einer) der zu löschenden Partikel definiert wird. Wir führen dies durch, nachdem wir abgefragt haben, ob das Partikel tatsächlich tot ist:var particles = [];
draw = function() {
background(133, 173, 242);
particles.push(new Particle(new PVector(width/2, 50)));
for (var i = 0; i < particles.length; i++) {
var p = particles[i];
p.run();
if (p.isDead()) {
particles.splice(i, 1);
}
}
};
Obwohl dieser Code gut laufen wird (und das Programm niemals zum Stillstand kommen wird), haben wir uns ganz schön in die Bredouille gebracht. Wenn wir den Inhalt eines Arrays verändern, während wir durch dieses Array iterieren, können wir uns ganz schön in Schwierigkeiten bringen. Schauen wir uns zum Beispiel den folgenden Code an:
for (var i = 0; i < particles.length; i++) {
var p = particles[i];
p.run();
particles.push(new Particle(new PVector(width/2, 50)));
}
Dies ist ein ziemlich extremes Beispiel (mit fehlerhafter Logik), liefert aber einen guten Beweis. Im vorherigen Fall fügen wir für jedes Partikel im Array ein neues Partikel zum Array hin zu (wir ändern also die
length
des Array). Dies führt zu einer Endlosschleife, da i
niemals über particles.length
hinaus laufen kann.Auch wenn das Programm nicht abstürzen wird, wenn man Elemente aus dem Partikel-Array entfernt (im Gegensatz zum Hinzufügen von Elementen), so gibt es doch ein Problem, das so heimtückisch ist, dass es keine Spuren hinterlässt. Um es zu entdecken, müssen wir erst einen wichtigen Punkt verstehen. Wenn ein Element aus einem Array entfernt wird, werden alle Elemente um eine Stelle nach links verschoben. Schau dir die Abbildung unten an, bei welcher der Partikel C (Index 2) entfernt wird. Die Partikel A und B behalten ihren Index, während er sich bei Partikel D von 3 auf 2 und bei E von 4 auf 3 ändert.
Tun wir mal so, als wären wir
i
und würden das Array durchlaufen.- wenn i = 0 → Prüfe Partikel A → Nicht löschen
- wenn i = 1 → Prüfe Partikel B → Nicht löschen
- wenn i = 2 → Prüfe Partikel C → Löschen!
- (Verschiebe Partikel D und E von den Stellen 3 und 3 auf 2 und 3)
- wenn i = 3 → Prüfe Partikel E → Nicht löschen
Siehst du das Problem? Wir haben Partikel D nicht geprüft! Als C von der Stelle #2 gelöscht wurde, ist D auf diese Stelle gerutscht, aber
i
befand sich schon bei Stelle #3. Das ist kein Weltuntergang, weil Partikel D beim nächsten Durchlauf geprüft wird. Trotzdem wollen wir eigentlich Code schreiben, der durch jedes einzelne Element des Arrays iteriert. Es ist nicht akzeptabel, ein Element auszulassen.Es gibt für dieses Problem eine einfache Lösung: rückwärts durch das Array zu iterieren. Wenn die restlichen Elemente beim Entfernen eines Elements von rechts nach links verschoben werden, kann man auf diese Art keines versehentlich auslassen. Wir müssen nur drei Stellen in der for-Schleife ändern:
for (var i = particles.length-1; i >= 0; i--) {
var p = particles[i];
p.run();
if (p.isDead()) {
particles.splice(i, 1);
}
}
Alles zusammengefasst haben wir nun Folgendes:
OK. Jetzt haben wir zwei Dinge umgesetzt. Wir haben ein Objekt geschrieben, das ein einzelnes
Particle
beschreibt. Und wir haben herausgefunden, wie man Arrays benutzt, um viele Objekte Particle
zu verwalten (das heißt auch, Partikel hinzuzufügen und zu löschen, wie man möchte).Wir könnten nun hier aufhören. Ein zusätzlicher Schritt, den wir aber noch gehen könnten und sollten, ist es, ein Objekt zu erstellen, welches die Ansammlung der
Particle
-Objekte selbst beschreibt: das Objekt ParticleSystem
. Damit können wir die umständliche Logik, mit der man in einer Schleife durch alle Partikel iteriert, von der Hauptseite entfernen, und auch mehr als ein Partikelsystem anlegen, wenn man möchte.Wenn du dich erinnerst, welches Ziel wir am Anfang des Lektion gesetzt haben, dann wollten wir, dass unser Programm folgendermaßen aussieht:
var ps = new ParticleSystem(new PVector(width/2, 50));
draw = function() {
background(0, 0, 0);
ps.run();
};
Nehmen wir das Programm, welches wir oben geschrieben haben, und schauen uns an, wie es in das Objekt
ParticleSystem
passt.Hier ist was wir vorher hatten:
var particles = [];
draw = function() {
background(133, 173, 242);
particles.push(new Particle(new PVector(width/2, 50)));
for (var i = particles.length-1; i >= 0; i--) {
var p = particles[i];
p.run();
if (p.isDead()) {
particles.splice(i, 1);
}
}
};
So können wir das zu einem Objekt umschreiben: Wir machen das Array
particles
zu einer Objekteigenschaft, legen eine Wrapper-Methode addParticle
zum Hinzufügen neuer Partikel an, und packen die gesamte Partikel-Ausführungslogik in run
:var ParticleSystem = function() {
this.particles = [];
};
ParticleSystem.prototype.addParticle = function() {
this.particles.push(new Particle());
};
ParticleSystem.prototype.run = function() {
for (var i = this.particles.length-1; i >= 0; i--) {
var p = this.particles[i];
p.run();
if (p.isDead()) {
this.particles.splice(i, 1);
}
}
};
Wir könnten auch dem Partikelsystem selbst neue Funktionen hinzufügen. Zum Beispiel könnte es für das Objekt
ParticleSystem
selbst nützlich sein, den Ursprungspunkt zu verfolgen, an dem die Partikel erzeugt werden. Dies stimmt mit der Idee überein, dass ein Partikelsystem ein “Emitter” ist, wo Partikel geboren und in die Welt hinausgeschickt werden. Der Ursprungspunkt sollte im Konstruktor initialisiert werden.var ParticleSystem = function(position) {
this.origin = position.get();
this.particles = [];
};
ParticleSystem.prototype.addParticle = function() {
this.particles.push(new Particle(this.origin));
};
Hier ist nun alles zusammen:
Willst du an der Diskussion teilnehmen?
Noch keine Beiträge.