Feuerwerk in C++
In diesem Artikel zeige ich euch wie man ein Feuerwerk in C++ programmiert. Passend zum Raketentag (so nennt meine Tochter Silvester). Dafür werden wir uns eine eigene kleine Physik Engine programmieren. Dargestellt wird das Ergebnis wie auch bei meinen KI Artikeln in der ClanLib Engine.
Feuerwerk in C++
Im letzten Beispiel eSheep habe ich als Abschlussarbeit alle erlernten Algorithmen und Techniken der Lehrveranstaltung künstliche Intelligenz eingebaut. Nun startet mit Physik ein ganz neues Abenteuer, wir können dafür aber auch vieles bereits verwendete aufbauen. Für die Visualisierung verwende ich weiter die leistungsfähige ClanLib 2D Engine. Alle weiteren Beispiele zum Thema Physik werden eine kleine selbst entwickelte Physik Engine verwenden. Diese wird nun mit dem Feuerwerk Beispiel zum ersten Mal eingeführt und wird hier im Detail erklärt.
Physik Engine in C++
Wie immer findet man den Source Code auf meiner GitHub Seite. Die Physik Engine wird in den Dateien mit dem Anfang phy_ im Dateinamen repräsentiert. In diesem Beispiel benötigen wir nur eingeschränkte Funktionen die in einer Partikel Engine umgesetzt werden. Die Partikel Engine verwendet dabei Basis Funktionalität einer Physik Engine. Wir benötigen:
- Partikel
phy_particle.h definiert einen Partikel. Dieser besitzt neben einer Position im 2D Raum noch eine Geschwindigkeit (2D Vektor), eine Masse und eine Kraft die darauf wirkt. In den Methoden setForce, resetForce und addForce werden beliebige Kräfte auf diesen Partikel angewendet. Die Methode integrate wendet diese Kraft auf den Partikel an. Über die Formel Kraft / Masse bekommen wir eine resultierende Geschwindigkeit in eine Richtung. - Kräfte
phy_forcegenerator.h ist die Basisklasse der Kräfte. über dieses Interface kann man Partikel in den Methoden registerClass und deregisterClass registrieren oder entfernen. Über die Methode compute wird die Kraft auf alle registrierten Partikel angewendet. Konkret ausprogrammiert sind diese Methoden in den abgeleiteten Klassen phy_gravity (für die Erdanziehungskraft), phy_impulse (für den initialen Impuls der Feuerwerksrakete) und phy_explosion (für die Explosion der Rakete). Diese Implementierungen unterscheiden sich nur minimal von einander.
In der globals.h Datei sind zahlreiche Konstanten für die Simulation definiert. Das sind unter anderem:
- PARTICLE_NUMBER
die Anzahl der neuen Partikel die bei der Explosion entstehen - MAX_TIEFE
die Anzahl an möglichen Folgeexplosionen - MAX_LIFETIME_MAX / MAX_LIFETIME_MIN
die maximale und minimale Lebenszeit der Partikel. Durch diese Variation entstehen realistischer Explosionen - START_IMPULSE
die Stärke der Impluskraft beim Abschuss des Feuerwerks - EXPLOSION_MAX / EXPLOSION_MIN
die maximale und minimale Stärke der Impulskraft auf die Partikel die bei der Explosion entstehen. Auch hier ist diese Variation nötig damit das Ergebnis realistischer aussieht. - GRAVITY_EARTH
die Erdanziehungskraft, damit die Partikel wieder zurück auf den Boden fallen.
In der App Klasse ist das Beispiel ausprogrammiert. Neben dem Handling der ClanLib Engine ist folgender Code enthalten. Zuerst wird in der Erkennung der Mauseingabe bei einem Klick ins Fenster von der Position weg eine neue Rakete gestartet. Auf diese werden zwei Kräfte angewendet: die Erdanziehungskraft und der Startimpuls vom Abschuss:
FireworksParticle *f = getFireworksParticle(static_cast<float>(x), static_cast<float>(y), MAX_LIFETIME_MAX, PARTICLE_SIZE, 0, TIMER); _gravitation->registerClass(f); _startimpuls->registerClass(f);
Die Rakete geht nun auf die Reise und wird in der App::render Methode gezeichnet. Dort wird auch geprüft ob der Partikel noch am leben ist. Falls nicht wird er entfernt. Falls nicht wird geprüft, ob es zu einer Explosion gekommen ist. Falls ja werden neue Partikel mit zufälligen Farben erzeugt:
for (int i = 0; i < PARTICLE_NUMBER; ++i) { FireworksParticle *f = getFireworksParticle(x, y, static_cast(irand(MAX_LIFETIME_MIN, MAX_LIFETIME_MAX)), size / 2, tiefe, (*it)->_maxtimer); f->_speed._x = (*it)->_speed._x; f->_speed._y = (*it)->_speed._y; f->_r = irand(0, 255) / 255.0f; f->_b = irand(0, 255) / 255.0f; f->_g = irand(0, 255) / 255.0f; //Physik _gravitation->registerClass(f); _explosion->registerClass(f); }
Auch diese Artikel werden für die Kräfte registriert. Je nach Rechner merkt man bei sehr vielen erzeugten Artikeln einen Einbruch in der Geschwindigkeit der Berechnung. Physik bedarf immer zahlreicher Berechnungen und sollte nur sparsam verwendet werden.
Fazit
Ein Feuerwerk ist ein sehr simples Programm in dem man die ersten Erfahrungen mit einer Physik Engine machen kann. In dieser Simulation erzeugt man ein realistisch wirkendes Feuerwerk durch zufällig erzeugter Punkte die gezeichnet werden. Die gesamte Dynamik wird durch physikalischen Berechnungen von Kräften erzeugt die auf diese Partikel wirken können. Obwohl wir nur Punkte zeichnen ergibt sich ein recht lebendiges verhalten das durchaus nahe an echtes Feuerwerk kommt. Physik macht Simulationen und Spiele realistisch – falls diese Berechnungen zu extrem oder schlicht falsch sind entstehen recht witzige Szenen.