Tennisspiel.docx PDF - Pygame Tutorial (German)

Summary

This document provides a guide on creating a tennis game using Pygame. It details the implementation of a main loop and event handling, alongside drawing shapes and updating the screen. Ideal for students learning game development concepts in Python.

Full Transcript

**17.2.1. Die Hauptschleife[ℑ](https://inf.ksz.ch/try-project.html#die-hauptschleife)** Verwende dein Testprogramm von oben und ergänze es durch die Zeilen 23 und 28. Grundsätzlich wird unser Spiel, solange es noch nicht beendet ist, in einer Hauptschleife ausgeführt. Diese Schleife sollte **nie lä...

**17.2.1. Die Hauptschleife[ℑ](https://inf.ksz.ch/try-project.html#die-hauptschleife)** Verwende dein Testprogramm von oben und ergänze es durch die Zeilen 23 und 28. Grundsätzlich wird unser Spiel, solange es noch nicht beendet ist, in einer Hauptschleife ausgeführt. Diese Schleife sollte **nie länger unterbrochen** werden. Ansonsten wird das Spiel nicht korrekt funktionieren. Im Moment wird nur der Bildschirm ständig aktualisiert - es passiert noch nichts. +-----------------------------------+-----------------------------------+ | 18 | py.display.set\_caption(\"Pygame | | | Test\") *\# Titel des Fensters* | | 19 | | | | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | 20 | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | | #\#\#\#\#\#\#\#\#\#* | | 21 | | | | *\# Globale Variabeln* | | 22 | | | | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | 23 | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | | #\#\#\#\#\#\#\#\#\#* | | 24 | | | | game\_is\_running = **True** *\# | | 25 | Variable für den Zustand des | | | Spiels* | | 26 | | | | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | 27 | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | | #\#\#\#\#\#\#\#\#\#* | | 28 | | | | *\# Hauptschleife* | | 29 | | | | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | 30 | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | | #\#\#\#\#\#\#\#\#\#* | | | | | | **while** game\_is\_running: *\# | | | Solange das Spiel läuft\...* | | | | | | *\# Hier werden alle Events des | | | Systems abgehandelt* | +-----------------------------------+-----------------------------------+ **17.2.2. Die Events[ℑ](https://inf.ksz.ch/try-project.html#die-events)** Jedes Spiel - auch in Scratch - lebt von den Interaktionen mit den Usern: Wurde die Maus gedrückt? Wurde die Pfeiltaste gedrückt? Wurde geschossen? Solche Ereignisse heissen in der Fachsprache **Events**. Sie werden meist vom User über ein Eingabegerät (Tastatur, Maus, Joystick, Bewegungssensor,...) ausgelöst. Das Betriebssystem, das mit den Eingabegeräten verbunden ist, merkt eine solche Auslösung und meldet diesen Event (Ereignis) an die Programme, die sich dafür interessieren. Wir werden in jedem Durchgang der Hauptschleife alle Events, die vom System gesendet werden, abfragen und im Verlauf dieses Kapitels auf verschiedene Eingaben reagieren [\[►\]](https://inf.ksz.ch/pygame/ref/event.html) . 1. Lege dir einen neuen Ordner mit dem Titel „game1", oder ähnliches, an. Speichere das Testprogramm neu in diesem Ordner unter dem Namen „game1-01.py" ab. 2. In Pygames können die Events des Betriebssystems mit dem Befehl py.event.get() abfragt werden. Wir erhalten eine Liste mit allen Events, die ausgelöst worden sind. Diese kann natürlich auch leer sein [\[►\]](https://inf.ksz.ch/pygame/ref/event.html#pygame.event.get) . 3. Wenn wir alle Elemente dieser Liste abarbeiten wollen, können wir sie mit einer for -Schleife abarbeiten: 4. **for** event **in** py.event.get(): Die Laufvariable heisst nicht mehr i , sondern wir nehmen einen sinnvollen Namen wie event , weil die Elemente in der Liste keine Zahlen, sondern Ereignisse sind. 5. Als Nächstes wollen wir wissen, ob der User das Fenster oben rechts geschlossen hat. 6. Wenn ja, soll das Spiel beendet und das Fenster geschlossen werden. Dazu müssen wir nur die Eventliste korrekt abfragen: if event.type == py.QUIT: [\[►\]](https://inf.ksz.ch/pygame/ref/event.html#pygame.event.EventType) : +-----------------------------------+-----------------------------------+ | 25 | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | 26 | #\#\#\#\#\#\#\#\#\#* | | | | | 27 | *\# Hauptschleife* | | | | | 28 | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | 29 | #\#\#\#\#\#\#\#\#\#* | | | | | 30 | **while** game\_is\_running: *\# | | | Solange das Spiel läuft\...* | | 31 | | | | *\# Hier werden alle Events des | | 32 | Systems abgehandelt* | | | | | 33 | **for** event **in** | | | py.event.get(): *\# Gehe alle | | 34 | Events des Sysems durch* | | | | | 35 | **if** event.type == py.QUIT: *\# | | | Ist das Spiel beendet worden?* | | 36 | | | | game\_is\_running = **False** *\# | | 37 | Wenn ja, beende die | | | Hauptschleife* | | 38 | | | | py.display.update() *\# Zeichne | | 39 | den Bildschirm neu* | | | | | 40 | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | | #\#\#\#\#\#\#\#\#\#* | | | | | | *\# Hauptschleife ist beendet* | | | | | | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | | #\#\#\#\#\#\#\#\#\#* | | | | | | py.quit() *\# Schliesse das | | | Fenster und beende das Programm* | +-----------------------------------+-----------------------------------+ 7. Wenn der User oben rechts geklickt hat, müssen wir die Hauptschleife beenden. D. h. wir setzen die Variable game\_is\_running einfach auf False , um die Hauptschleife zu verlassen. 8. Mit dem Befehl py.quit() können wir das Fenster schliessen und das Programm korrekt beenden. Probier es selber aus [\[►\]](https://inf.ksz.ch/pygame/ref/pygame.html?highlight=pygame%20quit#pygame.quit) . **17.2.3. Grundfiguren zeichnen[ℑ](https://inf.ksz.ch/try-project.html#grundfiguren-zeichnen)** Wie bei den SVG-Vektorgrafiken kann man auch in Pygames einige Grundfiguren zeichnen und ausmalen. Rechtecke, Kreise und Polygone kann man ganz einfach in der Hauptschleife anfordern [\[►\]](https://inf.ksz.ch/pygame/ref/draw.html) : 1. Mit py.draw.rect(screen, \[255, 0, 0\], (500,20,100,100), 1) kannst du ein Rechteck auf dem Fenster (screen ) mit roter Farbe \[255,0,0\] und den Koordinaten (500,20 , sowie der Länge und Breite 100,100) zeichnen. Die 1 am Schluss bedeutet, dass das Rechteck nicht ausgefüllt ist [\[►\]](https://inf.ksz.ch/pygame/ref/draw.html#pygame.draw.rect) . 2. Es ist dir vielleicht schon aufgefallen, dass der Nullpunkt des Koordinatensystems oben links ist und nicht unten links. Daran musst du immer denken, wenn du Objekte auf den Bildschirm setzt. 3. py.draw.rect(screen, \[0, 0, 255\], (400, 250, 200, 100), 0) zeichnet somit ein blaues, ausgefülltes Rechteck. 4. Ein Polygon lässt sich ähnlich wie ein Rechteck zeichnen. Hier musst du die Koordinaten der Ecken hintereinander wie in einer SVG-Datei auflisten [\[►\]](https://inf.ksz.ch/pygame/ref/draw.html#pygame.draw.polygon) : py.draw.polygon(screen, \[0, 255, 0\], ((146, 0), (291, 106), (236, 277), (56, 277), (0, 106)),0) +-----------------------------------+-----------------------------------+ | 36 | screen.fill(\[0, 0, 0\]) | | | | | 37 | *\# rotes Rechteck* | | | | | 38 | py.draw.rect(screen, \[255, 0, | | | 0\], (500,20,100,100), 1) | | 39 | | | | *\# blaues rechteck* | | 40 | | | | py.draw.rect(screen, \[0, 0, | | 41 | 255\], (400, 250, 200, 100), 0) | | | | | 42 | *\# grünes Polygon* | | | | | | py.draw.polygon(screen, \[0, 255, | | | 0\], ((146, 0), (291, 106), (236, | | | 277), (56, 277), (0, 106)),0) | +-----------------------------------+-----------------------------------+ 5. Den Befehl screen.fill(\[0, 0, 0\]) solltest du immer aufrufen, bevor du die Objekte auf den Bildschirm zeichnest. Er sorgt dafür, dass das ganze Fenster mit schwarzer \[0,0,0\] Farbe ausgefüllt wird. D. h. der Bildschirm wird zuerst gelöscht, bevor er neu gezeichnet wird. 6. Wir wollen zusätzlich den Kreis von links nach rechts über den Bildschirm bewegen. Dazu legen wir zuerst eine Variable ball\_koord mit den beiden Koordinaten an [\[►\]](https://inf.ksz.ch/pygame/ref/draw.html#pygame.draw.circle) : +-----------------------------------+-----------------------------------+ | 20 | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | 21 | #\#\#\#\#\#\#\#\#\#* | | | | | 22 | *\# Globale Variabeln* | | | | | 23 | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | 24 | #\#\#\#\#\#\#\#\#\#* | | | | | | game\_is\_running = **True** *\# | | | Variable für den Zustand des | | | Spiels* | | | | | | ball\_koord=\[300,450\] *\# | | | Ballkoordinaten* | +-----------------------------------+-----------------------------------+ 7. Danach können wir in der Hauptschleife die x-Koordinate des Kreises jeweils um eins vergrössern. Wenn der Ball am rechten Fensterrand angekommen ist, beginnen wir wieder links. Das erreichen wir ganz einfach mit der Modulo % win\_size\[0\] Operation. Danach können wir den Kreis zeichnen. +-----------------------------------+-----------------------------------+ | 43 | *\# Ball einen Schritt nach | | | rechts bewegen* | | 44 | | | | ball\_koord\[0\] = | | 45 | (ball\_koord\[0\] + 1) % | | | win\_size\[0\] | | 46 | | | | *\# Ball zeichnen* | | 47 | | | | py.draw.circle(screen, \[255, | | 48 | 128, 0\], | | | (ball\_koord\[0\],ball\_koord\[1\ | | | ]), | | | 10, 0) | | | | | | py.display.update() *\# Zeichne | | | den Bildschirm neu* | +-----------------------------------+-----------------------------------+ 8. Zuletzt geben wir Pygames mit dem Befehl py.display.update() den Auftrag, alle Objekte anzuzeigen.  **17.2.4. Das erste Objekt[ℑ](https://inf.ksz.ch/try-project.html#das-erste-objekt)** Die Grundfiguren sind für einfache Zeichnungen nützlich. Wir wollen aber als nächstes eine Figur und ein Hintergrundbild laden und anzeigen lassen. 1. Lade dir den [folgenden](https://inf.ksz.ch/_downloads/c4a1207631e8e00ec6c0afc0dad7cea5/res.zip) komprimierten Ordner (zip-File) herunter und lege ihn ins Verzeichnis, in welchem dein Programm gespeichert ist. 2. Entpacke das zip-File und lösche die komprimierte Version wieder. Im Ordner sind einige Bilder, die wir verwenden werden, sowie die Vorlage für das nächste Programm. Öffne das Programm 02-04-game1.py aus dem Ordner. 3. Eine erste ganz einfach Methode, um ein Bild anzuzeigen, ist, mit Hilfe des Pygamebefehls py.image.load() . Damit wir später auf das Bild zugreifen können, ordnen wir es einer Variablen zu. [\[►\]](https://inf.ksz.ch/pygame/ref/image.html#pygame.image.load) : 4. pad = py.image.load(\"res/pad.gif\") 5. Die Variable heisst pad und das Bild liegt in Bezug auf den Speicherort des Programms im Ordner res/ und heisst pad.gif. 6. Nun müssen wir das Bild noch zeichnen lassen. Dies geschieht in der Hauptschleife mit dem Befehl [\[►\]](https://inf.ksz.ch/pygame/ref/surface.html#pygame.Surface.blit) : 7. screen.blit(pad, (300, 550)) 8. screen war unser Fenster, .blit sorgt dafür, dass die Pixel unseres Bildes auf das Fenster an der Position (300,500) angezeigt werden. Probier es aus. +-----------------------------------+-----------------------------------+ | 20 | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | 21 | #\#\#\#\#\#\#\#\#\#* | | | | | 22 | *\# Globale Variabeln* | | | | | 23 | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | 24 | #\#\#\#\#\#\#\#\#\#* | | | | | 25 | game\_is\_running = **True** *\# | | | Variable für den Zustand des | | 26 | Spiels* | | | | | 27 | pad = | | | py.image.load(\"res/pad.gif\") | | 28 | *\# lädt das Bild \"pad.gif\" in | | | die Variable pad* | | 29 | | | | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | 30 | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | | #\#\#\#\#\#\#\#\#\#* | | 31 | | | | *\# Hauptschleife* | | 32 | | | | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | 33 | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | | #\#\#\#\#\#\#\#\#\#* | | 34 | | | | **while** game\_is\_running: *\# | | 35 | Solange das Spiel läuft\...* | | | | | 36 | *\# Hier werden alle Events des | | | Systems abgehandelt* | | 37 | | | | **for** event **in** | | 38 | py.event.get(): *\# Gehe alle | | | Events des Sysems durch* | | 39 | | | | **if** event.type == py.QUIT: *\# | | 40 | Ist das Spiel beendet worden?* | | | | | | game\_is\_running = **False** *\# | | | Wenn ja, beende die | | | Hauptschleife* | | | | | | screen.blit(pad, (300, 550)) *\# | | | Zeichne das Pad* | | | | | | py.display.update() *\# Zeichne | | | den Bildschirm neu* | | | | | | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | | #\#\#\#\#\#\#\#\#\#* | +-----------------------------------+-----------------------------------+ **17.2.5. Die Mauskoordinaten[ℑ](https://inf.ksz.ch/try-project.html#die-mauskoordinaten)** Als Nächstes soll das Rechteck der Maus folgen. In Scratch ging das ganz einfach mit: 1. Ähnlich schnell kann man dies mit Pygame realisieren. Mit dem Befehl py.mouse.get\_pos() können die Mauskoordinaten direkt abgefragt werden. D. h. statt zum Punkt mit den Koordinaten (300,500) zu gehen, springen wir zur Mausposition. Probier es aus! [\[►\]](https://inf.ksz.ch/pygame/ref/mouse.html#pygame.mouse.get_pos) , +-----------------------------------+-----------------------------------+ | 34 | **if** event.type == py.QUIT: *\# | | | Ist das Spiel beendet worden?* | | 35 | | | | game\_is\_running = **False** *\# | | 36 | Wenn ja, beende die | | | Hauptschleife* | | 37 | | | | screen.blit(pad, | | 38 | py.mouse.get\_pos()) *\# Zeichne | | | das Pad beim Mauszeiger* | | | | | | py.display.update() *\# Zeichne | | | den Bildschirm neu* | +-----------------------------------+-----------------------------------+ 2. Der Effekt mit der Spur ist zwar interessant, aber eigentlich wollen wir keinen Abdruck des Rechtecks hinterlassen. Deswegen müssen wir bei jedem Durchgang den Bildschirm löschen. Dies geschieht, indem wir den ganzen Hintergrund mit schwarzer Farbe ausfüllen: +-----------------------------------+-----------------------------------+ | 37 | screen.fill(\[0, 0, 0\]) *\# | | | Fülle den Hintergrund mit | | 38 | schwarzer Farbe auf* | | | | | 39 | screen.blit(pad, | | | py.mouse.get\_pos()) *\# Zeichne | | | das Pad beim Mauszeiger* | | | | | | py.display.update() *\# Zeichne | | | den Bildschirm neu* | +-----------------------------------+-----------------------------------+ **17.2.6. Das Sprite[ℑ](https://inf.ksz.ch/try-project.html#das-sprite)** Die oben beschriebene Methode, um Objekte darzustellen, funktioniert auf den ersten Blick nicht schlecht. Es wird sich aber zeigen, dass es mit dieser Art schwierig wird, Kollisionen mit anderen Objekten zu erkennen. Um effizient mit Objekten in Pygame zu arbeiten, bietet sich die **Sprite**-Klasse an. Ein Sprite ist ein vordefiniertes Objekt in Pygame, das bereits gewisse nützliche Eigenschaften hat, die uns die Arbeit erleichtern wird. In Scratch sind die Objekte wie die Katze oder Bart Simpson ebenfalls Sprites. [\[►\]](https://inf.ksz.ch/pygame/ref/sprite.html) . Das Arbeiten mit der Sprite-Klasse hat zur Folge, dass man sich mit Klassen, d.h. mit der objektorientierten Notation auseinandersetzen muss. Hier ein erstes Beispiel dazu: +-----------------------------------+-----------------------------------+ | 18 | py.display.set\_caption(\"Pygame | | | Test\") *\# Titel des Fensters* | | 19 | | | | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | 20 | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | | #\#\#\#\#\#\#\#\#\#* | | 21 | | | | *\# Die Klasse des Pads | | 22 | definieren* | | | | | 23 | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | 24 | #\#\#\#\#\#\#\#\#\#* | | | | | 25 | **class** | | | **Pad**(py.sprite.Sprite): *\# | | 26 | Wie sieht ein Pad aus?* | | | | | 27 | **def** \_\_init\_\_(self): *\# | | | Hier wird der Spieler | | 28 | initialisiert* | | | | | 29 | super().\_\_init\_\_() *\# Musst | | | du nicht verstehen* | | | | | | self.image = | | | py.image.load(\"res/pad.gif\") | | | *\# Das Bild des Spielers laden* | | | | | | self.rect = | | | self.image.get\_rect() *\# | | | Umrechteck = Rechteck, es soll | | | gleich sein, wie die des Bildes* | | | | | | self.rect.x = win\_size\[0\] / 2 | | | *\# Die x-Koordinate soll in der | | | halben Fensterlänge sein* | | | | | | self.rect.y = win\_size\[1\] - 50 | | | *\# Die y-Koordinate soll am | | | unteren Bildschirmrand sein* | +-----------------------------------+-----------------------------------+ 1. Die ersten drei Zeilen musst du nicht wirklich verstehen. Sie sagen lediglich, dass Pad ein Sprite sein soll. Dabei liefert uns die Sprite-Klasse viele vordefinierte Funktionen, um dann einfacher mit dem Objekt, hier das Pad, arbeiten zu können. Die drei Zeilen gehören zur Syntax der objektorientierten Programmierung (kurz OOP) und werden hier nicht weiter thematisiert. Wenn du ein neues Sprite brauchst, dann musst du dich einfach an diese Notation halten. 2. Alle weiteren Zeilen beginnen mit self.. Die Syntax gehört ebenfalls zur OOP und besagt, dass die Eigenschaft immer zum Objekt selber (self.) gehört. D. h. unser Sprite Pad: - hat ein Bild self.image [\[►\]](https://inf.ksz.ch/pygame/ref/image.html#pygame.image.load) und - ein Begrenzungsrechteck self.rect [\[►\]](https://inf.ksz.ch/pygame/ref/rect.html) . 3. Das Begrenzungsrechteck jedes Sprites hat eine x- und eine y-Koordinate. Man erhält sie durch self.rect.x bzw. self.rect.y. 4. Ein Sprite hat noch weitere Eigenschaften, die man einstellen oder abfragen kann. Wir gehen später darauf ein. [\[►\]](https://inf.ksz.ch/pygame/ref/sprite.html) . 5. Da wir nun das Pad als Sprite definiert haben, musst du das „alte" Pad aus dem Programm löschen. Lösche dazu die Zeilen mit pad = py.image.load(\"res/pad.gif\") und screen.blit(pad, py.mouse.get\_pos()). 6. Die gelöschten Befehle ersetzen wir: Ein Sprite sollte sich auch selber zeichnen können. D. h. wir geben dem Sprite eine neue Funktionalität: def draw(self):. Den Befehl fürs Zeichnen kennst du bereits: screen.blit(self.image, (py.mouse.get\_pos()\[0\],540)). Wir nehmen nur die x-Koordinate der Maus py.mouse.get\_pos()\[0\] und legen die y-Koordinate auf 540 fest. So bewegt sich der Spieler, d. h. das Pad, nur am unteren Rand. +-----------------------------------+-----------------------------------+ | 29 | self.rect.y = win\_size\[1\] - 50 | | | *\# Die y-Koordinate soll am | | 30 | unteren Bildschirmrand sein* | | | | | 31 | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | 32 | #\#\#\#\#\#\#\#\#\#* | | | | | 33 | *\# Hier wird das Pad gezeichnet* | | | | | 34 | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | 35 | #\#\#\#\#\#\#\#\#\#* | | | | | 36 | **def** draw(self): *\# Funktion | | | zum zeichnen des Objekts* | | 37 | | | | mx = py.mouse.get\_pos()\[0\] *\# | | | x-Koordinate der Maus* | | | | | | my = 540 *\# y-Koordinate auf 540 | | | festlegen* | | | | | | screen.blit(self.image, (mx, my)) | | | *\# Das Pad zeichnen* | +-----------------------------------+-----------------------------------+ 7. Wenn du jetzt das Programm laufen lässt, wird auf dem Bildschirm nichts passieren! Jetzt haben wir erst den Bauplan für ein Pad festgelegt. Wir brauchen nun eine Variable (Instanz) vom Typ dieses Sprites. Mit player = Pad() definieren wir eine neue Variable vom Typ Pad() , mit der wir nun konkret arbeiten können. +-----------------------------------+-----------------------------------+ | 39 | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | 40 | #\#\#\#\#\#\#\#\#\#* | | | | | 41 | *\# Globale Variabeln* | | | | | 42 | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | 43 | #\#\#\#\#\#\#\#\#\#* | | | | | | game\_is\_running = **True** *\# | | | Variable für den Zustand des | | | Spiels* | | | | | | player = Pad() *\# Variable | | | player ist ein Pad* | +-----------------------------------+-----------------------------------+ 8. Der Spieler wird immer noch nicht gezeichnet: In der Hauptschleife rufen wir die eben erstellte Zeichenfunktion des Spielers mit player.draw() auf. Probier es aus. +-----------------------------------+-----------------------------------+ | 56 | screen.fill(\[0, 0, 0\]) *\# | | | Fülle den Hintergrund mit | | 57 | schwarzer Farbe auf* | | | | | 58 | player.draw() *\# Zeichnet den | | | Spieler* | | | | | | py.display.update() *\# Zeichne | | | den Bildschirm neu* | +-----------------------------------+-----------------------------------+ **17.2.7. Kostümwechsel bei Sprites[ℑ](https://inf.ksz.ch/try-project.html#kostumwechsel-bei-sprites)** Nun wollen wir einen Ball dazunehmen, damit das Spiel interessanter wird. Schau im Ordner „res" nach: Hier sollten 15 Bilder eines Balls vorhanden sein. Wir wollen, ähnlich wie in Scratch, einen Kostümwechsel beim Ball vornehmen. 1. Zuerst schreiben wir wieder den Bauplan des Balls auf. Er sieht ähnlich wie der des Pad aus, da es eine OOP-Notation ist: +-----------------------------------+-----------------------------------+ | 40 | screen.blit(self.image, (mx, my)) | | | *\# Das Pad zeichnen* | | 41 | | | | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | 42 | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | | #\#\#\#\#\#\#\#\#\#* | | 43 | | | | *\# Die Klasse des Balls | | 44 | definieren* | | | | | 45 | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | 46 | #\#\#\#\#\#\#\#\#\#* | | | | | 47 | **class** | | | **Ball**(py.sprite.Sprite): *\# | | 48 | Wie sieht ein Ball aus?* | | | | | 49 | **def** \_\_init\_\_(self): *\# | | | Hier wird der Ball initialisiert* | | 50 | | | | super().\_\_init\_\_() *\# Musst | | 51 | du nicht verstehen* | | | | | 52 | self.animation = \[\] *\# Die | | | Liste der Animierten Bilder ist | | 53 | zuerst leer* | | | | | 54 | **for** i **in** range(15): *\# | | | Alle 15 vorhandenen Bilder laden* | | | | | | s = \"res/ball\"+str(i)+\".gif\" | | | *\# Den Namen des i-te Bildes | | | zusammensetzen* | | | | | | self.animation.append(py.image.lo | | | ad(s))*\# | | | Das i-ten Bilde laden und der | | | Liste hinzufügen* | | | | | | self.image = self.animation\[0\] | | | *\# Bild des Balls ist das erste | | | Bild der Animation* | | | | | | self.rect = | | | self.image.get\_rect() *\# | | | Umrechteck = Rechteck, es soll | | | gleich sein, wie die des Bildes* | | | | | | self.costume = 0 *\# Zähler für | | | das aktuelle Kostüm des Balls* | +-----------------------------------+-----------------------------------+ 2. Die ersten drei Zeilen sind gleich wie beim code:*Pad*, ausser dass unser Bauplan Ball heisst. 3. Der Ball soll 15 verschiedene Kostüme besitzen - wie ein Scratch-Objekt. Dafür müssen wir zuerst die Bilder des Balls laden. Es wäre mühsam, alle 15 Bildernamen abzutippen. Deswegen erstellen wir zuerst eine Liste mit allen Bildernamen. - Zu Beginn ist die Liste leer: self.animation = \[\] - Danach wiederholen wir 15 Mal Folgendes: - Wir setzen zuerst den Namen des i-ten Bildes zusammen: \"res/ball\"+str(i)+\".gif\" - Danach laden wir das Bild und fügen es der Liste der Animationen hinzu. self.animation.append(py.image.load(s)) 4. Wir laden das erste Bild der Animationsliste als Startbild für den Ball: self.image = self.animation\[0\]. 5. Das Umrandungsrechteck des Balls soll gleich gross sein wie das rechteckige Bild des Balls: self.rect = self.image.get\_rect() 6. Dann fügen wir noch einen Zähler für das aktuelle Kostüm ein. Das erste Kostüm hat die Nummer 0. **17.2.8. Funktionen hinzufügen[ℑ](https://inf.ksz.ch/try-project.html#funktionen-hinzufugen)** Vielleicht erahnst du bereits den Vorteil und die Stärke der OOP mit Hilfe der Klassen: Wir können die Klassen beliebig erweitern, ohne globale Variabeln einführen zu müssen (wie z. B. self.costume oder self.animation ). Diese Eigenschaft gehört dann genau zu dieser Klasse und zu keiner anderen. 1. Wir wollen, dass sich der Ball bewegt und dass sich die Kostüme wie in Scratch abwechseln. Wir definieren eine neue Funktionalität für den Ball: def refresh(self):. Hier sollen alle Änderungen des Balls durchgeführt werden. 2. Als Erstes ändern wir das Kostüm: Zuerst erhöhen wir die Variable costume um eins und schauen, dass die Zahl nicht grösser als 14 wird. Das erreichen wir ganz einfach mit dem Modulo Befehl: self.costume = (self.costume + 1) % self.animation) 3. Die Kostümnummer haben wir gewechselt, jetzt müssen wir nur noch das Kostüm „anziehen": self.image = self.animation\[self.costume\]. +-----------------------------------+-----------------------------------+ | 54 | self.costume = 0 *\# Zähler für | | | das aktuelle Kostüm des Balls* | | 55 | | | | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | 56 | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | | #\#\#\#\#\#\#\#\#\#* | | 57 | | | | *\# Hier werden alle Änderungen | | 58 | des Balls ausgeführt* | | | | | 59 | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | 60 | #\#\#\#\#\#\#\#\#\#* | | | | | 61 | **def** refresh(self): *\# Hier | | | werden alle Änderungen für den | | | Ball gemacht* | | | | | | self.costume = (self.costume + 1) | | | % len(self.animation)*\# costum | | | um eins erhöhen* | | | | | | self.image = | | | self.animation\[self.costume\] | | | *\# Das aktuelle Bild soll das | | | Bild des aktuellen Kostüms sein* | +-----------------------------------+-----------------------------------+ 4. Die nächste Funktionalität für den Ball kennst du schon: Der Ball soll sich selber zeichnen können. Deswegen definieren wir wie beim Pad die Funktion def draw(self):. Sie soll den Ball an seiner momentanen Position zeichnen. +-----------------------------------+-----------------------------------+ | 61 | self.image = | | | self.animation\[self.costume\] | | 62 | *\# Das aktuelle Bild soll das | | | Bild des aktuellen Kostüms sein* | | 63 | | | | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | 64 | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | | #\#\#\#\#\#\#\#\#\#* | | 65 | | | | *\# Hier wird der Ball | | 66 | gezeichnet* | | | | | 67 | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | | #\#\#\#\#\#\#\#\#\#* | | | | | | **def** draw(self): *\# Funktion | | | zum zeichnen des Objekts* | | | | | | screen.blit(self.image, | | | (self.rect.x,self.rect.y))*\# Den | | | Ball zeichnen* | +-----------------------------------+-----------------------------------+ 5. Wie beim Pad auch haben wir erst den Bauplan des Balls definiert. Einen Ball als Variable haben wir noch nicht. Deswegen bestellen wir uns als Nächstes einen Ball: ball = Ball(). +-----------------------------------+-----------------------------------+ | 70 | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | 71 | #\#\#\#\#\#\#\#\#\#* | | | | | 72 | *\# Globale Variabeln* | | | | | 73 | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | 74 | #\#\#\#\#\#\#\#\#\#* | | | | | 75 | game\_is\_running = **True** *\# | | | Variable für den Zustand des | | | Spiels* | | | | | | player = Pad() *\# Variable | | | player ist ein Pad-Sprite* | | | | | | ball = Ball() *\# Variable ball | | | ist ein Ball-Sprite* | +-----------------------------------+-----------------------------------+ 6. Mit der Definition der Variablen ball können wir jetzt den Ball in der Hauptschleife zeichnen. Vor dem Zeichnen wechseln wir natürlich noch das Kostüm, damit die Bilder sich abwechseln. Probier es aus! +-----------------------------------+-----------------------------------+ | 88 | screen.fill(\[0, 0, 0\]) *\# | | | Fülle den Hintergrund mit | | 89 | schwarzer Farbe auf* | | | | | 90 | player.draw() *\# Zeichnet den | | | Spieler* | | 91 | | | | ball.refresh() *\# Ball neu | | 92 | berechnen* | | | | | | ball.draw() *\# zeichnet den | | | Ball* | | | | | | py.display.update() *\# Zeichne | | | den Bildschirm neu* | +-----------------------------------+-----------------------------------+ **17.2.9. Die Framerate[ℑ](https://inf.ksz.ch/try-project.html#die-framerate)** Dir ist sicher aufgefallen, dass sich der Ball viel zu schnell dreht: Das liegt daran, dass die Hauptschleife sehr schnell ausgeführt wird. Du würdest wahrscheinlich die Unterschiede im Tempo auf verschiedenen Rechnern mit Auge sehen. Wenn du einen schnellen Rechner hast, dreht sich der Ball schneller als auf einem alten Rechner. Um diese Unterschiede auszugleichen gibt es die sogennante Framerate (FPS = frames per seconds). Die Zahl sagt aus, wie oft der Bildschirm pro Sekunde neu gezeichnet werden soll. Das menschliche Auge hat eine FPS von ca. 24 Bilder pro Sekunde. Alles was schneller ist, nehmen wir nicht wahr. Alles was langsamer ist nehmen wir als „laggen" wahr [\[►\]](https://inf.ksz.ch/pygame/ref/time.html#pygame.time.Clock) . 1. Pygame bietet die Kontrolle über die FPS mit Hilfe einer Uhr an: +-----------------------------------+-----------------------------------+ | 71 | player = Pad() *\# Variable | | | player ist ein Pad-Sprite* | | 72 | | | | ball = Ball() *\# Variable ball | | 73 | ist ein Ball-Sprite* | | | | | | clock = py.time.Clock() *\# Eine | | | Pygame-Uhr um die Framerate zu | | | kontrollieren* | +-----------------------------------+-----------------------------------+ 2. Mit dieser Uhr können wir die Framerate selber festlegen unabhängig davon wie schnell unser Rechner ist. Probier es aus und experimentiere mit der FPS [\[►\]](https://inf.ksz.ch/pygame/ref/time.html#pygame.time.Clock.tick) . +-----------------------------------+-----------------------------------+ | 89 | ball.draw() *\# zeichnet den | | | Ball* | | 90 | | | | py.display.update() *\# Zeichne | | 91 | den Bildschirm neu* | | | | | | clock.tick(24) *\# Der Bildschirm | | | soll alle 1/24 Sekunden | | | aktualisiert werden* | +-----------------------------------+-----------------------------------+ **17.2.10. Sprites bewegen[ℑ](https://inf.ksz.ch/try-project.html#sprites-bewegen)** Ein ruhender Ball macht kein Spass! Wir wollen, dass sich der Ball bei jedem Frame etwas bewegt. 1. Dazu fügen wir eine neue Eigenschaft zum Ball hinzu: Wir definieren einen Vektor (eine Liste) mit zwei Komponenten: Die erste gibt die Änderung in x-Richtung und die andere die Änderung in y-Richtung an. 2. Des weiteren setzen wir den Ball kurz über dem Pad und lassen ihn danach Richtung oben rechts laufen. +-----------------------------------+-----------------------------------+ | 39 | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | 40 | #\#\#\#\#\#\#\#\#\#* | | | | | 41 | *\# Die Klasse des Balls | | | definieren* | | 42 | | | | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | 43 | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | | #\#\#\#\#\#\#\#\#\#* | | 44 | | | | **class** | | 45 | **Ball**(py.sprite.Sprite): *\# | | | Wie sieht ein Ball aus?* | | 46 | | | | **def** \_\_init\_\_(self): *\# | | 47 | Hier wird der Ball initialisiert* | | | | | 48 | super().\_\_init\_\_() *\# Musst | | | du nicht verstehen* | | 49 | | | | file\_names = \[\] *\# Liste der | | 50 | Namen der Biler generieren* | | | | | 51 | **for** i **in** range(15): *\# | | | Alle 15 vorhandenen Bilder laden* | | 52 | | | | file\_names.append(\"res/ball\"+s | | 53 | tr(i)+\".gif\")*\# | | | \"res/ball0.gif\" bis | | 54 | \"res/ball15.gif\" in die Liste | | | einfügen* | | | | | | self.animation = | | | \[py.image.load(f) **for** f | | | **in** file\_names\]*\# Liste der | | | Bilder laden* | | | | | | self.image = self.animation\[0\] | | | *\# Bild des Balls ist das erste | | | Bild der Animation* | | | | | | self.rect = | | | self.image.get\_rect() *\# | | | Umrechteck = Rechteck, es soll | | | gleich sein, wie die des Bildes* | | | | | | self.rect.x = win\_size\[0\]/2 | | | *\# Startpunkt ist Bildschirmitte | | | in x-Richtung* | | | | | | self.rect.y = win\_size\[1\]-100 | | | *\# und 100 Pixel über dem Boden* | | | | | | self.direction = \[10,-7\] *\# | | | Vektor= Richtung in die sich der | | | Ball sich bewegt* | | | | | | self.costume = 0 *\# Zähler für | | | das aktuelle Kostüm des Balls* | +-----------------------------------+-----------------------------------+ 3. Nun können wir die Bewegung des Balls in Angriff nehmen. Sie soll bei jedem Durchgang der Hauptschleife erfolgen. In der Hauptschleife haben wir bereits die Funktion ball.refresh() aufgerufen. Hier werden alle Neuberechnungen für den Ball durchgeführt. In dieser Funktion soll neu auch die Bewegung des Balls in Richtung des Vektors direction erfolgen [\[►\]](https://inf.ksz.ch/pygame/ref/rect.html) : +-----------------------------------+-----------------------------------+ | 59 | **def** refresh(self): *\# Hier | | | werden alle Änderungen für den | | 60 | Ball gemacht* | | | | | 61 | *\# Kostümwechsel* | | | | | 62 | self.costume = (self.costume + 1) | | | % len(self.animation) *\# costume | | 63 | um eins erhöhen* | | | | | 64 | self.image = | | | self.animation\[self.costume\] | | 65 | *\# Bild mit der Kostümnummer | | | anzeigen* | | | | | | *\# Ball in Richtung des Vektors | | | direction bewegen* | | | | | | self.rect.x = self.rect.x + | | | self.direction\[0\] *\# Ball in | | | x-Richtung um Vektor direction | | | ändern* | | | | | | self.rect.y = self.rect.y + | | | self.direction\[1\] *\# Ball in | | | y-Richtung um Vektor direction | | | ändern* | +-----------------------------------+-----------------------------------+ 4. Wenn du dein Programm nun ausprobierst, wird der Ball am rechten Bildrand hinauswandern. Wir müssen noch dafür sorgen, dass der Ball innerhalb des Bildes bleibt. Schau dir die Skizze weiter oben an. Sie beschreibt die Kollision mit dem rechten Bildschirmrand. Ähnlich funktioniert die Kollision mit dem linken, oberen bzw. unteren Bildschirmrand. Wenn ein Kollision gefunden wurde, müssenn wir die Richtung des Bewegungsvektors ändern: Aus der Richtung \[10,-7\] sollte bei einer Kollision mit dem rechten Rand neu die Richtung \[-10,-7\] entstehen. D. h. wir müssen nur die entsprechende Koordinate mit der Zahl -1 multiplizieren. Probier es aus! +-----------------------------------+-----------------------------------+ | 63 | *\# Ball in Richtung des Vektors | | | direction bewegen* | | 64 | | | | self.rect.x = self.rect.x + | | 65 | self.direction\[0\] *\# Ball in | | | x-Richtung um Vektor direction | | 66 | ändern* | | | | | 67 | self.rect.y = self.rect.y + | | | self.direction\[1\] *\# Ball in | | 68 | y-Richtung um Vektor direction | | | ändern* | | 69 | | | | *\# Abprallen vom Rand* | | 70 | | | | **if** (self.rect.x \> | | | win\_size\[0\]-self.rect.width | | | **or** self.rect.x \< 0): *\# | | | linker und rechter Rand* | | | | | | self.direction\[0\] = | | | self.direction\[0\] \* -1 *\# | | | Richtung umdrehen* | | | | | | **if** (self.rect.y \> | | | win\_size\[1\]-self.rect.height | | | **or** self.rect.y \< 0):*\# | | | oberer und unterer Rand* | | | | | | self.direction\[1\] = | | | self.direction\[1\] \* -1 *\# | | | Richtung umdrehen* | +-----------------------------------+-----------------------------------+ **17.2.11. Kollision erkennen[ℑ](https://inf.ksz.ch/try-project.html#kollision-erkennen)** Kollisionen von zwei oder sogar mehreren Sprites erkennen, ist nicht immer ganz so einfach und doch muss die Erkennung wärend eines Spiels sehr häufig durchgeführt werden. Deswegen bietet Pygames verschiedene Kollisionserkennungsfunktionen von Objekten an. Wir behandeln in diesem ersten Spiel die einfachste Kollisionserkennung: Ein Objekt (Ball) mit einem anderen Objekt (Player). Wenn der Ball auf den Spieler auftrifft, möchten wir, dass der Ball reflektiert wird. Die Kollision mit der Wand haben wir über die Koordinaten des Balls gelöst ( Man hätte auch Wandobjekte definieren können und diese auf Kollision mit dem Ball überprüfen können.). 1. Um die Kollision zweier Sprites besser zu verstehen, werden wir die Umgebungsrechtecke des Balls und des Spielers vorläufig einzeichnen, damit du besser verstehst, wie die Kollision funktioniert. Zeichne die beiden Rechtecke als Grundfiguren ein und teste das Programm. +-----------------------------------+-----------------------------------+ | 31 | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | 32 | #\#\#\#\#\#\#\#\#\#* | | | | | 33 | *\# Hier wird das Pad gezeichnet* | | | | | 34 | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | 35 | #\#\#\#\#\#\#\#\#\#* | | | | | 36 | **def** draw(self): *\# Funktion | | | zum zeichnen des Objekts* | | 37 | | | | mx = py.mouse.get\_pos()\[0\] *\# | | 38 | x-Koordinate der Maus* | | | | | | my = 540 *\# y-Koordinate auf 540 | | | festlegen* | | | | | | screen.blit(self.image, (mx, my)) | | | *\# Das Pad zeichnen* | | | | | | py.draw.rect(screen, \[255, 0, | | | 0\], self.rect, 1) *\# | | | provisorisch: Umrechteck | | | zeichnen* | +===================================+===================================+ | 73 | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | 74 | #\#\#\#\#\#\#\#\#\#* | | | | | 75 | *\# Hier wird der Ball | | | gezeichnet* | | 76 | | | | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | 77 | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | | #\#\#\#\#\#\#\#\#\#* | | 78 | | | | **def** draw(self): *\# Funktion | | | zum zeichnen des Objekts* | | | | | | screen.blit(self.image, | | | (self.rect.x,self.rect.y)) *\# | | | Den Ball zeichnen* | | | | | | py.draw.rect(screen, \[255, 0, | | | 0\], self.rect, 1) *\# | | | provisorisch: Umrechteck | | | zeichnen* | +-----------------------------------+-----------------------------------+ 2. Du siehst, wir haben zwar das Bild des Spielers bewegt, aber das Begrenzungsrechteck nicht. Da die Kollision über dise Rechtecke erkannt wird, ist es wichtig die Rechtecke immer mit zu verschieben: +-----------------------------------+-----------------------------------+ | 31 | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | 32 | #\#\#\#\#\#\#\#\#\#* | | | | | 33 | *\# Hier wird das Pad gezeichnet* | | | | | 34 | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | 35 | #\#\#\#\#\#\#\#\#\#* | | | | | 36 | **def** draw(self): *\# Funktion | | | zum zeichnen des Objekts* | | 37 | | | | mx = py.mouse.get\_pos()\[0\] *\# | | 38 | x-Koordinate der Maus* | | | | | 39 | my = 540 *\# y-Koordinate auf 540 | | | festlegen* | | 40 | | | | self.rect.x = mx *\# Das | | | Begrenzungsrechteck des Bildes | | | setzen: x* | | | | | | self.rect.y = my *\# Das | | | Begrenzungsrechteck des Bildes | | | setzen: y* | | | | | | screen.blit(self.image, (mx, my)) | | | *\# Das Pad zeichnen* | | | | | | py.draw.rect(screen, \[255, 0, | | | 0\], self.rect, 1) *\# | | | provisorisch: Umrechteck | | | zeichnen* | +-----------------------------------+-----------------------------------+ 3. Mit dem Pygames Befehl py.sprite.collide\_rect(ball,player) kann man überfrüfen, ob sich die Begrenzungsrechtecke zweier Sprites überschneidet oder nicht [\[►\]](https://inf.ksz.ch/pygame/ref/sprite.html) . +-----------------------------------+-----------------------------------+ | 99 | **if** event.type == py.QUIT: *\# | | | Ist das Spiel beendet worden?* | | 100 | | | | game\_is\_running = **False** *\# | | 101 | Wenn ja, beende die | | | Hauptschleife* | | 102 | | | | *\# Kollision Ball - Spieler* | | 103 | | | | **if** | | 104 | (py.sprite.collide\_rect(ball,pla | | | yer) | | 105 | == **True**):*\# Kollidiert Ball | | | mit Spieler?* | | | | | | print(\"kollision\") | | | | | | ball.direction\[1\] = | | | ball.direction\[1\] \* (-1)*\# | | | ändere die y-Richtung* | +-----------------------------------+-----------------------------------+ 4. Ausserdem ist es sinnvoll, die Kollision nur als solche anzuerkennen, wenn der Ball nach unten fällt und nicht von unten ans Pad herankommt. +-----------------------------------+-----------------------------------+ | 102 | *\# Kollision Ball - Spieler* | | | | | 103 | **if** | | | (py.sprite.collide\_rect(ball,pla | | 104 | yer) | | | == **True**):*\# Kollidiert Ball | | 105 | mit Spieler?* | | | | | | **if** ball.direction\[1\] \> 0: | | | *\# Fällt der Ball oder steigt | | | er?* | | | | | | ball.direction\[1\] = | | | ball.direction\[1\] \* (-1)*\# | | | ändere die y-Richtung* | +-----------------------------------+-----------------------------------+ **17.2.12. Töne abspielen[ℑ](https://inf.ksz.ch/try-project.html#tone-abspielen)** Was ist ein Spiel ohne Soundeffekte? Diese kann man in Pygames natürlich ebenfalls wie in Scratch ganz einfach abspielen. 1. Zuerst müssen wir den Ton wie in Scratch auch in das Spiel laden. Wir wollen, dass der Ball einen Ton von sich gibt, wenn er von einer Wand abprallt. Dewegen geben wir dem Ball eine neue Eigenschaft: self.music [\[►\]](https://inf.ksz.ch/pygame/ref/mixer.html#pygame.mixer.Sound) : +-----------------------------------+-----------------------------------+ | 54 | self.rect.y = win\_size\[1\]-100 | | | *\# und 100 Pixel über dem Boden* | | 55 | | | | self.direction = \[10,-7\] *\# | | 56 | Vektor= Richtung in sich der Ball | | | sich bewegt* | | 57 | | | | self.costume = 0 *\# Zähler für | | | das aktuelle Kostüm des Balls* | | | | | | self.music = | | | py.mixer.Sound(\"res/pingpong.wav | | | \") | | | *\# Ton beim Aufprall auf die | | | Wand* | +-----------------------------------+-----------------------------------+ 2. Um diesen Ton abzuspielen, müssen wir nur an der richtigen Stelle den Befehl py.mixer.Sound.play(self.music) einfügen und schon wird der Ton abgspielt. Füge das Abspielen beim Abprallen von den Wänden ein [\[►\]](https://inf.ksz.ch/pygame/ref/mixer.html#pygame.mixer.Sound.play) . +-----------------------------------+-----------------------------------+ | 69 | *\# Abprallen vom Rand* | | | | | 70 | **if** (self.rect.x \> | | | win\_size\[0\]-self.rect.width | | 71 | **or** self.rect.x \< 0): *\# | | | linker und rechter Rand* | | 72 | | | | self.direction\[0\] = | | 73 | self.direction\[0\] \* -1 *\# | | | Richtung umdrehen* | | 74 | | | | py.mixer.Sound.play(self.music) | | 75 | *\# Ton abspielen* | | | | | | **if** (self.rect.y \> | | | win\_size\[1\]-self.rect.height | | | **or** self.rect.y \< 0):*\# | | | oberer und unterer Rand* | | | | | | self.direction\[1\] = | | | self.direction\[1\] \* -1 *\# | | | Richtung umdrehen* | | | | | | py.mixer.Sound.play(self.music) | | | *\# Ton abspielen* | +===================================+===================================+ | 104 | **if** | | | (py.sprite.collide\_rect(ball,pla | | 105 | yer) | | | == **True**):*\# Kollidiert Ball | | 106 | mit Spieler?* | | | | | 107 | **if** ball.direction\[1\] \> 0: | | | *\# Fällt der Ball oder steigt | | | er?* | | | | | | ball.direction\[1\] = | | | ball.direction\[1\] \* (-1)*\# | | | ändere die y-Richtung* | | | | | | py.mixer.Sound.play(ball.music) | | | *\# Ton abspielen* | +-----------------------------------+-----------------------------------+ **17.2.13. Hintergrundbild einfügen[ℑ](https://inf.ksz.ch/try-project.html#hintergrundbild-einfugen)** Der schwarze Hintergrund ist nicht sehr schön. In Pygames lässt sich ganz einfach ein Hintergrundbild einfügen. 1. Im Ordner „res" findest du einen Hintergrund. 2. Mit dem Befehl bg\_image = py.image.load(\"res/bg.gif\") kannst du das Hintergundbild laden [\[►\]](https://inf.ksz.ch/pygame/ref/image.html#pygame.image.load) . +-----------------------------------+-----------------------------------+ | 15 | py.init() *\# Pygames | | | initialisieren* | | 16 | | | | win\_size = (800,600) *\# | | 17 | Fenstergrösse definieren* | | | | | 18 | screen = | | | py.display.set\_mode(win\_size) | | 19 | *\# Fenstergrösse setzen* | | | | | | py.display.set\_caption(\"Pygame | | | Test\") *\# Titel des Fensters* | | | | | | bg\_image = | | | py.image.load(\"res/bg.gif\") *\# | | | Hintergrundsbild laden* | +-----------------------------------+-----------------------------------+ 3. Mit dem Befehl screen.blit(bg\_image,(0,0)) kannst du ihn anzeigen [\[►\]](https://inf.ksz.ch/pygame/ref/surface.html#pygame.Surface.blit) . 4. Wir ersetzen somit das Löschen des Bildschirms (Füllen mit schwarzer Farbe) durch das Anzeigen des Hintergrundbildes. +-----------------------------------+-----------------------------------+ | 110 | screen.blit(bg\_image,(0,0)) *\# | | | Zeigt das Hintergrundbild an* | | 111 | | | | player.draw() *\# Zeichnet den | | 112 | Spieler* | | | | | 113 | ball.refresh() *\# Ball neu | | | berechnen* | | 114 | | | | ball.draw() *\# zeichnet den | | | Ball* | | | | | | py.display.update() *\# Zeichne | | | den Bildschirm neu* | +-----------------------------------+-----------------------------------+ **17.2.14. Punktzahl anzeigen[ℑ](https://inf.ksz.ch/try-project.html#punktzahl-anzeigen)** Natürlich wollen wir auch die Anzahl Schläge zählen, die der Spieler erreicht hat. 1. Die Punktzahl ist eine Eigenschaft des Spielers. Deswegen definieren wir sie beim Spieler: self.points = 0 . +-----------------------------------+-----------------------------------+ | 29 | self.rect.x = win\_size\[0\] / 2 | | | *\# Die x-Koordinate soll in der | | 30 | halben Fensterlänge sein* | | | | | 31 | self.rect.y = win\_size\[1\] - 50 | | | *\# Die y-Koordinate soll am | | | unteren Bildschirmrand sein* | | | | | | self.points = 0 *\# Anzahl Punkte | | | des Spielers* | +-----------------------------------+-----------------------------------+ 2. Wir müssen diese erhöhen, wenn eine Kollision mit dem Ball erkannt wird. Das geschieht in der Hauptschleife bei der Überprüfung, ob sich die Sprites des Balls und des Spielers überschneiden: +-----------------------------------+-----------------------------------+ | 104 | *\# Kollision Ball - Spieler* | | | | | 105 | **if** | | | (py.sprite.collide\_rect(ball,pla | | 106 | yer) | | | == **True**):*\# Kollidiert Ball | | 107 | mit Spieler?* | | | | | 108 | **if** ball.direction\[1\] \> 0: | | | *\# Fällt der Ball oder steigt | | 109 | er?* | | | | | | ball.direction\[1\] = | | | ball.direction\[1\] \* (-1)*\# | | | ändere die y-Richtung* | | | | | | py.mixer.Sound.play(ball.music) | | | *\# Ton abspielen* | | | | | | player.points = player.points + 1 | | | *\# Die Punkte des Spieler | | | erhöhen* | +-----------------------------------+-----------------------------------+ 3. Die Punkte müssen noch irgendwo auf dem Bildschirm angezeigt werden. Leider geht das nicht ganz so einfach mit einem print() - Befehl. Ähnlich wie einen Ton oder ein Bild müssen wir auch eine Schrift in Pygames zuerst laden [\[►\]](https://inf.ksz.ch/pygame/ref/font.html#pygame.font.SysFont) : +-----------------------------------+-----------------------------------+ | 18 | py.display.set\_caption(\"Pygame | | | Test\") *\# Titel des Fensters* | | 19 | | | | bg\_image = | | 20 | py.image.load(\"res/bg.gif\") *\# | | | Hintergrundsbild laden* | | | | | | my\_font = | | | py.font.SysFont(\'Comic Sans | | | MS\', 36) *\# Font zum schreiben | | | laden* | +-----------------------------------+-----------------------------------+ 4. Danach müssen wir den Text, den wir schreiben wollen, erst zusammensetzen. Da sich die Punktzahl ändern kann, müssen wir dies auch in der Hauptschleife programmieren. Um die Hauptschleife übersichtlich zu halten und da es sein kann, dass du noch weiteren Text auf den Schirm schreiben möchtest, werden wir das Textschreiben in eine Funktion auslagern: [\[►\]](https://inf.ksz.ch/pygame/ref/font.html#pygame.font.Font.render) : +-----------------------------------+-----------------------------------+ | 86 | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | 87 | #\#\#\#\#\#\#\#\#\#* | | | | | 88 | *\# Schreibt den Spielstand auf | | | den Bildschirm* | | 89 | | | | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | 90 | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | | #\#\#\#\#\#\#\#\#\#* | | 91 | | | | **def** draw\_score(): | | 92 | | | | text = my\_font.render(\"Score: | | 93 | \"+str(player.points),**True**, | | | (0,0,0)) *\# Text zusammensetzen* | | 94 | | | | screen.blit(text,(20,20)) *\# | | | Spielstand auf den Bildschirm | | | schreiben* | | | | | | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | | #\#\#\#\#\#\#\#\#\#* | | | | | | *\# Globale Variabeln* | +-----------------------------------+-----------------------------------+ 5. Zu guter Letzt können wir die neue Funktion in der Hauptschleife aufrufen: +-----------------------------------+-----------------------------------+ | 121 | player.draw() *\# Zeichnet den | | | Spieler* | | 122 | | | | ball.refresh() *\# Ball neu | | 123 | berechnen* | | | | | 124 | ball.draw() *\# zeichnet den | | | Ball* | | | | | | draw\_score() *\# Schreibt den | | | Spielstand auf den Bildschirm* | +-----------------------------------+-----------------------------------+ **17.2.15. Spiel beenden[ℑ](https://inf.ksz.ch/try-project.html#spiel-beenden)** Im Moment läuft das Spiel unendlich lange. Wir wollen, dass es aufhört, wenn der Ball den unteren Fensterrand berührt. 1. Dazu müssen wir die Abprallbedingung, die wir bereits für den unteren Rand programmiert haben, wieder herausnehmen: +-----------------------------------+-----------------------------------+ | 76 | **if** self.rect.y \< 0: *\# | | | oberer Rand* | | 77 | | | | self.direction\[1\] = | | 78 | self.direction\[1\] \* -1 *\# | | | Richtung umdrehen* | | | | | | py.mixer.Sound.play(self.music) | | | *\# Ton abspielen* | +-----------------------------------+-----------------------------------+ 2. Es ist Aufgabe des Balls zu überprüfen, ob er am unteren Rand schon raus ist oder nicht. Je nachdem müssen wir das Spiel beenden oder nicht. Dies gelingt aber nur, wenn wir die Variable game\_is\_running auf False setzen können, damit die Hauptschleife beendet wird. Das Problem ist lediglich, dass die Ball-Klasse nicht auf die Variable game\_is\_running direkt zugreifen und diese manipulieren kann. 3. Die Lösung liegt in einer Funktion, die den aktuellen Zustand des Spiels als Rückgabewert zur Verfügung stellt. Diese Funktion gehört zur Klasse des Balls. Wer auch immer sie aufruft, erhält als Antwort True oder False , je nachdem, ob der Ball aus dem Fenster raus ist oder nicht. +-----------------------------------+-----------------------------------+ | 80 | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | 81 | #\#\#\#\#\#\#\#\#\#* | | | | | 82 | *\# Hier wird der Ball | | | gezeichnet* | | 83 | | | | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | 84 | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | | #\#\#\#\#\#\#\#\#\#* | | 85 | | | | **def** draw(self): *\# Funktion | | 86 | zum zeichnen des Objekts* | | | | | 87 | screen.blit(self.image, | | | (self.rect.x,self.rect.y)) *\# | | 88 | Den Ball zeichnen* | | | | | 89 | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | 90 | #\#\#\#\#\#\#\#\#\#* | | | | | 91 | *\# Prüft, ob der Ball im | | | Spielfeld ist* | | 92 | | | | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | 93 | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | | #\#\#\#\#\#\#\#\#\#* | | | | | | **def** end\_of\_game(self): | | | | | | **if** self.rect.y \> | | | win\_size\[1\]: *\# Ist der Ball | | | unten aus dem Fenster heraus?* | | | | | | **return** **True** *\# Wenn ja, | | | gib True zurück* | | | | | | **else**: | | | | | | **return** **False** *\# | | | ansonsten soll False zurück | | | gegeben werden* | +-----------------------------------+-----------------------------------+ 4. Nun können wir in der Hauptschleife bequem diese neue Funktion aufrufen: +-----------------------------------+-----------------------------------+ | 135 | clock.tick(24) *\# Der Bildschirm | | | soll alle 1/24 Sekunden | | 136 | aktualisiert werden* | | | | | 137 | **if** game\_is\_running: *\# | | | Falls das Fenster mit dr Maus | | | geschlossen wurde=\> | | | game\_is\_running=False* | | | | | | game\_is\_running = | | | **not**(ball.end\_of\_game()) *\# | | | Ist der Ball noch im Spiel?* | +-----------------------------------+-----------------------------------+ 5. Das Ende ist kommt ein wenig plötzlich. Wir wollen noch ein Schlussbild mit den erreichten Punktzahlen einblenden. Dazu laden wir ein neues Hintergrundbild. Du findest es ebenfalls im „res"-Ordner: +-----------------------------------+-----------------------------------+ | 225 | **if** player.life == 0: *\# Noch | | | leben vorhanden?* | | 226 | | | | game\_is\_running = **False** *\# | | 227 | Ist der Ball noch im Spiel?* | | | | | 228 | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | 229 | #\#\#\#\#\#\#\#\#\#* | | | | | 230 | *\# Hauptschleife des Spiels ist | | | beendet* | | 231 | | | | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | 232 | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | | #\#\#\#\#\#\#\#\#\#* | | | | | | bg\_image = | | | py.image.load(\"res/bg\_game\_ove | | | r.gif\") | | | *\# Hintergrundsbild laden* | | | | | | screen.blit(bg\_image,(0,0)) *\# | | | Zeigt das Hintergrundbild an* | +-----------------------------------+-----------------------------------+ 6. Den aktuellen Punktestand müssen wir als Text auf das Hintergrundbild schreiben. Zuerst zusammensetzen, dann schreiben: +-----------------------------------+-----------------------------------+ | 228 | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | 229 | #\#\#\#\#\#\#\#\#\#* | | | | | 230 | *\# Hauptschleife des Spiels ist | | | beendet* | | 231 | | | | *\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\# | | 232 | \#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\ | | | #\#\#\#\#\#\#\#\#\#* | | 233 | | | | bg\_image = | | 234 | py.image.load(\"res/bg\_game\_ove | | | r.gif\") | | 235 | *\# Hintergrundsbild laden* | | | | | 236 | screen.blit(bg\_image,(0,0)) *\# | | | Zeigt das Hintergrundbild an* | | | | | | *\# Text zusammensetzen* | | | | | | text = my\_font.render(\"Du hast: | | | \

Use Quizgecko on...
Browser
Browser