If you're seeing this message, it means we're having trouble loading external resources on our website.

Wenn du hinter einem Webfilter bist, stelle sicher, dass die Domänen *. kastatic.org und *. kasandbox.org nicht blockiert sind.

Hauptinhalt

Partikelsysteme mit Kräften

In diesem Kapitel haben wir uns bisher darauf konzentriert, unseren Code objektorientiert zu strukturieren und eine Ansammlung von Partikeln zu handhaben. Vielleicht ist dir schon aufgefallen, dass wir dabei stillschweigend vom in den vorherigen Kapiteln gelernten ein paar Schritte zurückgegangen sind. Schauen wir uns den Konstruktur unseres einfachen Objektes Particle an:
var Particle = function(position) {
  this.acceleration = new PVector(0, 0{,}05);
  this.velocity = new PVector(random(-1, 1), random(-1, 0));
  this.position = new PVector(position.x, position.y);
  this.timeToLive = 255{,}0;
};
Und jetzt schauen wir uns mal die Methode update() an:
Particle.prototype.update = function(){
  this.velocity.add(this.acceleration);
  this.position.add(this.velocity);
  this.timeToLive -= 2;
};
Beachte, dass die Beschleunigung hier konstant ist und niemals größer wird, als im Konstruktur definiert. Ein deutlich besseres Framework wäre es, dem zweiten newtonschen Gesetz zu folgen (F=MA) und den Algorithmus zur Kräfteaddition einzubauen, an dem wir in dem Kapitel über Kräfte so hart gearbeitet haben.
Der erste Schritt ist, eine Methode applyForce() hinzuzufügen. (Vergiss nicht, dass wir eine Kopie von PVector machen müssen, bevor wir ihn durch die Masse teilen.)
Particle.prototype.applyForce = function(force) {
  var f = force.get();
  f.div(this.mass);
  this.acceleration.add(f);
};
Anschließend können wir eine weitere Codezeile einfügen, um die Beschleunigung am Ende von update() zurückzusetzen.
Particle.prototype.update = function() {
  this.velocity.add(this.acceleration);
  this.position.add(this.velocity);
  this.acceleration.mult(0);
  this.timeToLive -= 2{,}0;
};
So erhalten wir ein Objekt Particle, auf das wir eine Kraft anwenden können. Wo sollten wir die Funktion applyForce() nun aufrufen? An welcher Stelle im Code könnte man am besten eine Kraft auf ein Partikel anwenden? Die Wahrheit ist, dass es hier keine richtige oder falsche Antwort gibt. Tatsächlich hängt dies von der genauen Funktionalität und den Zielen eines bestimmten Programms ab. Dennoch können wir eine allgemeine Situation schaffen, die man wahrscheinlich auf die meisten Fälle anwenden kann, und ein Modell zur Anwendung von Kräften auf einzelne Partikel in einem System erschaffen.

Wind hinzufügen

Nehmen wir einmal das folgende Ziel: eine Kraft jedes Mal global mithilfe der Funktion draw() auf alle Partikel anwenden. Wir beginnen mit einer einfachen windartigen Kraft, welche die Partikel nach rechts drückt:
var wind = new PVector(0.4, 0);
Wir geben vor, dass dies immer angewandt werden soll, d.h. innerhalb von draw(). Schauen wir uns also unsere aktuelle Funktion draw() an.
draw = function() {
  background(168, 255, 156);
  particleSystem.addParticle();
  particleSystem.run();
};
Scheint so, als hätten wir ein kleines Problem. applyForce() ist eine Methode innerhalb des Objekts Particle. Es existiert aber keinen Verweis auf die einzelnen Partikel, sondern nur auf das Objekt ParticleSystem, und zwar in Form der Variable particleSystem.
Da wir die Kraft auf alle Partikel übertragen wollen, können wir jedoch die Kraft auf das Partikelsystem anwenden und es die Kraft so auf die einzelnen Partikel übertragen lassen.
draw = function() {
  background(168, 255, 156);
  particleSystem.applyForce(wind);
  particleSystem.addParticle();
  particleSystem.run();
};
Wenn wir aus der Funktion draw() eine neue Funktion auf dem Objekt ParticleSystem aufrufen, müssen wir diese Funktion natürlich in das Objekt ParticleSystem schreiben. Beschreiben wir mal die Aufgabe, welche diese Funktion erfüllen muss: eine Kraft in Form eines PVector annehmen und auf alle Partikel anwenden.
Und jetzt als Code:
ParticleSystem.prototype.applyForce = function(f){
  for(var i = 0; i < this.particles.length; i++){
    this.particles[i].applyForce(f);
  }
};
Es kommt einem fast schon dumm vor, diese Funktion zu schreiben. Wir sagen hier im Prinzip: Wende eine Kraft so auf ein Partikelsystem an, dass dieses System die Kraft auf alle einzelnen Partikel anwenden kann. Trotzdem ist das kein Blödsinn. Immerhin kontrolliert das Objekt ParticleSystem alle Partikel. Wenn wir also den Partikeln Anweisungen geben wollen, dann läuft das über ihren Manager.
Hier ist alles zusammengesetzt. Spiele mit der Windkraft und schau dir an, wie sie die Bewegung der Partikel beeinflusst. Siehst du, wie Partikel mit unterschiedlicher Masse auch unterschiedlich reagieren? Warum könnte das so sein?

