Pygame Zero ist ein Python-Modul womit man relativ einfach grafische Programme schreiben kann. Pygame Zero hilft beim Erstellen eines Fensters und bietet mehrere Möglichkeiten um Bilder und Grafiken darzustellen, zu animieren und auch zu steuern.
Achtung
Pygame Zero-Programme verwenden neben der Python-Skript-Datei Medien-Dateien wie Bilder und Sounds. Diese müssen von Pygame Zero gefunden werden können. Dies wird durch eine vorgegebene Ordnerstruktur erreicht:
pygame-zero/
├── sounds/
│ └── click.mp3
├── images/
│ ├── alien.png
│ └── alien_hurt.png
└── game.py
2
3
4
5
6
7
Aufgabe: Ordner-Struktur erstellen
- gehe in deinen Informatik-Ordner
- erstelle dort einen Unterordner
pygame-zero
für alle Pygame Zero-Skripte - öffne den neu-erstellten Ordner
- erstelle darin die Unterordner
images
undsounds
für verwendete Medien-Dateien
Speichere in Zukunft alle Pygame-Zero-Skripte und die verwendeten Medien-Dateien in den oben erstellten Ordnern!
Vorbereitung
Damit man Pygame Zero verwenden kann, muss man zuerst das Package pgzero
installieren, dann kann man im Skript das Modul verwenden (import pgzrun
) und ganz am Schluss dann mit pgzrun.go()
alles starten:
import pgzrun
pgzrun.go()
2
3
Alles andere kommt also zwischen die beiden oben stehenden Zeilen.
Aufgabe: Package installieren
- öffne Thonny und installiere das Package
pgzero
- teste ob es funktioniert, indem du das obenstehende Zwei-Zeilen-Programm ausführst
Fenstereinstellungen
Nun können wir sehr einfach ein Fenster erstellen: Dazu definieren wir zwei Ganze Zahlen als Konstanten mit den Namen WIDTH
und HEIGHT
.
WIDTH = 200
HEIGHT = 200
2
Diese werden beim Start von Pygame Zero gelesen und für die Dimensionen des Fensters verwendet. Wenn wir das Programm starten, sehen wir ein kleines schwarzes quadratisches Fenster.
Optional können wir einen dritten Wert TITLE
definieren. Hier müssen wir Text eingeben, gekennzeichnet durch Anführungszeichen. Pygame Zero verwendet diesen Wert um das Fenster zu beschriften:
TITLE = "Erstes Programm"
Aufgabe: Fenstereinstellungen
Baue Fenstergrösse und Fenstertitel in dein Programm ein.
Lösung
import pgzrun
TITLE = "Erstes Programm"
WIDTH = 200
HEIGHT = 200
pgzrun.go()
2
3
4
5
6
7
Actor hinzufügen
Nun wollen wir etwas darin anzeigen. Wir wollen einen sogenannten Actor einbauen. Wie der Name es sagt – ein Schauspieler. Beim Actor handelt es sich sozusagen um eine Spielfigur, die wir dann verschieben und verwenden können. Der Actor wird mit Hilfe einer Bilddatei dargestellt.
Aufgabe: Bilder
Zuerst müssen wir aber die beiden Bilder an der richtigen Stelle abspeichern:
- gehe in den Ordner wo dein Python-Skript liegt
- erstelle einen Unterordner images
- speichere die beiden pinken Alien-Bilder dort drin ab
Wir hängen vor das pgzrun.go()
den folgenden Code an:
...
zorg = Actor('alien')
zorg.x = 100
zorg.y = 100
2
3
4
Damit haben wir einen sogenannten Actor
definiert:
- Zeile 2
- Erstellt einen
Actor
aus dem Bild alien.png (muss im Unterordner images liegen) und weist dem neuenActor
den Namenzorg
zu, damit wir ihn später wieder ansprechen können. - Zeile 3 und 4
- Da
zorg
einActor
ist, hat er auch eine Position. Wir setzen seine Koordinaten auf die Mitte des Fensters.
Spiel zeichnen
Wenn wir das Programm starten, ändert sich aber nichts – das Fenster bleibt immer noch schwarz. Dies ist deshalb so, weil Pygame Zero zum Zeichnen des Fensterinhaltes ein spezielles Unterprogramm aufruft, nämlich draw()
. Dieses Unterprogramm muss sämtlichen Fensterinhalt zeichnen und von uns definiert werden. Wir fügen das folgende Unterprogramm vor der letzte Zeile in unseren Code ein:
def draw():
screen.clear()
zorg.draw()
2
3
- Zeile 1
- Mit
def
geben wir an, dass wir ein Unterprogramm definieren wollen. draw
soll der Name des Unterprogramms sein. Sonst kann Pygame Zero das Unterprogramm gar nicht aufrufen- mit der Klammer
()
wird gesagt dass der Namedraw
für ein Unterprogramm steht und mit dem Doppelpunkt:
wird der Inhalt des Unterprogramms eingeleitet - Zeile 2
- löscht den Fensterinhalt komplett
- (ist eingerückt und gehört deshalb zum Inhalt des Unterprogramms)
- Zeile 3
- zeichnet unseren
Actor
- (ist eingerückt und gehört deshalb zum Inhalt des Unterprogramms)
Aufgabe: Actor
Baue den Actor in dein Programm ein und teste das Programm. Das Programm sollte wie folgt aussehen:
Lösung: Actor
import pgzrun
TITLE = "Erstes Programm"
WIDTH = 200
HEIGHT = 200
zorg = Actor('alien')
zorg.x = 100
zorg.y = 100
def draw():
screen.clear()
zorg.draw()
pgzrun.go()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Aufgabe: Änderungen
Versuche die folgenden Änderungen am Programm vorzunehmen:
- Vergrössere das Pygame Zero-Fenster, so dass es einigermassen den Bildschirm füllt.
- Füge ein weiteres «Alien» hinzu. Gib ihm aber einen anderen Namen und natürlich andere Koordinaten!
Hier findest du weitere Aliens in verschiedenen Farben und etwas kleinerem Format:
Zusatzaufgabe: dritter Actor
Füge einen dritten Actor hinzu. Suche dazu ein passendes Bild, z.B. von https://www.kenney.nl/assets?q=2d.
Das Bild muss im PNG-Format vorhanden sein und in den images-Unterordner relativ zum Python-Skript abgespeichert werden.
Zusatzaufgabe: Hintergrund
Versuche einen Hintergrund zu zeichnen. Schau dir dazu die Unterprogramme von screen
an.
(z.B. ein Himmel, farbige Planeten und ein Boden)
Animation
Der Game-Loop ruft das Unterprogramm update
immer wieder auf. Dabei übergibt der Game-Loop die verstrichene Zeit seit dem letzten Aufruf als Argument dt
. Damit kann im Unterprogramm an Hand der Geschwindigkeit der Objekte ihr in der Zwischenzeit zurückgelegter Weg berechnet werden:
zorg.vx = 10
def update(dt):
delta_x = dt * zorg.vx
zorg.x = zorg.x + delta_x
2
3
4
5
- Zeile 1
- Zorgs Startgeschwindigkeit in x-Richtung
- Zeile 4
- der in der Zeit
dt
zurückgelegte Weg wird berechnet - Zeile 5
- Zorg wird um den zurückgelegten Weg verschoben
Aufgabe: vertikale Bewegung
Schaffst du es den Alien vertikal zu Bewegen?
Und diagonal? und leicht diagonal?
Zusatzaufgabe: Freier Fall
Kannst du den Alien runterfallen lassen, inklusive Beschleunigung?
Im obigen Beispiel hat der Alien eine konstante Geschwindigkeit nach rechts. Nun müsste er eine vertikale Geschwindigkeit erhalten, die zunimmt – am Besten mit einer Gravitationsbeschleunigung!
Der Actor (API)
Zusammenfassung von: https://pygame-zero.readthedocs.io/en/stable/builtins.html#actors
Position
Ein Actor
kann beliebig positioniert werden. Dazu setzen wir einfach neue Werte für die x- und y-Koordinate des gewünschten Actors:
zorg.x = 100
zorg.y = 400
2
Grafik
Man kann die Grafik des Actors
auch wechseln und damit z.B. anzeigen dass der Spieler verletzt wurde:
zorg.image = "alien_hurt"
Rotation
Man kann die Grafik des Actors
auch drehen. Standardmässig ist die Richtung 0°, also gegen Rechts. Man kann den Actor
also z.B. 90° nach oben drehen:
zorg.angle = 90
Aufgabe
Lies die beiden nächsten Kapitel durch und zeichne einen Hintergrund für unseren Alien:
- Ein Boden (wohl ein Rechteck unten)
- farbige Planeten im Hintergrund
Farben definieren
Farben müssen in Pygame Zero selbst gemischt werden. Dabei werden die 3 Grundfarben Rot, Grün und Blau des additiven Farbmodells in Anteilen von 0-255 gemischt.
Das Ganze wird als Tripel (Folge von 3 Zahlen) geschrieben. Die erste Zahl bestimmt den Rot-, die Zweite den Grün- und die Dritte den Blau-Anteil. Am Besten definiert man die Farben gleich zu Beginn des Programms als Konstante und gibt ihnen einen aussagekräftigen Namen:
WHITE = (255,255,255)
BLACK = (0,0,0)
ORANGE = (255,165,0)
MINT_CREAM = (245,255,250)
2
3
4
Hier kannst du dir eine Farbe mischen:
Schreibweisen:
html: #ffffff
, rgb(255, 255, 255)
pygame: (255, 255, 255)
Auch online findet man diverse Tools, wo man Farben interaktiv mischen kann und die RGB-Werte als Ergebnis mit Copy & Paste in den Code übernehmen kann.
z.B. https://www.rapidtables.com/web/color/RGB_Color.html
Eine Farbe wird in den folgenden Aufrufen als (r, g, b)
dargestellt.
Der Screen (API)
Zusammenfassung von: https://pygame-zero.readthedocs.io/en/stable/builtins.html#screen
Löschen und Füllen
clear()
Setzt alles auf Schwarz
screen.clear()
fill((r, g, b))
Füllt alles mit der Farbe (r, g, b)
screen.fill(MINT_CREAM)
Zeichnen
draw.line(start, end, (r, g, b))
Zeichnet eine Linie vom Start-Punkt zum Endpunkt
screen.draw.line((200, 100), (400, 12), BLACK)
draw.circle(pos, radius, (r, g, b))
Zeichnet einen Kreis.
screen.draw.circle((200, 100), 60, BLACK)
draw.filled_circle(pos, radius, (r, g, b))
Zeichnet einen gefüllten Kreis
screen.draw.filled_circle((200, 100), 60, ORANGE)
draw.rect(rect, (r, g, b))
Zeichnet ein Rechteck. Die Dimensionen müssen als Rect
definiert werden.
BOX = Rect((20, 20), (100, 100))
screen.draw.rect(BOX, BLACK)
2
draw.filled_rect(rect, (r, g, b))
Zeichnet ein gefülltes Rechteck. Die Dimensionen müssen als Rect
definiert werden.
screen.draw.rect(Rect((20, 20), (100, 100)), ORANGE)
Text
draw.text(text, pos, color=(r, g, b))
Zeichnet Text
screen.draw.text("hello pygame zero", (150, 10), color=BLACK)
👉 Weiteres zu Text: https://pygame-zero.readthedocs.io/en/stable/ptext.html
Ereignisse (Events)
Ereignisse sind spezielle Unterprogramme, die von Pygame-Zero automatisch aufgerufen werden, wenn ein entsprechendes Ereignis eintritt. Als Argumente erhalten wir Details zum Ereignis (Mauskoordinaten, welche Maus-Taste wurde gedrückt, welche Keyboard-Taste wurde getippt, …). Anhand dieser Details können wir entscheiden wie sich das Ereignis auf unser Spiel auswirkt.
Tastatur
Bei den Tastatur-Ereignissen entscheidet man zwischen Taste runterdrücken und wieder loslassen. Beide von den Ereignissen aufgerufene Unterprogramme erhalten als Argument die gedrückte Taste:
def on_key_down(key):
print(key, "wurde runtergedrückt")
def on_key_up(key):
print(key, "wurde losgelassen")
2
3
4
5
Die komplette Liste der überprüfbaren Tasten findet man hier:
https://pygame-zero.readthedocs.io/en/stable/hooks.html#buttons-and-keys
Beispiel
Wir wollen unseren Alien mit den Tasten ←, → nach links und rechts steuern.
def on_key_down(key):
if key == keys.RIGHT:
zorg.x = zorg.x + 1
elif key == keys.LEFT:
zorg.x = zorg.x -1
2
3
4
5
Aufgabe
Baue eine Tastatursteuerung ein. Überlege dir folgende Punkte:
- Welche Tasten möchtest du verwenden?
- Was soll ein Tastendruck machen?
- Was geschieht wenn der Actor den Rand des Fenster erreicht?
Maus
Die Ereignisse der Maus werden in drei Unterprogrammen zusammengefasst: «Maustaste heruntergedrückt», «Maustaste losgelassen» und «Maus verschoben»:
def on_mouse_down(pos, button):
print("Mouse button", button, "clicked at", pos)
def on_mouse_up(pos, button):
print("Mouse button", button, "released at", pos)
def on_mouse_move(pos, rel, buttons):
print("Mouse moved", button, "released at", pos, "relative distance", rel)
2
3
4
5
6
7
8
Bei allen drei Unterprogrammen können ein, mehrere und auch alle Argumente weggelassen werden!
Beispiel
Wir könnten z.B. überprüfen, ob bei einem Mausklick, ein Actor
getroffen wird oder nicht. Dazu reicht uns die Position, wo der Mausklick stattgefunden hat. Welche Taste es war, ist uns egal:
def on_mouse_down(pos):
if zorg.collidepoint(pos):
print("Treffer!")
else:
print("Daneben!")
2
3
4
5
- Zeile 1
- Wir definieren das Unterprogramm das bei einem Mausklick aufgerufen werden soll.
- Zeile 2
collidepoint
liefert True, wenn derActor
zorg
mit den Mauskoordinatenpos
«kollidiert» – also die Mauskoordinaten im Bereich des Aliens liegen.
Zeit-gesteuert
Ein weiteres wichtiges Ereignis sind gestellte Timer. So können wir irgendwann einen Timer starten. Wenn dieser abläuft, wird ein Unterprogramm aufgerufen:
def on_mouse_down(pos):
if zorg.collidepoint(pos):
zorg_hurt()
clock.schedule_unique(zorg_normal, 1)
else:
print("Daneben!")
def zorg_hurt():
zorg.image = "alien_hurt"
def zorg_normal():
zorg.image = "alien"
2
3
4
5
6
7
8
9
10
11
12
Solche Timer setzen wir mit clock.schedule...
. Als Argument geben wir den Namen des Unterprogrammes an (ohne Klammern! – wir rufen es ja noch nicht auf) und die Dauer des Timers in Sekunden.
schedule_unique(callback, delay)
Event nur einmal auslösen, d.h. falls das Event bereits geplant ist wird es gelöscht und neu angesetzt.
clock
kennt neben schedule_unique(callback, delay)
noch drei weitere Befehle:
schedule(callback, delay)
Event immer auslösen, auch wenn das selbe bereits geplant ist.
schedule_interval(callback, interval)
Event immer wieder in zeitgleichen Abständen (interval
) aufrufen.
unschedule(callback)
ein geplantes Event löschen.
Kollisionen erkennen
In vielen Spielen die mit Pygame Zero programmiert werden können, müssen wir überprüfen, ob zwei Objekte sich berühren. Mathematisch ist das nicht ganz einfach:
- Bei runden Objekten könnte man den Abstand < Radius überprüfen
- Als Vereinfachung überprüft man, ob sich die umgebenden Rechtecke überschneiden
Pygame Zero kann für einen Actor eine Kollision mit einem einzelnen Punkt oder mit einem Rechteck (und somit einen weiteren Actor) erkennen:
collidepoint(point)
z.B. bei einem mouse-event: Traf der Mausklick den Actor zorg
?
def on_mouse_down(pos):
if zorg.collidepoint(pos):
print("Treffer!")
else:
print("Daneben!")
2
3
4
5
colliderect(rect)
z.B. im update-Unterprogramm: berühren sich die umgebenden Rechtecke von zorg
und dem Gegner enemy
?
def update():
if zorg.colliderect(enemy):
print("Game-Over!")
2
3
Spiel
Seitwärts-Scroller, den Meteoriten ausweichen:
Vorgehen
- Alien auf linker Seite
- Alien kann sich nur auf und ab bewegen
- von Rechts kommen Meteoriten (Liste)
- Alien darf die Meteoriten nicht berühren
Aufgabe
Setze die Programmidee um und programmiere ein solches Meteoriten-Spiel!
Erweiterungs-Ideen
- Meteoriten rotieren
- Meteoriten sind verschieden gross
- Meteoriten sind verschieden schnell
- Punkte zählen (z.B. Zeit, Anzahl Meteoriten, …)
- mehrere Leben (nach Treffer einige Zeit unverwundbar, anderes Alien-Bild)
- mit der Zeit schwieriger (z.B. schneller, mehr Meteoriten)
- Meteoriten können zerschossen werden
- Parallax-Scroll (Hintergrund scrollt mit, ev. mehr als eine Ebene)
- Münzen einsammeln
Aufgabe
Baue einige der oben vorgeschlagenen oder eigene Erweiterungs-Ideen ein