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
1
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 und sounds 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()
1
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
1
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"
1

Aufgabe: Fenstereinstellungen

Baue Fenstergrösse und Fenstertitel in dein Programm ein.

Lösung
import pgzrun

TITLE = "Erstes Programm"
WIDTH = 200
HEIGHT = 200

pgzrun.go()
1
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
1
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 neuen Actor den Namen zorg zu, damit wir ihn später wieder ansprechen können.
Zeile 3 und 4
Da zorg ein Actor 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()
1
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 Name draw 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:

Screenshot
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()
1
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)

👉 Der Screen (API)

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
1
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
1
2

Grafik

Man kann die Grafik des Actors auch wechseln und damit z.B. anzeigen dass der Spieler verletzt wurde:

zorg.image = "alien_hurt"
1

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
1

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 RGB-Farbmodell

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)
1
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()
1

fill((r, g, b))

Füllt alles mit der Farbe (r, g, b)

screen.fill(MINT_CREAM)
1

Zeichnen

draw.line(start, end, (r, g, b))

Zeichnet eine Linie vom Start-Punkt zum Endpunkt

screen.draw.line((200, 100), (400, 12), BLACK)
1

draw.circle(pos, radius, (r, g, b))

Zeichnet einen Kreis.

screen.draw.circle((200, 100), 60, BLACK)
1

draw.filled_circle(pos, radius, (r, g, b))

Zeichnet einen gefüllten Kreis

screen.draw.filled_circle((200, 100), 60, ORANGE)
1

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)
1
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)
1

Text

draw.text(text, pos, color=(r, g, b))

Zeichnet Text

screen.draw.text("hello pygame zero", (150, 10), color=BLACK)
1

👉 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")
1
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
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)    
1
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!")
1
2
3
4
5
Zeile 1
Wir definieren das Unterprogramm das bei einem Mausklick aufgerufen werden soll.
Zeile 2
collidepoint liefert True, wenn der Actor zorg mit den Mauskoordinaten pos «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"    
1
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!")
1
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!")
1
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

Hinweis: mögliche Grafiken für Meteoriten

👉 alles als Zip-Datei (inkl. noch kleineren Meteoriten)

Aufgabe

Baue einige der oben vorgeschlagenen oder eigene Erweiterungs-Ideen ein