Dies ist eine alte Version des Dokuments!
Zunächst haben wir uns Gedanken über die Schwerpunkte unseres Projektes gemacht. Dazu kamen wir auf folgende Bereiche:
Eine erste Überlegung befindet sich auf unserem Konzeptblatt.
Gemiensam mit unserem Konzeptblatt erstellten wir auch einen Zeitplan. Um ehrlich zu sein umfasste dieser nicht viel. Und mit nicht viel meinen wir konkret nur zwei Daten. Wir hatten uns vorgenommen am 25.01.2018 mit dem Hauptcode CreatureR fertig zu sein und am 15.02.2018 mit der Visualisierung. Schnell merkten wir , dass der Visualisierungscode notwendig zum Weiterkommen mit dem Hauptcode wurde. Weshalb wir eigentlich diesen Zeitplan überhaupt nicht mehr beachteten und permanent parallel an beiden Codes arbeiteten.
Vielmehr setzten wir uns dann wöchentliche Ziele, die wir abarbeiteten. Die wichtigsten Punkte sind dabei folgende gewesen:
PyGame bot uns im Gegensatz zu Turtle wesentlich umfangreichere Visualisierungsmöglichkeiten für unser Projekt. Einer der wichtigsten Vorteile dieses Pakets war dabei, dass wir damit in der Lage dazu waren, mehrere Kreaturen gleichzeitig auf dem Bildschirm erscheinen zu lassen. Mit Turtle konnte man Maximal eine Kreatur beobachten und so zwar den Hauptcode verbessern, aber nicht das volle Ausmaß dessen graphisch darstellen.
Wie der Name schon sagt ist PyGame an sich für Videospiee mit Python entwickelt worden und ist leider entsprechend nicht besonders gut dokumentiert. Die beste Dokumentation, die wir gefunden hatten waren die Tutorials von KidsCanCode auf YouTube.
Das grundsätzliche Konzept eines jeden Spiels also auch eines Spiels, das man mit Pyhtn programmiert ist die sogenannte Game Loop. Diese sorgt dafür, dass das Spiel angezeigt wird und sich je nach bestimmten Inputs etwas auf dem Bildschirm verändert.
Auf dem Folgenden Bild erkennt man die Bestandteile eines Game Loops, in die der Sceleton eines PyGame Projekts auch im Groben unterteilt.\newline
Der Processing Input ist alles, was extern vom Code selbst eingegeben wird. Dazu gehören z.B. Interaktionen mit der Maus oder den Tasten auf dem Keyboard. Auch so etwas wie ein „x“ am oberen Fensterrrand zum Schließen des Programms muss in diesen Bereich des Codes eingebaut werden.
In der Update Section werden in jedem Frame die neuen Daten verarbeitet, die sich seit dem letzten Frame verändert haben, in unserem Fall also jegliche Fortbewegungne er Kreaturen.\newline
Die Render Section kann man auch als rawing section bezeichen Sie lässt die daten, die beim Updaten sih verändert haben auf dem Bildschirm ausgeben. \newline
Die Uhr am ende der gameloop soll symbolisieren, dass die Game loop sich in jedem Frame wiederholt und so eine Game Loop sich innerhalb von einer Sekunde z.B. 24 mal ( 24 fps = Anzahl an Frames für ein bewegtes Bild) oder auch 60 Mal (Frame rate moderner videospiele) wiederholen kann. Je nach dem wie gut der Prozessor des zum Ausführen verwendeten endgerätes ist, kann das Programm dann auch bei einer zu hohen Frame-Rate ruckeln oder hängen. \newline
Nach dieser etwas längeren Einführung nun zum eigentlichen Code. \newline
Die Notwendigen Bestandteile eines PyGame Codes haben wir so gut wie eins zu eins aus dem „Sceleton Code“ von Kids Can Code (Github link) übernommen. Da dieser für jedes PyGame Projekt so gut wie gleich ist, werden wir ihn an dieser stelle nicht näher erklären, sondern auf das zugehörige Tutorial verweisen.
Nun aber wirklich zu unserem Code:
Beginnen wir mit den Klassen:
Für alle Hindernisse, die auf unserer Karte zu sehen sind (namentlich die Regentropfen, die Wolke und die Plattformen) haben wir Klassen angelegt. Von der Struktur her sind die Klassen sehr ähnlich weswegen hier nun exemplarisch eine erklärt wird.
Das ist die Klasse für unsere Regentropfen:
class DROP(pygame.sprite.Sprite): """Regentropfen""" def __init__(self, x, y): pygame.sprite.Sprite.__init__(self) self.x = x self.y = y self.image = pygame.image.load(os.path.join(img_folder, "drop.png")).convert() self.image.set_colorkey(BLACK) self.image = pygame.transform.scale(self.image, (30,50)) self.mask = pygame.mask.from_surface(self.image) self.rect = self.image.get_rect() self.rect.center = (self.x, self.y)
Der Klasse wird ein PyGame Sprite übergeben. Da alle Objekte in PyGame als Sprites bezeichnet werden, ist dies an dieser Stelle obligatorisch.
Genauso muss in jeder Klasse eine __init__-Funktion enthalten sein, der die relevanten Variablen (in diesem Fall z.B. die x- und y-Koordniaten) übergeben werden, die für die Vorgänge in der Klasse benötigt werden.
Als erste Zeile in dieser Funktion muss zudem die __init__-Funktion selbst initialisiert werden.
Darauf folgt das Einbauen und Positionieren der Regentropfen-Bilder.
Die übergebenen x- und y-Koordinaten müssen in der Funktion zur weiteren Verwendung mit ihrer eigenen Selbstreferenz gleichgesetzt werden.
Mit den drei Zeilen, die mit „self.image“ beginnen wird zuerst erreicht, dass das Bild namens „drop.png“ aus einem extra Ordner namens „img“ geladen wird. Der „.convert()“-Befehl muss an das Ende des Ausdrucks gesetzt werden, sonst entsteht eventuell eine falsche Darstellung des eigentlichen Bildes.
Der „set_colorkey“-Befehl Entfernt jegliche Stellen im Bild mit der vermerkten Farbe. Alle selbstgezeichneten Bilder wurden freigestellt und mit einem schwarzen Hintergrund ausgestattet, sodass durch diesen Befehl das Bild ohne schwarzen Hintergrund auf dem Bildschirm zu sehen ist.
Der transform.scale(), der in der darauffolgenden Zeile verwenden wird, gibt uns die Möglichkeit ohne zusätzliche Bildbearbeitung, sondern direkt in PyGame die Größe des Bildes bequem anzupassen. Dabei steht die erste Zahl in den Klammern für die Länge und die zweite für die Breite.
Bevor die nächste Zeile versanden werden kann, ist es wichtig die zwei „self.rect“-Zeilen danach zu erklären. Hier wird nämlich erstmal ein Rechteck (rect) um unseren Regentropfen erstellt und im Weiteren mit dem „.center“-Befehl wird dieses Rechteck an das Zentrum der Version des Bildes mit schwarzem Hintergrund gelegt. Das ist bei jedem PyGame-Sprite erforderlich, damit Pygame das Bild auch als Objekt wahrnehmen und nach verfolgen kann. Zwar können Bilder freigestellt werden, jedoch gelten sie im PyGame-Algorithmus nur als Rechtecke mit speziellen Eigenschaften.
An dieser Stelle kommt die „self.mask“-Zeile ins Spiel, die zuerst ausgelassen wurde.
Wir möchten bei unseren Hindernissen und auch später bei unseren Kreaturen nicht, dass (selbst wenn man es wegen den freigestellten Bildern so nicht sieht) nur die Rechtecke um die Obejekte herum mit einander kollidieren, sondern die Umrisse der Bilder, wie sie durch die freigestellten Bilder sichtbar sind. Dafür wird eine Maske (mask) erstellt. Diese Maske wird bei der Kollision weiterverwendet werden.
class CREA(pygame.sprite.Sprite): """erstellt die kreaturen""" def __init__(self,spec): pygame.sprite.Sprite.__init__(self) self.c1 = cr.Creature(species=spec) self.c1.milkAppend() self.apCounter = self.c1.species[3] self.image = all_images[spec] self.spec = spec self.rect = self.image.get_rect() self.rect.center = (0,0) self.block_index = 0 def update(self): if self.block_index < len(self.c1.milkyway): block = self.c1.milkyway[self.block_index] self.block_index += 1 self.c1.moveFurther(block) self.rect.center = (self.c1.xcor+100,self.c1.ycor+360) if self.apCounter == 1 and self.c1.survivaltimer < moveCap and not self.c1.dead: self.c1.milkAppend() self.apCounter = self.c1.species[3] elif self.apCounter > 1: self.apCounter -= 1 else: self.c1.dead = True if (self.c1.xcor+100) < 0 or (self.c1.xcor+100) > 1000: self.c1.dead = True if (self.c1.ycor+360) < 0 or (self.c1.ycor+360) > 720: self.c1.dead = True