Lektion 39
Einführung in physikalische Simulationen
Wenn Sie mit Physik vertraut sind und anfangen wollen, Code einer physikalischen Simulation zu implementieren, könnte dieses Tutorial Ihnen helfen. Damit Sie nutzen aus diesem Tutorial ziehen können, sollten Sie mit Vektor-Operationen in 3D sowie mit physikalischen Konzepten wie Kraft und Geschwindigkeit vertraut sein.
In diesem Tutorial werden Sie eine einfache physikalische Simulations Engine finden. Inhalt des Tutorials ist folgender:
Inhalt:
Das Design: |
* class Vector3D |
-> |
Ein Objekt um einen 3D Vektor oder einen 3D Punkt im Raum zu repräsentieren. |
Kraft und Bewegung: |
* class Mass |
-> |
Ein Objekt um eine Masse zu repräsentieren. |
Wie eine Simulation operieren sollte: |
* class Simulation |
-> |
Ein Container Objekt für simulierte Massen. |
Eine Simulation von einer Applikation durchführen lassen: |
* class ConstantVelocity : public Simulation |
-> |
Ein Simulation Objekt das eine Masse mit einer konstanten Geschwindigkeit enthält. |
Kräfte anwenden: |
* class MotionUnderGravitation : public Simulation |
-> |
Ein Simulation Objekt das eine Masse erzeugt, die sich unter Gravitation bewegt. |
* class MassConnectedWithSpring : public Simulation |
-> |
Ein Simulation Objekt das eine Masse erzeugt, die mit einem Punkt mittels einer Feder verbunden ist. |
Das Design:
Das designen von physikalischen Simulation Engines ist nicht immer einfach. Aber es gibt eine einfache Reihenfolge von Abhängigkeiten; Applikationen hängen vom Simulation Toolkit ab und das Simulation Toolkit von Mathe Bibliotheken. Hier verwenden wir diese einfache Reihenfolge. Unser Vorhaben ist es, einen Kontainer zu erhalten, der Bewegungen von Massen simuliert. Das Simulation Toolkit wird ein Objekt "class Mass" enthalten und ein Objekt "class Simulation". "class Simulation" wird unser Kontainer sein. Wenn wir die Simulation Klasse haben, werden wir in der Lage sein, Applikationen zu entwickeln. Aber davor benötigen wir eine Mathe Bibliothek. Die Bibliothek enthält nur eine Klasse "class Vector3D". Die Klasse Vector3D wird dazu verwendet, um Punkte, Vektoren, Position, Geschwindigkeit und Kraft im 3 dimensionalen Raum zu repräsentieren.
* class Vector3D ---> Einn Objekt um einen 3D Vektor oder einen 3D Punkt im Raum zu repräsentieren.
Die Vector3D Klasse ist das einzige Element in unserer bescheidenen Mathe Bibliothek. Vector3D enthält die x, y und z Werte und implementiert Operationen zur Vektor Arithmetik in 3D. Addition, Subtraktion, Multiplikation und Division Operatoren werden in Vector3D implementiert. Da dieses Tutorial sich auf Physik konzentriert, werde ich bei Vector3D nicht weiter ins Detail gehen. Wenn Sie einen Blick in Physics1.h werfen, werden Sie sehen, wie einfach Vector3D ist.
Kraft und Bewegung:
Um physikalische Simulationen zu implementieren, sollten wir wissen, was eine Masse ist. Eine Masse hat eine Position und eine Geschwindigkeit. Eine Masse hat ein Gewicht auf der Erde, Mond, Mars und jedem anderen Platz, wo es Schwerkraft gibt. Das Gewicht ist verschieden bei verschiedener Schwerkraft an verschiedenen Plätzen. Aber es gibt einen übereinstimmenden Wert für eine Masse, der für alle Bedingungen gleich ist. Dieser Wert wird ebenfalls Masse genannt. Der Massen Wert einer Masse! Der Massen Wert repräsentiert "wieviel eine Masse im Raum existiert"! Nehmen wir als Beispiel ein Buch, welches eine Masse mit dem Gewicht von, sagen wir, 1 kg auf der Erde und einem Gewicht von 0.17 kg auf dem Mond hat und einen Massen Wert von 1 kg überall hat. Der Massen Wert ist gleich der Masse auf der Erde.
Nachdem Sie die Masse einer Masse verstanden haben, sollten wir mit Kraft und Bewegung weitermachen. Eine Masse, mit einer Geschwindigkeit im Raum ungleich null, bewegt sich in die Richtung der Geschwindigkeit. Deshalb ist ein Grund der Positionsänderung die Geschwindigkeit. Das verstreichen von Zeit ist ein weiterer Grund. Die Änderung der Position hängt davon ab wie schnell sich die Masse bewegt und wieviel Zeit verstreicht. Sie sollten das bishier alles verstanden haben, bevor Sie zum nächsten Paragraphen weitergehen. Wenn nicht, verbringen Sie etwas Zeit damit, darüber nachzudenken wie Position, Geschwindigkeit und Zeit zusammenhängen.
Die Geschwindigkeit einer Masse ändert sich, wenn ein Kraft auf diese wirkt. Die Geschwindigkeit richtet sich in die Richtung der Kraft. Diese Richtung ist proportional zur Kraft und invers proportional zur Masse. Die Änderung der Geschwindigkeit pro Zeiteinheit wird Beschleunigung genannt. Je größer die Kraft auf die Masse wirkt, umso größer ist dessen Beschleunigung. Je größer der Massenwert einer Masse, desto kleiner ist seine Beschleunigung. Beschleunigung wird so formuliert:
Beschleunigung = Kraft / Masse
Daraus leiten wir die folgende bekannt Formel ab:
Kraft = Masse * Beschleunigung
(Wir werden hauptsächlich die Beschleuniguns-Formel verwenden)
Zur Vorbereitung, ein physikalisches Medium zu simulieren, sollten Sie sich Gedanken über die Umgebung machen, in der die Simulation statt findet. Die Umgebung in diesem Tutorial ist einfach ein leerer Raum, der darauf wartet, mit Massen gefüllt zu werden, die wir erzeugen. Die Einheiten der Werte, die die Massen repräsentieren und die Zeit sollten als erstes bestimmt werden. Ich habe mich dazu entschieden, Sekunden als Zeiteinheit zu nehmen und als Positions-Einheit Meter. Daraus ergibt sich, dass die Einheit der Geschwindigkeit Meter pro Sekunde (m/s) sein wird. Und die Einheit der Beschleunigung ist dann Meter pro Sekunde pro Sekunde (m/s/s)! (das bedeutet Geschwindigkeit pro Sekunde, da Geschwindigkeit gleich Meter pro Sekunde ist). Ich habe weiterhin festgelegt, dass die Massenwerte Kilogramm (kg) sind.
* class Mass ---> Ein Objekt um eine Masse zu repräsentieren.
Nun fangen wir an, die Theorie zu nutzen! Wir müssen eine Klasse schreiben, die eine Masse repräsentiert. Sie sollte den Massenwert, die Position, die Geschwindigkeit und die Kraft die auf die Instanz wirkt, enthalten.
class Mass
{
public:
float m; // Der Massenwert.
Vector3D pos; // Position im Raum.
Vector3D vel; // Geschwindigkeit.
Vector3D force; // Kraft die auf diese Masse angewendet wird.
Mass(float m) // Konstruktor.
{
this->m = m;
}
...
Wir wollen eine Kraft auf diese Masse wirken lassen. Zu einem Zeitpunkt können verschiedene Quellen von externen Kräfte auf die Masse einwirken. Die Vektor Summe dieser Kräfte ergibt die gesaamte Kraft auf die Masse zu diesem Zeitpunkt. Bevor wir anfangen Kräfte anzuwenden, sollten wir die Kraft auf die Masse zurücksetzen (resetten). Dann können wir externe Kräfte auf die Masse hinzu addieren.
(class Mass continued)
void applyForce(Vector3D force)
{
this->force += force; // die externe Kraft wird zu der Kraft auf die Masse hinzugefügt.
}
void init() // Diese Methode setzt die Kraft-Werte auf null.
{
force.x = 0;
force.y = 0;
force.z = 0;
}
...
Es gibt eine einfache Reihenfolge and Dingen, die in einer Simulation gemacht werden müssen:
- Die Kraft zurücksetzen (resetten) (siehe auch die init() Methode)
- Externe Kräfte anwenden
- Die Zeit um "die Änderung in der Zeit" iterieren/wiederholen
Hier wird die Iteration der Zeit durch "die Euler Methode" implementiert. Die Euler Methode ist eine einfache Simulations Methode. Es gibt bessere Methoden für Simulationen. Aber Euler ist gut genug für eine Vielzahl an Applikationen. Die meisten Computer und Videospiele benutzen die Euler Methode. Was diese Methode macht, ist das Berechnen der nächsten Geschwindigkeit und die nächste Position einer Masse entsprechend der angewendeten Kräfte und der vergangenen Zeit. Die Iteration wird in void simulate(float dt) vorgenommen:
(class Mass continued)
void simulate(float dt)
{
vel += (force / m) * dt; // Die Änderung der Geschwindigkeit wird zur Geschwindigkeit hinzu addiert.
// Die Änderung ist proportional zur Beschleunigung (Kraft / m) und der Änderung der Zeit.
pos += vel * dt; // Änderung der Position wird zur Position hinzu addiert.
// Änderung der Position ist die Geschwindigkeit mal die Änderung der Zeit.
}
Wie eine Simulation operieren sollte:
In einer physikalischen Simulation wird bei jeder Iteration der selbe Prozess ausgeführt. Kräfte werden auf null gesetzt, Kräfte werden angewendet, neue Positionen und neue Geschwindigkeiten werden berechnet. Dieser Prozess wiederholt sich so lange, wie wir wollen, dass die Zeit voran schreitet. Dieser Prozess ist in "class Simulation" implementiert.
* class Simulation ---> Ein Kontainer Objekt um Massen zu simulieren.
Die Simulation Klasse enthält Massen als seine Elemente. Die Rolle der Klasse ist es, Massen zu erzeugen und zu löschen und die Simulations-Prozedur zu enthalten.
class Simulation
{
public:
int numOfMasses; // Anzahl der Massen in diesem Kontainer.
Mass** masses; // Massen werden in einem Array von Zeigern gehalten. (Hier repräsentiert Mass** ein 1 dimensionales Array).
Simulation(int numOfMasses, float m) // Konstruktor erzeugt einige Massen mit dem Massenwert m.
{
this->numOfMasses = numOfMasses;
masses = new Mass*[numOfMasses]; // erzeugt ein Array aus Zeigern.
for (int a = 0; a < numOfMasses; ++a) // wir werden jeden Zeiger im Array durchlaufen.
masses[a] = new Mass(m); // erzeuge eine Masse als Zeiger und speicher sie in dem Array.
}
virtual void release() // lösche die erzeugten Massen.
{
for (int a = 0; a < numOfMasses; ++a) // wir werden alle löschen.
{
delete(masses[a]);
masses[a] = NULL;
}
delete(masses);
masses = NULL;
}
Mass* getMass(int index)
{
if (index < 0 || index >= numOfMasses) // wenn der Index nicht im Array ist.
return NULL; // dann gebe NULL zurück.
return masses[index]; // ermittle die Masse an diesem Index.
}
...
Die Simulations Prozedur besteht aus drei Schritten:
- init() um die Kräfte auf null zu setzen
- solve() um die Kräfte anzuwenden
- simulate(float dt) um die Massen pro Änderung der Zeit zu iterieren
(class Simulation Fortsetzung)
virtual void init() // Diese Methode wird die init() Methode für jede Masse aufrufen.
{
for (int a = 0; a < numOfMasses; ++a) // Wir werden jede Masse initialisieren.
masses[a]->init(); // Rufe die init() Methode der Masse auf.
}
virtual void solve() // Keine Implementation, da keine Kräfte in diesem Basis-Kontainer gewünscht sind.
{
// In fortschrittlicheren Kontainern wird die Methode überschrieben und einige Kräfte werden auf die Massen angewendet.
}
virtual void simulate(float dt) // Iteriere die Massen um die Änderung in der Zeit.
{
for (int a = 0; a < numOfMasses; ++a) // Wir iterieren durch jede Masse.
masses[a]->simulate(dt); // Iteriere die Masse und ermittle neue Position und neue Geschwindigkeit.
}
...
Die Simulations-Prozedur wird in eine Methode gepackt:
(class Simulation continued)
virtual void operate(float dt) // Die komplette Prozedur der Simulation.
{
init(); // Schritt 1: Resette die Kräfte auf null.
solve(); // Schritt 2: Wende Kräfte an.
simulate(dt); // Schritt 3: iteriere durch die Massen um die Änderung in der Zeit.
}
};
Bis hier haben wir eine einfach physikalische Simualations Engine implementiert. Sie basierd auf einer Mathe-Bibliothek. Sie enthält Masse und Simulations Klassen. Sie benutzt ein sehr übliches Pattern der Simulations-Prozedur und sie benutzt die Euler Methode. Nun sind wir bereit Applikationen zu entwickeln. Die Applikationen die wir entwickeln werden sind:
- Masse mit konstanter Geschwindigkeit
- Masse unter Schwerkraft
- Masse die mit einem fixen Punkt durch eine Feder verbunden ist
Eine Simulation duch eine Applikation ausführen lassen:
Bevor wir eine bestimmte Simulation schreiben, sollten wir wissen, wie wir Simulationen von Applikationen durchführen lassen können. In diesem Tutorial ist die Simulations Engine und die Applikation, die die Simulationen ausführt in zwei verschiedenen Dateien untergebracht. In der Applikations-Datei ist eine Funktion wie diese:
void Update (DWORD milliseconds) // Perform Motion Updates Here.
Diese Funktion wird immer wieder aufgerufen und zwar bei jeder Frame Aktualisierung. Das "DWORD milliseconds" ist die Zeitperiode zwischen des vorherige Frame zum aktuellen Frame. Dadurch können wir sagen, dass wir Simulationen entsprechend zu den "Millisekunden" interierern sollten. Wenn die Simulationen diesesr Zeitperiode folgen, sollten sie parallel zur realen Welt Zeit gehen. Um eine Simulation zu iterieren, rufen wir einfach die "void operate(float dt)" Methode auf. Um diese Methode aufzurufen, sollten wir "dt" kennen. Da wir als Zeiteinheit Sekunden haben, konvertieren wir als erstes die Millisekunden in Sekunden (siehe folgender Code). Dann benutzen wir einen Wert "SlowMotionRatio" (langsame Bewegung Verhältnis), was bedeutet, wie langsam wir die Simulation relativ zu realen Welt Zeit laufen lassen wollen. Wir dividieren dt durch diesen Wert und wir erhalten ein neues dt. Nun können wir dt zu "timeElapsed" (verstrichene Zeit) addieren. "timeElapsed" ist die Zeit der Simulation, nicht die Zeit der realen Welt.
void Update (DWORD milliseconds)
{
...
...
...
float dt = milliseconds / 1000.0f; // konvertiert Millisekunden in Sekunden.
dt /= slowMotionRatio; // Dividiere dt durch slowMotionRatio und erhalte das neue dt.
timeElapsed += dt; // Iteriere die vergangene Zeit.
...
Nun ist dt fast bereit, um die Simulation durchzuführen. Aber! es gibt eine wichtige Sache, die wir wissen sollte: dt ist das Detail der Präzision. Wenn dt nicht klein genug ist, wird Ihre Simulation Instabilität zeigen und die Bewegung würde nicht genau genug berechnet werden. Stabilitäts Analyse wird für physikalische Simulationen dazu verwendet, um den maximalen dt Wert zu finden, den die Simulation händeln kann. In diesem Tutorial werden wir nicht auf die Details eingehen und wenn Sie nur ein Spiel entwickeln und nicht gerade eine wissentschaftliche Applikation, ist es immer möglich den maximalen dt Wert durch "trial and error" herauszufinden.
In einem Autorennspiel zum Beispiel ist ein dt Wert von 2 bis 5 Millisekunden für ein normales Auto passend und 1 bis 3 für ein Formel-1-Wagen. In einer Arcade Simulation ist es möglich für dt 20 bis 200 Millisekunden zu setzen. Je kleiner der Wert von dt ist, desto mehr CPU Zyklen werden benötigt, um mit der realen Welt Zeit gleichzuziehen. Aus diesem Grund werden physikalische Simulationen nur sehr selten in älteren Spielen verwendet.
Im folgenden Code definieren wir das mögliche Maximum für dt als 0.1 Sekunden (100 Millisekunden). Mit diesem Wert werden wir die Anzahl der Iterationen berechnen, die gemacht werden für die aktuelle Aktualisierung. Wir schreiben die folgende Formel:
int numOfIterations = (int)(dt / maxPossible_dt) + 1;
numOfIterations ist die Anzahl der Iterationen, die für die Simulation gemacht werden. Sagen wir, dass die Applikation mit 20 Frames pro Sekunde läuft, was dt = 0.05 Sekunden entspricht. Dann wird numOfIterations gleich 1. Die Simulation wird einmal in 0.05 Sekunden iteriert. Sagen wir, dass dt gleich 0.12 Sekunden war. Dann ist numOfIterations gleich 2. Direkt von "int numOfIterations = (int)(dt / maxPossible_dt) + 1;" gefolgt, sollten Sie sehen, dass dt erneut berechnet wird. Dann wird dt durch numOfIterations dividiert und somit gilt dt = 0.12 / 2 = 0.06. dt war ursprünglich größer als der maximal mögliche Wert 0.1. Nun haben wir dt gleich 0.06, aber wir werden die Simulation zweimal durchführen, so dass wir 0.12 Sekunden als Ergebnis erhalten. Schauen Sie sich den folgenden Code an und seien Sie sich sicher, dass Sie den Paragraphen eben verstanden haben.
...
float maxPossible_dt = 0.1f; // sage, dass das maximal mögliche dt 0.1 Sekunden ist.
// dies wird benötigt, so dass wir nicht einen ungenauen dt Wert übergeben.
int numOfIterations = (int)(dt / maxPossible_dt) + 1; // berechne die Anzahl der Iterationen bei dieser Aktualisierung abhängig von maxPossible_dt und dt.
if (numOfIterations != 0) // vermeide Division durch null.
dt = dt / numOfIterations; // dt sollte aktualisiert werden, abhängig von numOfIterations.
for (int a = 0; a < numOfIterations; ++a) // wir müssen die Simulation "numOfIterations" Mal iterieren.
{
constantVelocity->operate(dt); // IteriereconstantVelocity Simulation um dt Sekunden.
motionUnderGravitation->operate(dt); // Iteriere motionUnderGravitation Simulation um dt Sekunden.
massConnectedWithSpring->operate(dt); // Iteriere massConnectedWithSpring Simulation um dt Sekunden.
}
}
Fangen wir damit an, die Applikation zu schreiben:
1. Masse mit konstanter Geschwindigkeit
* class ConstantVelocity : public Simulation ---> Ein Simulation Objekt, das eine Masse mit konstanter Geschwindigkeit erzeugt.
Masse mit konstanter Geschwindigkeit benätigt keine externe Kraft. Wir müssen nur 1 Masse erzeugen und dessen Geschwindigkeit auf (1.0f, 0.0f, 0.0f) setzen, so dass sie sich in die X-Richtung mit einer Geschwindigkeit von 1 m/s bewegt. Wir werden eine Klasse von Simulation ableiten. Diese Klasse ist "class ConstantVelocity":
class ConstantVelocity : public Simulation
{
public:
ConstantVelocity() : Simulation(1, 1.0f) // Konstruktor erzeugt als erstes seine Basisklasse mit 1 Masse und 1 Kg.
{
masses[0]->pos = Vector3D(0.0f, 0.0f, 0.0f); // Eine Masse wurde erzeugt und wir setzen seine Position auf den Ursprung.
masses[0]->vel = Vector3D(1.0f, 0.0f, 0.0f); // Wir setzen die Massengeschwindigkeit auf (1.0f, 0.0f, 0.0f) m/s.
}
};
Wenn die operate(float dt) Methode der ConstantVelocity Klasse aufgerufen wird, berechnet diese den nächsten Status der Masse. Diese Methode wird von der Haupt-Applikation aufgerufen, vor jedem neu-zeichnen des Fensters. Sagen wir, dass Ihre Applikation mit 10 Frames pro Sekunde läuft. Wenn die exakte Zeitänderung operate(float dt) bei jedem Frame übergeben wird, dann wäre dt gleich 0.1 Sekunden. Wenn die Simulation simulate(float dt) der Masse aufruft, wird die neue Position um die Geschwindigkeit * dt inkrementiert, das wäre:
Vector3D(1.0f, 0.0f, 0.0f) * 0.1 = Vector3D(0.1f, 0.0f, 0.0f)
Bei jeder Iteration bewegt sich die Masse um 0.1 Meter nach rechts. Nach 10 Frames wird diese sich 1.0 Meter nach rechts bewegt haben. Die Geschwindigkeit war 1.0 m/s und bewegt sich 1.0 Meter in einer Sekunde. War das ein Zufall oder ein logisches Ergebnis? Wenn Sie die Frage nicht beantworten können, verbringen Sie etwas Zeit damit, über die obige Relation nachzudenken.
Wenn Sie die Applikation laufen lassen, werden Sie sehen, dass die Masse mit der konstanten Geschwindigkeit sich in die X-Richtung bewegt. Die Applikation hat zwei Modi des Bewegungsablaufs. Indem Sie F2 drücken, erhalten Sie den Zeitablauf parallel zur realen Welt. Und indem Sie F3 drücken, erhalten Sie den Zeitfluss 10 mal langsamer als die reale Welt. Auf dem Bildschirm werden Sie Linien sehen, die die Koordinaten-Ebenen repräsentieren. Der Abstand zwischen diesen Linien ist 1 Meter. Durch die Verwendung dieser Linien können Sie beobachten, dass sich die Masse um 1 Meter in der Sekunde, wenn der reale Zeit Modus gesetzt ist. Und im langsamen Modus bewegt sie sich um 1 Meter in 10 Sekunden. Die oben beschriebene Technik ist eine übliche, um Real-Time Simulationen parallel zur realen Welt Zeit ablaufen zu lassen. Um diese Technik zu verwenden, müssen Sie sich auf jeden Fall entschieden haben, welche Einheiten Sie in Ihrer Simulation verwenden wollen.
Kräfte anwenden:
In der konstanten Geschwindigkeits Simulation, haben wir keine Kräfte auf die Masse angewendet. Da wir wissen, dass wenn eine Kraft auf einen Körper wirkt, dieser beschleunigt. Wenn wir beschleunigte Bewegung haben wollen, wenden wir Kräfte an. In einer Operation der Simulation wenden wir Kräfte in der "solve" Methode an. Wenn die Operation zur "simulate" Phase kommt, resultiert die netto Kraft aus der Summe aller Kräfte. Die netto Kraft bestimmt die Bewegung.
Sagen wir, dass Sie 1 N Kraft auf die Masse in X-Richtung anwenden wollen. Dann sollten Sie:
mass->applyForce(Vector3D(1.0f, 0.0f, 0.0f));
in der "solve" Methode schreiben. Wenn Sie eine weitere Kraft, sagen wir 2 N in die Y-Richtung anwenden wollen, sollten Sie:
mass->applyForce(Vector3D(1.0f, 0.0f, 0.0f));
mass->applyForce(Vector3D(0.0f, 2.0f, 0.0f));
in der "solve" Methode schreiben. Sie können jede Kraft in jeglichem Weg hinzufügen und Sie erhalten Bewegung. In der nächsten Applikation werden Sie eine einzelne Kraft in einer formulierten Art sehen.
2. Masse unter Schwerkraft
* class MotionUnderGravitation : public Simulation ---> Ein Simulation Objekt, das eine Masse erzeugt, die sich unter Schwerkraft bewegt.
Die MotionUnderGravitation Klasse erzeugt eine Masse und wendet eine Kraft auf sie an. Diese Kraft ist die Schwerkraft. Schwerkraft ist gleich der Masse mal der Gravitationsbeschleunigung:
F = m * g
Gravitationsbeschleunigung ist die Beschleunigung eines freien Körpers. Auf der Erde erhält ein Objekt, was Sie falle lassen, eine Geschwindigkeit von 9.81 m/s jede Sekunde, so lange bis es einer anderen Kraft als der Schwerkraft ausgesetzt wird. Deshalb ist die Gravitationsbeschleunigung konstant für alle Massen auf der Erde und diese ist 9.81 m/s/s. (Das ist unabhängig von der Masse. Alle Massen fallen mit der selben Beschleunigung.)
Die MotionUnderGravitation Klasse hat folgenden Konstruktor:
class MotionUnderGravitation : public Simulation
{
Vector3D gravitation; // Die Gravitationsbeschleunigung.
MotionUnderGravitation(Vector3D gravitation) : Simulation(1, 1.0f) // Der Konstruktor erzeugt als erste seine Basis-Klasse mit 1 Masse und 1 Kg.
{ // Vector3D Gravitation, ist die Gravitationsbeschleunigung.
this->gravitation = gravitation; // Setze die Schwerkraft der Klasse.
masses[0]->pos = Vector3D(-10.0f, 0.0f, 0.0f); // Setze die Position der Masse.
masses[0]->vel = Vector3D(10.0f, 15.0f, 0.0f); // Setze die Geschwindigkeit der Masse.
}
...
Der Konstruktor wird Vector3D gravitation übergeben, was die Gravitationsbeschleunigung ist und die Simulation benutzt diese Kraft, um sie anzuwenden.
virtual void solve() // Schwerkraft wird angewandt, weshalb wir eine "Solve" Methode benötigen.
{
for (int a = 0; a < numOfMasses; ++a) // Wir werden die Kraft auf alle Massen anwenden (tatsächlich haben wir 1 Masse, aber das können wir in Zukunft weiter ausbauen).
masses[a]->applyForce(gravitation * masses[a]->m); // Schwerkraft ist F = m * g. (Masse mal die Gravitationsbeschleunigung).
}
Im obigen Code sollten Sie die Kraft Formel als F = m * g sehen. Die Applikation erzeugt MotionUnderGravitation mit einemVector3D Wert als "Vector3D(0.0f, -9.81f, 0.0f)". -9.81 bedeutet die Beschleunigung in die negative Y-Richtung, so dass die Masse herunterfällt, wenn Sie sie im Fenster anzeigen. Lassen Sie die Applikation laufen und schauen Sie, was passiert. Benutzen Sie 9.81 statt -9.81 und beobachten Sie den Unterschied.
3. Masse verbunden durch eine Feder mit einem fixen Punkt
* class MassConnectedWithSpring : public Simulation ---> Ein Simulation Objekt, das eine Masse erzeugt, die durch eine Feder mit einem fixen Punkt verbunden ist.
In diesem Beispiel wollen wir eine Masse mit einem fixen Punkt mittels einer Feder verbinden. Die Feder sollte die Masse in die Richtung des Fixpunktes bewegen, so dass sie hin und her schwingt. In dem Konstruktor, MassConnectedWithSpring, wird der Fixpunkt und die Position der Masse gesetzt.
class MassConnectedWithSpring : public Simulation
{
public:
float springConstant; // Je größerspringConstant, desto steifer die Federkraft.
Vector3D connectionPos; // der willkürliche Fixpunkt, mit dem die Masse verbunden ist.
MassConnectedWithSpring(float springConstant) : Simulation(1, 1.0f) // Konstruktor erzeugt als erstes seine Basisklasse mit 1 Masse und 1 Kg.
{
this->springConstant = springConstant; // Setze springConstant.
connectionPos = Vector3D(0.0f, -5.0f, 0.0f); // Setze connectionPos.
masses[0]->pos = connectionPos + Vector3D(10.0f, 0.0f, 0.0f); // Setze diePosition der Masse 10 Meter rechts von connectionPos.
masses[0]->vel = Vector3D(0.0f, 0.0f, 0.0f); // Setze die Geschwindigkeit der Masse auf null.
}
...
Die Geschwindigkeit der Masse wird auf null gesetzt und wird 10 Meter rechts von connectionPos positioniert, so dass sie anfangs nach links bewegt wird. Die Kraft einer Feder ist wie folgt formuliert:
F = -k * x
k ist ein Wert um die Steifheit der Feder zu repräsentieren. Und x ist die Distanz von der Masse zum Fixpunkt. Das negative Vorzeichen in der Formel indiziert, dass dies eine anziehende Kraft ist. Wenn es positiv wäre, würde die Feder die Masse vorwärts stoßen, was nicht gerade das wäre, was wir erwarten würden.
virtual void solve() // die Federkraft wird angewendet.
{
for (int a = 0; a < numOfMasses; ++a) // Wir werden die Kraft auf alle Massen anwenden (tatsächlich haben wir 1 Masse, aber das können wir in Zukunft weiter ausbauen).
{
Vector3D springVector = masses[a]->pos - connectionPos; // Finde einen Vektor von der Position der Masse zu connectionPos.
masses[a]->applyForce(-springVector * springConstant); // wende die Kraft basierend auf der bekannten Federkraft Formel an.
}
}
Die Federkraft im obigen Code ist die selbe wie in der Feder Formel (F = -k * x). Hier benutzen wir stat x einen Vector3D, da wir 3D Raum verwenden wollen. "springVector" enthält den Unterschied zwischen der Massenposition und connectionPos und springConstant steht für k. Je größer der Wert von springConstant ist, desto größer die Kraft und so schneller schwingt die Masse.
In diesem Tutorial, habe ich versucht, die grundlegenden Konzepte von physikalischen Simulationen aufzuzeigen. Wenn Sie Interesse an Physik haben, werden Sie keine Probleme haben, selbst neue Simulationen zu schreiben. Sie können komplizierte Interaktion ausprobieren und sehr attraktive Demos und Spiele erhalten. Die nächsten zu unternehmenden Schritte wären Simulationen von festen Körpern, einfache Mechanismen und fortgeschrittene Simulationsmethoden.
Für irgendwelche Kommentare oder Fragen kontaktieren Sie mich bitte:
Erkin Tunca (erkintunca@icqmail.com)
Jeff Molofee (NeHe)
* DOWNLOAD Visual C++ Code für diese Lektion.
* DOWNLOAD Borland C++ Builder 6 Code für diese Lektion. ( Conversion by Le Thanh Cong )
* DOWNLOAD Code Warrior 5.3 Code für diese Lektion. ( Conversion by Scott Lupton )
* DOWNLOAD Delphi Code für diese Lektion. ( Conversion by Michal Tucek )
* DOWNLOAD Dev C++ Code für diese Lektion. ( Conversion by Warren Moore )
* DOWNLOAD Linux/GLut Code für diese Lektion. ( Conversion by Laks Raghupathi )
* DOWNLOAD Visual Studio .NET Code für diese Lektion. ( Conversion by Grant James )
Deutsche Übersetzung: Joachim Rohde
Der original Text ist hier zu finden.
Die original OpenGL Tutorials stammen von NeHe's Seite.