Waldbrand – jetzt fackeln wir alles ab
Im neuen C++ ClanLib Projekt Waldbrand können wir nun endlich mal der Zerstörung freien Lauf lassen und einen ganzen Wald abfackeln. Diese Simulation zeigt die Ausbreitung von Feuer.
Waldbrand- jetzt fackeln wir alles ab
Im letzten Beispiel zur Sanduhr habe ich euch recht ausführlich gezeigt wie man eine einfache Simulation mit der ClanLib Engine erstellt. Nun wird es Zeit dieses Wissen zu festigen, am besten gleich mit einem neuen Beispiel. Die Simulation des Waldbrands unterscheidet sich nur minimal von jener der Sanduhr. Zu finden ist der Source Code wie immer auf meiner GitHub Seite. Ja, tatsächlich! Auch hier gibt es ein zweidimensionales Array von Pixel die unterschiedliche Zustände haben können (Wald, Feuer oder leer). Basierend auf Regeln ändern sich diese im Laufe der Simulation.
Simulation Basics
In einer Simulation versucht man am PC beobachtbare Vorgänge aus der Natur nachzubauen. Die Wissenschaft sagt, auf eine Aktion folgt immer eine Reaktion und diese lassen sich aus einfachen Regeln ableiten. Kennt man einige dieser Regeln, dann lässt sich in einer Simulation bereits zeigen wie sich der Zustand eines Systems unter Anwendung dieser verändert. Solange das Ergebnis nicht zu den Beobachten passen weiß man: die Regeln sind noch nicht korrekt oder unvollständig. Je genauer man die Vorgänge identifiziert und implementiert, desto realistischer wird das Ergebnis der Simulation.
Konkretes Beispiel
Ein Waldbrand ist eines dieser Vorgänge in der Natur. So ein Feuer basiert auf zahlreichen Regeln und Voraussetzungen. In unserer kleinen Simulation werden wir das nur sehr rudimentär umsetzen. Aber selbst bei diesen sehr simplen Regeln zeigt sich schon ein recht realistisches Bild von Feuer, das sich über einen Wald bewegt. Das fertige Programm in Aktion:
Das Programm verwendet den identischen Aufbau der Sanduhr. Alle ClanLib spezifischen Erklärungen sind aus diesem Artikel zu entnehmen. Es gibt lediglich Anpassungen an der eigenen Simulation. In der globals.h sind ähnliche, aber andere Konstanten angelegt. Die interessantesten sind:
#define MIN 0 // min and max value for random number generator #define MAX 10000 #define PROPABILITY_WOOD 4500 // possibility for wood #define PROPABILITY_FIRE 10 // possibility for fire #define PROPABILITY_NEW_WOOD 100 // percent if new field is wood #define PROPABILITY_NEW_FIRE 5 // percent if new field is fire (flying sparks) enum Fieldvalue { EMPTY = 0, WOOD = 1, FIRE = 2 };
Die Definition von Konstanten erlaubt später eine einfachere Anpassung der Simulation. Ideal wäre, wenn diese in einer *.ini Datei stehen, damit nicht neu kompiliert werden muss (kleine Aufgabe für die fleißigen unter euch). MIN und MAX definieren die Spanne in der Zufallszahlen generiert werden. Dieser resultierende Zufallswert wird direkt mit PROPABILITY_NEW_WOOD und PROPABILITY_NEW_FIRE verglichen, d.h. diese 4 Werte legen fest was mit einem leeren Feld passiert. Die Werte PROPABILITY_WOOD und PROPABILITY_FIRE legen das initiale Aussehen vom Board fest.
Simulationszustand
Der aktuelle Zustand der Simulation wird über das bereits erwähnte zweidimensionale Array abgebildet. In diesem dreidimensionalen Array werden 2 zweidimensionale Arrays gespeichert:
unsigned char _wood[2][WOOD_WIDTH][WOOD_HEIGHT];
je nach Index wird eines der beiden Arrays gerade angezeigt und das andere neu berechnet. Der initiale Zustand wird in der init() Methode erstellt.
Simulationsregeln
Die Regeln für die einzelnen Pixel des Boards sind recht einfach zusammengefasst und in der compute() Methode implementiert:
- EMPTY
ein leeres Pixel ändert den Zustand abhängig von einer Zufallszahl zu einem WOOD Pixel oder bleibt einfach leer - WOOD
wenn das Nachbarfeld brennt, dann fängt auch dieses Feld zu brennen an. Gibt es kein feuer in der nähe bleibt es ein WOOD Pixel, beziehungsweise es besteht eine minimale Chance, dass es durch Funkenflug zu brennen beginnt. - FIRE
ein brennendes Feld wird zu einem leeren Feld.
Die Regel mit dem Funkenflug ist eine kleine Schwindelei von uns Programmierern. Testet das Programm einfach mal ohne. Früher oder später wird das Feuer verschwunden sein und jedes Pixel mit Wald bewachsen sein. Die Natur findet immer einen Weg! Mit dem Funkenflug läuft die Simulation ewig.
Das Zeichnen des Boards in der render() Methode ist uninteressant, diese funktioniert exakt wie im Sanduhr Beispiel, nur mit anderen Farben.
Fazit
Die C++ ClanLib Waldbrand Simulation zeigt bereits einen praxisnähere Anwendung der Engine. Es ist möglich komplexere Simulationen umzusetzen und eine einfach grafische Ausgabe dafür zu schreiben. Interessant ist es, wenn man solche Simulationen über Parameter von außen steuern und im Idealfall auch gleich analysieren kann. Willkommen in der faszinierenden Welt der Simulations und Spieleprogrammierer, denn der Weg zum Spiel ist nur noch ein kleiner. Wir müssten lediglich noch Benutzereingaben einfügen auf welche die Simulation in Echtzeit reagiert. Zusätzlich benötigen wir auch noch eine Zielbedingung.
Gerne darf der Code verbessert und in eigene Projekte eingebaut werden.