Ein Unterprogramm stellt einen wiederverwendbaren Code-Block dar. Es kann durch übergebene Argumente mit verschiedenen Situationen umgehen. Zudem kann es einen Wert berechnen und diesen als Rückgabewert an den Aufrufenden zurückliefern
# Game-Loop
In einem sogenannten Game-Loop wird in Pygame-Zero automatisch das Spiel neu berechnet. Dabei wird immer wieder das Folgende erledigt:
- Checke Auftreten von Ereignissen – Z.B. Tastendruck oder Mausklick zur Steuerung der Spielfigur. (siehe Ereignisse im Kapitel «Verzweigungen»)
- Berechne den neuen Spielstand – Hat sich der Spieler seit dem letzten Aufruf bewegt? Haben sich andere Figuren bewegt? Dann müssen neue Positionen berechnet werden. Oder hat es eine Kollision gegeben? Dann verliert der Spieler ein Leben. (siehe Animation)
- Stelle den neuen Spielstand dar – Wenn sich etwas verändert hat, so muss das Spiel neu gezeichnet werden.
"""
Autor: S. Forster
Datum: 3.12.2020
Beschreibung: einfache Animation
"""
import pgzrun
TITLE = "Alien bewegt sich"
WIDTH = 800
HEIGHT = 200
zorg = Actor('alien')
zorg.x = 100
zorg.y = 100
zorg.vx = 10
def draw():
screen.clear()
zorg.draw()
def update(dt):
delta_x = dt * zorg.vx
zorg.x = zorg.x + delta_x
pgzrun.go()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# Aufruf eines Unterprogrammes
Wir haben bereits Unterprogramme verwendet: Z.B. zorg.draw() ruft das Unterprogramm draw des Actors mit dem Namen zorg auf. Oder auch screen.fill(MINT_CREAM) ruft das Unterprogramm fill von screen auf und übergibt ihm eine Farbe namens MINT_CREAM. (Die Farbe muss vorher definiert werden!)
Nach dem gleichen Schema ruft der Game-Loop das von uns definierte Unterprogramm draw auf!
Im Allgemeinen ruft man ein Unterprogramm auf, indem man seinen Namen, gefolgt von runden Klammern mit – falls vorhanden – durch Kommas getrennte Werte, sogenannte Argumente, schreibt.
# Definition eines Unterprogrammes
Wenn wir ein eigenes Unterprogramm definieren, so verlassen wir die eigentliche Haupt-Sequenz des Programms. Dies kennzeichnen wir durch Einrücken:
def hallo():
print("Hallo Welt")
print("Hallo Zorg")
print("hi")
hallo()
2
3
4
5
6
- Zeile 1
- definiert ein Unterprogramm
namemit Argumentenargs - Zeilen 2-3
- gehören zur Sequenz des Unterprogramms, werden also erst aufgerufen wenn irgendwo das Unterprogramm aufgerufen wird
- Zeile 5
- gehört nicht mehr zum Unterprogramm
- Zeile 6
- ruft das Unterprogramm auf
Das Unterprogramm hallo wird zwar definiert, aber erst am Schluss aufgerufen. Der Output wäre also:
hi
Hallo Welt
Hallo Zorg
Achtung
Bevor ein Unterprogramm aufgerufen werden kann, muss es definiert sein!
# Beispiel
def zorg_hurt():
# Setzt das Bild von Zorg auf "verletzt"
zorg.image = "alien_hurt"
def zorg_happy():
# Setzt das Bild von Zorg auf "normal"
zorg.image = "alien"
2
3
4
5
6
7
- Zeile 1
- wir definieren das Unterprogramm
hurt - Zeile 3
- das Unterprogramm besteht aus einem Befehl: es ändert das Bild des Aliens in «alien_hurt»
Hinweis
Wenn wir ein Unterprogramm definieren, dann dokumentieren wir es gerade mit einem Kommentar. Der Kommentar kommt gleich nach nach der Definitions-Zeile und wird – wie der Rest des Unterprogramm – eingerückt.
# Argumente
Ein Unterprogramm kann kein, ein oder mehrere Argumente entgegennehmen. Damit wir ein Unterprogramm mit den korrekten Argumenten «füttern» können, müssen wir aber wissen, was es tut und was es als Argument(e) erwartet.
# Übergeben von Argumenten beim Aufruf
Um das Fenster farbig zu füllen, müssen wir eine Farbe angeben. Das Zeichnungs-Unterprogramm des Actors hingegen benötigt kein Argument:
MINT_CREAM = (245,255,250)
screen.fill(MINT_CREAM)
zorg.draw()
2
3
Die folgenden Aufrufe von Unterprogrammen ergeben keinen Sinn und führen zu Error-Meldungen:
MINT_CREAM = (245,255,250)
screen.fill() # fill erwartet als Argument eine Farbe
zorg.draw(MINT_CREAM) # draw hat kein Argument
2
3
Ebenso spielt die Reihenfolge der Argumente eine Rolle: Das screen.circle-Unterprogramm erwartet als erstes Argument den Mittelpunkt als Koordinaten-Paar, dann den Radius und als drittes die Farbe.
BLACK = (255,255,255)
screen.draw.circle((200, 100), 60, BLACK)
2
# Entgegennehmen von Argumenten bei der Definition
Wenn wir uns die Definition des Unterprogrammes update im obersten Beispiels anschauen, so sehen wir dort, dass das Unterprogramm genau ein Argument erhält: def update(dt): sagt eigentlich:
Definiere Unterprogramm mit Namen
updateund Argumentdt
Im eingerückten Teil der folgt, kann nun der Wert des Argumentes dt wie eine Variable verwendet werden:
def update(dt):
delta_x = dt * zorg.vx
zorg.x = zorg.x + delta_x
2
3
# Rückgabewerte
Gewisse Unterprogramme berechnen etwas und liefern den Wert zurück.
# mit Rückgabewerten arbeiten
Wir können z.B. die Distanz und den Winkel zwischen zwei Actors bestimmen:
"""
Autor: S. Forster
Datum: 3.12.2020
Beschreibung: Distanz und Winkel zweier Actors
"""
import pgzrun
WIDTH = 600
HEIGHT = 500
zorg = Actor('alien')
zorg.x = 100
zorg.y = 100
zira = Actor('alien')
zira.x = 500
zira.y = 400
def draw():
screen.clear()
zorg.draw()
zira.draw()
distance = zorg.distance_to(zira)
print("Distanz :", distance)
zorg.angle = zorg.angle_to(zira)
pgzrun.go()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
- Zeile 24 & 25
- mit
distance_toberechnen wir die Distanz zwischenzorgundziraund geben diese aus – laut Pythagoras:
- Zeile 27
- Wir berechnen den Winkel zwischen
zorgundziramitangle_tound verwenden den Rückgabewert sofort, umzorgin Richtungzirablicken zu lassen.
# Wert als Rückgabewert zurückliefern
Dies geschieht mit dem Schlüsselwort return gefolgt von dem zurückzugebenden Wert. return ist immer die letzte Anweisung eines Unterprogramms!
def is_zorg_hurt():
return zorg.image == "alien_hurt"
2
Das Doppelgleich == vergleicht zwei Werte. Sind sie identisch ergibt der Vergleich True, sonst False.
# 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
dtzurü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!