Ein Unterprogramm stellt einen wiederverwendbaren Code-Block dar. Dieses kann durch übergebene Argumente mit verschiedenen Situationen umgehen. Zudem kann es einen Wert berechnen und diesen als Rückgabewert zurückliefern

# Game-Loop

In einem sogenannten Game-Loop wird in Pygame-Zero automatisch das Spiel neu berechnet. Dabei wird immer wieder folgendes erledigt:

  1. Checke Auftreten von Ereignissen – Z.B. Tastendruck oder Mausklick zur Steuerung der Spielfigur. (siehe Ereignisse)
  2. Berechne den neuen Spielstand – Hat sich der Spieler 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)
  3. Stelle den neuen Spielstand dar – Wenn sich etwas verändert hat, so muss das Spiel neu gezeichnet werden.
Der Game-Loop mit den Aufrufen der Unterprogramme
"""
Autor: S. Forster
Datum: 3.12.2019
Beschreibung: einfache Animation
"""

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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 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 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()
1
2
3
4
5
6
Zeile 1
definiert ein Unterprogramm name mit Argumenten args
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:

Border

hi
Hallo Welt
Hallo Zorg

Hinweis

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"
1
2
3
4
5
6
7
Zeile 1
wir definieren das Unterprogramm hurt
Zeile 2
das Unterprogramm besteht aus einem Befehl: es ändert das Bild des Aliens in «alien_hurt»

Hinweis

Wenn wir eine Funktion definieren, dann dokumentieren wir sie gerade mit einem Kommentar. Der Kommentar kommt gleich nach nach der Definitions-Zeile und wird – wie der Rest der Funktion – 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()
1
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
1
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)
1
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 update und Argument dt

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
1
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 der Winkel zwischen zwei Actors bestimmen:

"""
Autor: S. Forster
Datum: 3.12.2019
Beschreibung: Distanz und Winkel zweier Actors
"""

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)
1
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
Zeile 23 & 24
mit distance_to berechnen wir die Distanz zwischen zorg und zira und geben diese aus – laut Pythagoras:
Zeile 26
Wir berechnen den Winkel zwischen zorg und zira mit angle_to und verwenden den Rückgabewert sofort, um zorg in Richtung zira blicken 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"
1
2

# 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

Aufgabe

Schaffst du es den Alien vertikal zu Bewegen?
Und diagonal? und leicht diagonal?

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 sich zunimmt – am Besten mit einer Gravitationsbeschleunigung!

Letzte Änderung: 11.12.2019, 10:25:44