Hier werden die Unterschiede zwischen zwei Versionen gezeigt.
Beide Seiten der vorigen Revision Vorhergehende Überarbeitung Nächste Überarbeitung | Vorhergehende Überarbeitung | ||
ws1718:visualisierungscode [2018/04/07 17:25] lorenztu [Turtle als Test-Tool] |
ws1718:visualisierungscode [2018/04/18 18:53] (aktuell) lenarost [Game loop] |
||
---|---|---|---|
Zeile 20: | Zeile 20: | ||
==== Turtle als Test-Tool ==== | ==== Turtle als Test-Tool ==== | ||
- | Wie schon angesprochen können mit Turtle viele Ideen **umgesetzt** werden bevor sie in komplizierteren Visualisierungen, wie Pygame, eingesetzt werden. Wenn wir also unsere Pygame-Visualisierung Flexibler machen wollen, versuchen wir zuerst Turtle so flexibel wie möglich zu gestalten. So kann Turtle bereits mit allen Situationen einer einzelnen Kreatur umgehen oder passt jede Position vom Ziel an das Fenster an etc. Ebenfalls nutzt Turtle bereits den **Koordinatenumrechner** vom Hauptcode, welcher ansonsten noch keinen Einsatz zeigen konnte, obwohl er eine wichtige Rolle in Flexibilität spielt. | + | Wie schon angesprochen können mit Turtle viele Ideen **umgesetzt** werden bevor sie in komplizierteren Visualisierungen, wie Pygame, eingesetzt werden. Wenn wir also unsere Pygame-Visualisierung **flexibler** machen wollen, versuchen wir zuerst Turtle so flexibel wie möglich zu gestalten. So kann Turtle **schon jetzt** mit allen Situationen einer einzelnen Kreatur umgehen oder passt jede Position vom Ziel an das Fenster an etc. Ebenfalls nutzt Turtle bereits den **Koordinatenumrechner** vom Hauptcode, welcher ansonsten noch keinen Einsatz zeigen konnte, obwohl er eine wichtige Rolle in Flexibilität spielt. |
- | Der Koordinatenumrechner ist eine Klasse, in die sämtliche Umrechnungsfunktionen hinein programmiert werden können. Die für Turtle wichtige Funktion ist dabei ''sim2turt''! Es werden dabei alle Simulationskoordinaten für die Turtle-Visualisierung umgerechnet, damit, egal wie groß das Turtlefenster ist, immmer alle Zeichnungen wie Interface und Kreaturen auf das Fenster **abgestimmt** sind. Dazu berechnet der Umrechner Variablen mit ein, wie die Fenstergrößen oder einem ''zoom''-Faktor. Um die Flexibilität zu erhöhen, kann der Umrechner sowohl mit Tupeln als auch mit zwei Variablen als Input umgehen und beherrscht **mehrere Funktionen**. Neben dem typischen Koordinatenumrechnen kann er, wenn z.B ''function=distance'' übergeben wird, auch andere Dinge berechnen, wie die im Beispiel die umgerechneten Distanzen für Turtle. | + | Der Koordinatenumrechner ist eine Klasse, in die sämtliche Umrechnungsfunktionen hinein programmiert werden können. Die für **Turtle wichtige Funktion** ist dabei ''sim2turt''! Es werden dabei alle Simulationskoordinaten für die Turtle-Visualisierung umgerechnet, damit, egal wie groß das Turtlefenster ist, immmer alle Zeichnungen wie Interface und Kreaturen auf das Fenster **abgestimmt** sind. Dazu berechnet der Umrechner Variablen mit ein, wie die Fenstergrößen oder einem ''zoom''-Faktor. Um die Flexibilität zu erhöhen, kann der Umrechner sowohl mit Tupeln als auch mit zwei Variablen als Input umgehen und beherrscht **mehrere Funktionen**. Neben dem typischen Koordinatenumrechnen kann er, wenn z.B ''function=distance'' übergeben wird, auch andere Dinge berechnen, wie die im Beispiel die umgerechneten Distanzen für Turtle. |
+ | |||
+ | ---- | ||
===== Pygame ===== | ===== Pygame ===== | ||
Zeile 128: | Zeile 130: | ||
Wie bei jeder anderen Klasse auch in pygame, wird der **CREA Klasse** zunächst ein Pygame Sprite übergeben. In der ''<nowiki>__init__</nowiki>''-Funktion wird diese obligatorisch selbst initialisiert in der ersten Zeile. \\ | Wie bei jeder anderen Klasse auch in pygame, wird der **CREA Klasse** zunächst ein Pygame Sprite übergeben. In der ''<nowiki>__init__</nowiki>''-Funktion wird diese obligatorisch selbst initialisiert in der ersten Zeile. \\ | ||
Zudem wird der ''<nowiki>__init__</nowiki>''-Funktion ein Variable spec zugewiesen. Diese Variable beinhaltet die Information, welche **Spezie** realisiert werden soll. Dazu greifen wir auf die Klasse Creature des Hauptcodes zu und erhalten alle **nötigen Eigenschaften der Kreatur.** \\ | Zudem wird der ''<nowiki>__init__</nowiki>''-Funktion ein Variable spec zugewiesen. Diese Variable beinhaltet die Information, welche **Spezie** realisiert werden soll. Dazu greifen wir auf die Klasse Creature des Hauptcodes zu und erhalten alle **nötigen Eigenschaften der Kreatur.** \\ | ||
- | Es folgt die Zeile "''self.c1.appendWay()''". Diese Funktion der Creature Klasse des Hauptcodes ist dafür zuständig **neue Schritte für die Kreatur zu generieren**. Beim Erstellen der Kreatur werden hier direkt eine gewisse Anzahl Schritte erstellt. Die eigentliche Bewegung findet erst in der update-Funktion statt.\\ | + | Es folgt die Zeile ''self.c1.appendWay()''. Diese Funktion der Creature Klasse des Hauptcodes ist dafür zuständig **neue Schritte für die Kreatur zu generieren**. Beim Erstellen der Kreatur werden hier direkt eine gewisse Anzahl Schritte erstellt. Die eigentliche Bewegung findet erst in der update-Funktion statt.\\ |
Anschließend ordnen wir jeder Kreatur ein **Spezies abhängiges Bild zu **( "''self.image = all_images[spec]''"). Somit erreichen wir zusätzlich einen visuellen Unterschied der Kreaturen gemäß ihrer Art. \\ | Anschließend ordnen wir jeder Kreatur ein **Spezies abhängiges Bild zu **( "''self.image = all_images[spec]''"). Somit erreichen wir zusätzlich einen visuellen Unterschied der Kreaturen gemäß ihrer Art. \\ | ||
- | Die Zeilen, die mit "''self.rect''" beginnen, umgeben das **Bild mit einem rectangle** und positionieren dieses. Das Prinzip ist das selbe, wie in den Klassen der Hindernisse. \\ | + | Die Zeilen, die mit ''self.rect''beginnen, umgeben das **Bild mit einem rectangle** und positionieren dieses. Das Prinzip ist das selbe, wie in den Klassen der Hindernisse. \\ |
- | Für die nächste Funktion der Klasse ist die letzte Zeile der ''<nowiki>__init__</nowiki>''-Funktion wichtig. Sie liefert uns eine Variable, die später einen Index- Counter bildet. Um diese genauer nach zu vollziehen werfen wir einen Blick auf die update-Funktion der CREA-Klasse. \\ | + | Für die nächste Funktion der Klasse ist die letzte Zeile der ''<nowiki>__init__</nowiki>''-Funktion wichtig. Sie liefert uns eine Variable, die später einen Index- Counter bildet. Um diese genauer nach zuvollziehen werfen wir einen Blick auf die update-Funktion der CREA-Klasse. \\ |
- | Die **update-Funktion** der CREA-Klasse ist vor allem wichtig für die **Bewegung unserer erzeugten Kreaturen**. Hier kommt der "''self.block_index''" -Counter zum Einsatz. Zunächst stellen wir hier eine Bedingung auf. Sie bewirkt, dass die Bewegung der Kreatur nur solange von statten geht, wie Schritte (gespeichert im briefing) vorhanden sind. Ansonsten stirbt die Kreatur.\\ | + | Die **update-Funktion** der CREA-Klasse ist vor allem wichtig für die **Bewegung unserer erzeugten Kreaturen**. Hier kommt der ''self.block_index'' -Counter zum Einsatz. Zunächst stellen wir hier eine Bedingung auf. Sie bewirkt, dass die Bewegung der Kreatur nur solange von statten geht, wie Schritte (gespeichert im briefing) vorhanden sind. Ansonsten stirbt die Kreatur.\\ |
Auf Grund der Strukturierung des ** briefings **arbeiten wir hier mit zwei Indizier. Das briefing besteht aus einer **Liste von Blocks**. Ein Block beschreibt einen Schritt. Die Blocks enthalten Informationen über die Richtung und Schrittlänge. \\ | Auf Grund der Strukturierung des ** briefings **arbeiten wir hier mit zwei Indizier. Das briefing besteht aus einer **Liste von Blocks**. Ein Block beschreibt einen Schritt. Die Blocks enthalten Informationen über die Richtung und Schrittlänge. \\ | ||
- | Zunächst speichern wir in der Variable "''block''" den **aktuellen Schritt** ab, der jetzt ausgeführt werden soll. Damit beim nächsten Durchlauf der nächste Schritt ausgeführt wird, erweitern wir den "''self.block_index''"-Counter um 1. \\ | + | Zunächst speichern wir in der Variable ''block''den **aktuellen Schritt** ab, der jetzt ausgeführt werden soll. Damit beim nächsten Durchlauf der nächste Schritt ausgeführt wird, erweitern wir den ''self.block_index''-Counter um 1. \\ |
- | Dann greifen wir auf eine weitere Funktion der Creature-Klasse des Hauptcodes zu. "''nextStep(block)''" ist dafür zuständig die Kreatur **einen Schritt ausführen** zu lassen. Alles was sie dazu braucht sind die Informationen über den Schritt. Diese übergeben wir anhand der "''block''" – Variable, die eben diese Informationen enthält. \\ | + | Dann greifen wir auf eine weitere Funktion der Creature-Klasse des Hauptcodes zu. "''nextStep(block)''" ist dafür zuständig die Kreatur **einen Schritt ausführen** zu lassen. Alles was sie dazu braucht sind die Informationen über den Schritt. Diese übergeben wir anhand der ''block'' – Variable, die eben diese Informationen enthält. \\ |
- | Um die Kreatur auch **visuell zu bewegen**, müssen die Koordinaten des rects bewegt werden. Hier setzen wir dieses einfach auf die **neuen x- und y-Koordinaten der Kreatur**. Diese Informationen erhalten wir ebenfalls aus dem Hauptcode. ("''self.rect.center = self.c1.stats[“pos“][0]+100, self.c1.stats[“pos“][1]+360''")\\ | + | Um die Kreatur auch **visuell zu bewegen**, müssen die Koordinaten des rects bewegt werden. Hier setzen wir dieses einfach auf die **neuen x- und y-Koordinaten der Kreatur**. Diese Informationen erhalten wir ebenfalls aus dem Hauptcode. (''self.rect.center = self.c1.stats[“pos“][0]+100, self.c1.stats[“pos“][1]+360'')\\ |
- | Zusätzlich kommt hier der "''stepCounter"'' zur Sprache. Die Liste der Schritte(briefing) wird immer **schrittweise verlängert**. Wenn der "''stepCounter''" **geringer als die movecap und die Kreatur nicht als tot gemeldet** wurde (zum Beispiel durch Kollision), wird erneut die Funktion "''appendWay()'''" der Creature-Klasse des Hauptcodes verwendet. Diese **fügt immer neue Schritte zum briefing hinzu**, solange die Bedingungen erfüllt sind.\\ | + | Zusätzlich kommt hier der ''stepCounter'' zur Sprache. Die Liste der Schritte(briefing) wird immer **schrittweise verlängert**. Wenn der ''stepCounter''**geringer als die movecap wird und die Kreatur nicht als tot gemeldet** wurde (zum Beispiel durch Kollision), wird erneut die Funktion ''appendWay()''' der Creature-Klasse des Hauptcodes verwendet. Diese **fügt immer neue Schritte zum briefing hinzu**, solange die Bedingungen erfüllt sind.\\ |
Nach dieser Bedingung finden wir noch zwei weitere Bedingungen in der update-Funktion. Diese **überprüfen mit jedem Durchlauf die Koordinaten der Kreaturen**. Wenn diese über den **Bildschirmrand hinaus** sind, werden die Kreaturen auf dead gesetzt und sterben. | Nach dieser Bedingung finden wir noch zwei weitere Bedingungen in der update-Funktion. Diese **überprüfen mit jedem Durchlauf die Koordinaten der Kreaturen**. Wenn diese über den **Bildschirmrand hinaus** sind, werden die Kreaturen auf dead gesetzt und sterben. | ||
Zeile 151: | Zeile 153: | ||
====Funktionen...==== | ====Funktionen...==== | ||
===...zum Erstellen der restlichen Objekte === | ===...zum Erstellen der restlichen Objekte === | ||
- | Wolke, Plattformen und Tropfen werden alle gleich erstellt. Jedes Objekt wird durch **Aufrufen der Klasse** und **Angabe der Koordinaten** erstellt. Daraufhin wird es zunächst der allgemeinen **Sprite_Gruppe** und der **Hinderniss-Gruppe** zu geordnet. | + | Wolke, Plattformen und Tropfen werden alle gleich erstellt. Jedes Objekt wird durch **Aufrufen der Klasse** und **Angabe der Koordinaten** erstellt. Daraufhin wird es zunächst der allgemeinen **Sprite_Gruppe** und der **Hinderniss-Gruppe** zugeordnet. |
===...für die Kreaturen=== | ===...für die Kreaturen=== | ||
==bubbly(): | ==bubbly(): | ||
- | Die ''bubbly()''-Funktion ist zuständig für die Kollision. Das Prinzip ist eine simple** hit-box-Abfrage**. Mit Hilfe des "''spritecollide()''"- Befehls wird für jedes Objekt in der Kreaturen-Klasse eine Kollisionsabfrage mit jedem Objekt der Hinderniss-Klasse gemacht. Falls die Abfrage positiv sein sollte, wird die Kreatur auf **dead** gesetzt. | + | Die ''bubbly()''-Funktion ist zuständig für die Kollision. Das Prinzip ist eine simple** hit-box-Abfrage**. Mit Hilfe des ''spritecollide()''- Befehls wird für jedes Objekt in der Kreaturen-Klasse eine Kollisionsabfrage mit jedem Objekt der Hinderniss-Klasse gemacht. Falls die Abfrage positiv sein sollte, wird die Kreatur auf **dead** gesetzt. |
==newGen(n) == | ==newGen(n) == | ||
Nun kommen wir zu einer äußerst wichtigen Funktion der Simulation. | Nun kommen wir zu einer äußerst wichtigen Funktion der Simulation. | ||
Als erstes fügen wir an dieser Stelle **zwei Counter** ein, die in der Visualisierung mit abgebildet werden. \\ | Als erstes fügen wir an dieser Stelle **zwei Counter** ein, die in der Visualisierung mit abgebildet werden. \\ | ||
- | Der erste Counter ("''gencnt''") zählt die **Generationen** mit und wird dementsprechend bei jedem Abruf der Funktion um 1 erweitert. \\ | + | Der erste Counter (''gencnt'') zählt die **Generationen** mit und wird dementsprechend bei jedem Abruf der Funktion um 1 erweitert. \\ |
- | Der zweite Counter ("''ratingcnt''") wird bei jedem Abruf der Funktion genullt. Erst nachdem alle Kreaturen erstellt werden, wird dieser mit dem **Rating aller Kreaturen** erweitert. \\ | + | Der zweite Counter (''ratingcnt'') wird bei jedem Abruf der Funktion genullt. Erst nachdem alle Kreaturen erstellt werden, wird dieser mit dem **Rating aller Kreaturen** erweitert. \\ |
Beide Counter werden im laufendem Programm am rechten Bildrand angezeigt. Somit kann man mit den fortschreitenden Generationen, die **Entwicklung unserer Evolution** erkennen. \\ | Beide Counter werden im laufendem Programm am rechten Bildrand angezeigt. Somit kann man mit den fortschreitenden Generationen, die **Entwicklung unserer Evolution** erkennen. \\ | ||
- | Um nun eine neue Generation zu erstellen, nutzen wir zunächst die angekündigte "''.sort()''"-Funktion. Durch sortieren der **creatures-Liste**, wird uns eine absteigende Reihenfolge der **Ratings** der Kreaturen ausgegeben. \\ | + | Um nun eine neue Generation zuerstellen, nutzen wir zunächst die angekündigte ''.sort()''-Funktion. Durch sortieren der **creatures-Liste**, wird uns eine absteigende Reihenfolge der **Ratings** der Kreaturen ausgegeben. \\ |
- | Für die neu erstellte Liste nutzen wir eine erste Schleife mit dem "''enumerate''"-Befehl. Mit ihm gelingt es uns sowohl durch den **Index i**, als auch durch das **Element c** von "''creatures''" getilt durch n zu gehen. **n** beschreibt hier den Selektionsdruck und legt somit fest, wie viele Kreaturen vererben dürfen.\\ | + | Für die neu erstellte Liste nutzen wir eine erste Schleife mit dem ''enumerate''-Befehl. Mit ihm gelingt es uns sowohl durch den **Index i**, als auch durch das **Element c** von ''creatures'' geteilt durch n zugehen. **n** beschreibt hier den Selektionsdruck und legt somit fest, wie viele Kreaturen vererben dürfen.\\ |
- | In der **ersten Schleife** speichern wir unter der Variable "''spec''" die jeweilige Spezie jedes Elementes der Liste. \\ | + | In der **ersten Schleife** speichern wir unter der Variable ''spec'' die jeweilige Spezie jedes Elementes der Liste. \\ |
- | Es folgt eine **zweite Schleife**, die durch eine von **n-anhängige Liste**geht. Dabei wird für jedes n-tel eine neue Kreatur **abhängig der vererbten Eigenschaften** erstellt ( "''cr.procreation(c.c1)''"). Anschließend wird mit der "''appendWay()''"- Funktion für die neue Kreatur ein neuer Weg erstellt (abnhängig von den vererbten Eigenschaften). Zu guter Letzt wird jeder neu erstellten Kratur ihre **alte Spezie** durch die Variable "''spec''" zugeordnet und ein **dazu passendes Bild**. \\ | + | Es folgt eine **zweite Schleife**, die durch eine von **n-anhängige Liste**geht. Dabei wird für jedes n-tel eine neue Kreatur **abhängig der vererbten Eigenschaften** erstellt ( ''cr.procreation(c.c1)''). Anschließend wird mit der ''appendWay()''- Funktion für die neue Kreatur ein neuer Weg erstellt (abnhängig von den vererbten Eigenschaften). Zu guter Letzt wird jeder neu erstellten Kratur ihre **alte Spezie** durch die Variable ''spec'' zugeordnet und ein **dazu passendes Bild**. \\ |
Somit haben wir eine neue Generation, die auf die Eigenschaften der Mutter zugreift. | Somit haben wir eine neue Generation, die auf die Eigenschaften der Mutter zugreift. | ||
Zeile 284: | Zeile 286: | ||
====Game loop==== | ====Game loop==== | ||
- | Damit wir am Ende auch alles darstellen können, kommen wir jetzt zum eigentlichen "''Game loop''". \\ | + | Damit wir am Ende auch alles darstellen können, kommen wir jetzt zum eigentlichen ''Game loop''. \\ |
- | Hier greifen wir auf das bekannte Skeleton von Pygame zu, welches wir schon am anfangs verlinkten. \\ | + | Hier greifen wir auf das bekannte Skeleton von Pygame zu, welches wir schon anfangs verlinkten. \\ |
- | Wir setzen also running auf "''True''", rufen alle **Sprites** (außer die Kreaturen) mit der "''defSprites()''" auf und gehen dann in die obligatorsiche **while-Schleife**. \\ | + | Wir setzen running auf "''True''", rufen alle **Sprites** (außer die Kreaturen) mit der ''defSprites()'' auf und gehen dann in die obligatorsiche **while-Schleife**. \\ |
<code python> | <code python> | ||
""" Game loop """ | """ Game loop """ | ||
Zeile 301: | Zeile 303: | ||
</code> | </code> | ||
- | Wir rufen zunächst die Kollisionsfunktion "''bubbly()''" auf. | + | Wir rufen zunächst die Kollisionsfunktion ''bubbly()'' auf. |
- | Anschließend überprüfen wir mit einer for-Schleife durch die creatures-list, ob alle Kreaturen tot sind. Sollte dies der Fall sein, erstellen wir eine neue Generation mit der "''newGen()''"-Funktion. Zusätzlich rufen wir an dieser Stelle "''stats()''" ab, um die Spezienanzahl der neuen Generation zu zählen. \\ | + | Anschließend überprüfen wir mit einer for-Schleife durch die creatures-list, ob alle Kreaturen tot sind. Sollte dies der Fall sein, erstellen wir eine neue Generation mit der ''newGen()''-Funktion. Zusätzlich rufen wir an dieser Stelle ''stats()'' ab, um die Spezienanzahl der neuen Generation zuzählen. \\ |
<code python> | <code python> | ||
Zeile 319: | Zeile 321: | ||
</code> | </code> | ||
- | Nach diesem Schritt kommen wir zu unserer Update-section. Hier werden zunächst alle Sprites mit dem Befehl "''all_sprites.update()''" aktualisiert. Der "''finpergencnt''"-Counter zählt alle Kreaturen, die das Ziel erreicht haben. Dieser wird nach jeder Generation wieder genullt. \\ | + | Nach diesem Schritt kommen wir zu unserer Update-section. Hier werden zunächst alle Sprites mit dem Befehl ''all_sprites.update()'' aktualisiert. Der ''finpergencnt''-Counter zählt alle Kreaturen, die das Ziel erreicht haben. Dieser wird nach jeder Generation wieder genullt. \\ |
<code python> | <code python> | ||
Zeile 333: | Zeile 335: | ||
</code> | </code> | ||
- | Abschließend wird in der draw-section unsere "''draw()''"-Funktion aufgerufen. Hiermit zeichnen wir alles in den screen und aktualisieren diesen mit der "''pygame.display.flip()''"-Funktion. | + | Abschließend wird in der draw-section unsere ''draw()''-Funktion aufgerufen, in der wir alle zu zeichnenden Objekte finden. Um den screen regelmäßig zu aktualisieren, nutzen wir die ''pygame.display.flip()''-Funktion. Somit sind wir am Ende unseres Programmes angekommen. |
<code python> | <code python> |