Schwerkraft hinzufügen

Jetzt können wir noch eine etwas komplexere Kraft hinzufügen, die Schwerkraft. Der Unterschied zum Wind liegt darin, dass sie von der Masse der Objekte abhängt, auf die sie angewandt wird.
Erinnern wir uns noch mal an die Gleichung für die Gewichtskraft zwischen zwei Massen:  Fg=Gm1m2||r||2r^
Beachte beim Modellieren der Schwerkraft auf der Erde, dass die Erdanziehung deutlich stärker ist als alle anderen Gravitationskräfte. Daher müssen wir nur die Gleichung für die Schwerkraft zwischen der Erde und dem entsprechenden Objekt lösen. G and m1 sind für jedes Partikel gleich und r (der Radius vom Erdmittelpunkt) ebenfalls (der Radius der Erde ist sehr groß, im Vergleich zu der kurzen Strecke, welche sich die Partikel von ihm wegbewegen). In der Regel können wir also eine Vereinfachung zur Gravitationskonstante auf der Erde durchführen:
g=Gm1||r||2
Nun ist die Gravitationskraft nur noch eine Konstante g, die mit der Masse der Partikel mulitpliziert wird, sowie mit einem Einheitsvektor in Richtung der Kraft (welche immer nach unten zeigt):
Fg=gm2r^
Als Code bedeutet dies, dass wir auf jedes Partikel abhängig von seiner jeweiligen Masse eine andere Gewichtskraft anwenden müssen. Wie können wir das machen? Wir können hier nicht die bestehende Funktion applyForce benutzen, da hier von einer gleichen Kraft für jedes Partikel ausgegangen wird. Vielleicht können wir ihr ja einen Parameter übergeben, der applyForce die Anweisung gibt, mit der Masse zu multiplizieren. Lassen wir aber die Funktion so, wie sie ist, und erstellen eine neue Funktion namens applyGravity, welche die Kraft auf Basis eines globalen, konstanten Vektors berechnet:
// Ein konstanter, nach unten zeigender Vektor, am Anfang deklariert
var gravity = new PVector(0| 0,2);
ParticleSystem.prototype.applyGravity = function() {
    for(var i = 0; i < this.particles.length; i++) {
        var particleG = gravity.get();
        particleG.mult(this.particles[i].mass);
        this.particles[i].applyForce(particleG);
    }
};
Wenn wir dies nun korrekt ausgeführt haben, sollten alle unsere Partikel mit der gleichen Rate fallen wie in der folgenden Simulation. Der Grund dafür ist, dass die Schwerkraft auf der Multiplikation der Masse basiert, die Beschleunigung jedoch auf der Division durch die Masse. Letztendlich hat die Masse also keine Auswirkungen. Es mag nun unsinnig erscheinen, so viel Aufwand zu betreiben, nur um am Ende keine Auswirkungen zu haben. Es wird aber wichtig, wenn wir damit beginnen, verschiedene Kräfte zu addieren.

Abstoßungskräfte hinzufügen

