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