Vielleicht wollen wir jetzt noch einen Schritt weitergehen und ein Objekt "Repeller" hinzufügen, das auf näherkommende Objekte eine Abstoßungskraft ausübt? Es wäre ähnlich zum Objekt "Attractor", das wir schon vorher angelegt haben, nur dass die Kraft in die andere Richtung geht. Wieder einmal müssen wir, wie bei der Schwerkraft, für jedes Partikel eine andere Kraft berechnen. Im Fall des Repellers basiert die Berechnung jedoch nicht auf der Masse, sondern auf der Distanz. Bei der Schwerkraft zeigen alle Kraftvektoren in die gleiche Richtung, beim Repeller jedoch haben die Kraftvektoren unterschiedliche Richtungen:
Schwerkraft: Alle Vektoren zeigen in die gleiche Richtung
Abstoßungskräfte: alle Vektoren haben unterschiedliche Richtungen
Da die Berechnung einer Abstoßungskraft etwas komplexer ist als die Berechnung der Schwerkraft (umso mehr, als wir letztendlich viele Abstoßungskräfte möchten), werden wir das Problem durch ein neues Objekt Repeller in unserem Partikelsystem mit der Schwerkraft lösen. Wir müssen unserem Code also zwei wichtige Dinge hinzufügen:
  1. Ein Objekt Repeller (deklarieren, initialisieren, anzeigen).
  2. Eine Funktion, welche das Objekt Repeller an das ParticleSystem übergibt, so dass es eine Kraft auf jedes Objekt Particle anwenden kann.
var particleSystem = new ParticleSystem(new PVector(width/2, 50));
var repeller = new Repeller(width/2-20, height/2);
var gravity = new PVector(0, 0{,}1);

draw = function() {
  background(214, 255, 171);

  // Apply gravity force to all Particles
  particleSystem.applyForce(gravity);
  particleSystem.applyRepeller(repeller);
  repeller.display();
  particleSystem.addParticle();
  particleSystem.run();
};
Es ist relativ einfach, ein Objekt Repeller zu erstellen, das angezeigt werden kann. Es ist nämlich ein Duplikat des bereits angelegten Objekts Attractor:
var Repeller = function(x, y) {
  this.position = new PVector(x, y);
};

Repeller.prototype.display = function() {
  stroke(255);
  strokeWeight(2);
  fill(127);
  ellipse(this.position.x, this.position.y, 32, 32);
};
Die schwierigere Frage ist nun: Wie schreiben wir die Methode applyRepeller()? Anstatt wie bei applyForce() einen PVector an eine Funktion zu übergeben, übergeben wir ein Objekt Repeller an applyRepeller() und lassen die Funktion die Berechnungen der Kräfte zwischen dem Repeller und allen Partikeln erledigen:
ParticleSystem.prototype.applyRepeller = function(r) {
  for(var i = 0; i < this.particles.length; i++){
    var p = this.particles[i];
    var force = r.calculateRepelForce(p);
    p.applyForce(force);
  }
};
Der große Unterschied ist hier, dass für jedes Partikel eine neue Kraft berechnet wird, da die Kraft, wie wir zuvor gesehen haben, von den Eigenschaften jedes Partikel im Verhältnis zum Repeller abhängt. Wir berechnen diese Kraft mithilfe der Funktion calculateRepelForce, dem Gegenteil zur Funktion calculateAttractionForce unserer Attractors.
Repeller.prototype.calculateRepelForce = function(p) {
  // Berechne Richtung der Kraft
  var dir = PVector.sub(this.position, p.position); 
  // Berechne den Abstand zwischen Objekten
  var dist = dir.mag();
  // Behalte die distanz in einem vernünftigen Bereich
  dist = constrain(dist, 1, 100);    
  // Berechne die Abstoßungskraft
  // Indirekt proportional zur Distanz
  var force = -1 * this.power/ (dist * dist);     
  // Normiere den Richtungsvektor
  // // (Distanz spielt hier keine Rolle)
  dir.normalize();
  // Erzeuge Kraftvektor --> Betrag * Richtung
  dir.mult(force);                                  
  return dir;
};
Beachte, dass wir während der gesamten Addition des Repellers zur Umgebung niemals darüber nachgedacht haben, das Objekt Particle selbst zu bearbieten. Ein Partikel muss also gar nicht mit den Details zu seiner Umgebung konfrontiert werden. Es muss einfach nur fähig sein, Ort, Geschwindigkeit und Beschleunigung zu steuern sowie auf eine externe Kraft, die auf es ausgeübt wird, zu reagieren.
Nun können wir uns das Beispiel im Ganzen ansehen. Versuche, die Stärken der Kräfte auf die Partikel - Schwerkraft und Abstoßungskraft - zu verändern und schau, was dann mit den Partiken passiert:

Willst du an der Diskussion teilnehmen?

Noch keine Beiträge.
Verstehst du Englisch? Klick hier, um weitere Diskussionen auf der englischen Khan Academy Seite zu sehen.