Python Tutorial
- jetzt Python programmieren lernen

Spiel Pong: Bewegung des Balls - Elemente animieren

Pong mit Python Pygame programmieren

Viele Spiele leben von der Aktion auf dem Bildschirm. Hier kommt nun die Animation von Elementen in unsere Programmierung. Wir werden im folgenden Kapitel einen Ball animieren, wie wir diese aus dem Spieleklassiker Pong kennen.

Hier spielen 2 Spieler quasi Tischtennis (Ping-Pong) und der Blick auf das Spielfeld ist von oben auf die Platte, den Ball und die Schläger. Grafisch extrem simpel – was halt bei der Erstveröffentlich von Pong 1972 so möglich war. Lustigerweise war der Begriff „Ping Pong“ geschützt und so wurde das Spiel Pong genannt.

Wir wollen jetzt nur einen Ball animieren, der immer, wenn der den Fensterrand erreicht, wieder im entsprechenden Winkel abprallt.

Dazu benötigen wir unsere Grundgerüst aus dem Kapitel „Grundgerüst für Spiele mit Pygame“.

# Importieren der Pygame-Bibliothek
import pygame

# initialisieren von pygame
pygame.init()

# genutzte Farbe
ORANGE  = ( 255, 140, 0)
ROT     = ( 255, 0, 0)
GRUEN   = ( 0, 255, 0)
SCHWARZ = ( 0, 0, 0)
WEISS   = ( 255, 255, 255)

# Fenster öffnen
screen = pygame.display.set_mode((640, 480))

# Titel für Fensterkopf
pygame.display.set_caption("Unser erstes Pygame-Spiel")

# solange die Variable True ist, soll das Spiel laufen
spielaktiv = True

# Bildschirm Aktualisierungen einstellen
clock = pygame.time.Clock()

# Schleife Hauptprogramm
while spielaktiv:
    # Überprüfen, ob Nutzer eine Aktion durchgeführt hat
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            spielaktiv = False
            print("Spieler hat Quit-Button angeklickt")

    # Spiellogik hier integrieren

    # Spielfeld löschen
    screen.fill(SCHWARZ)

    # Spielfeld/figuren zeichnen

    # Fenster aktualisieren
    pygame.display.flip()

    # Refresh-Zeiten festlegen
    clock.tick(60)

pygame.quit()

Bei unserem Klassiker ist der Hintergrund des Spielfelds schwarz. Daher können wir jedes Mal, wenn wir unser Spielfeld neu aufbauen (sprich löschen) einfach unseren „screen“ mit der Farbe Schwarz füllen:

    # Spielfeld löschen
    screen.fill(SCHWARZ)

Zur Erinnerung. Die Konstanten für die Farben (wie z.B. SCHWARZ) haben wir am Anfang unseres Pygame-Programmes definiert:

# genutzte Farbe
ORANGE  = ( 255, 140, 0)
SCHWARZ = ( 0, 0, 0)

Ball zeichnen für unser Spiel

Im letzten Kapitel haben wir bereits einen Kreis gezeichnet. Genau diesen wollen wir nun als Spielball nutzen.

    # Spielfeld/figuren zeichnen
    pygame.draw.ellipse(screen, WEISS, [10,30,20,20])
unser Spielball bei Pong

Wir platzieren unseren Ball am Anfang mit dieser Anweisung an den Punkt X=10 und Y=30

Allerdings wollen wir unseren Ball animieren. Also setzten wir diese Werte nicht fest als Zahl, sondern nutzen Variablen. Nennen wir diese ballpos_x und ballpos _y. Unsere Ballposition steckt also in diesen beiden Variablen, die wir vor der Hauptroutine definieren müssen:

# Definieren der Variablen
ballpos_x = 10
ballpos_y = 30

# Schleife Hauptprogramm
while spielaktiv:

Jetzt können wir diese Variablen beim Zeichnen unseres Kreises nutzen:

pygame.draw.ellipse(screen, WEISS, [ballpos_x, ballpos_y, 20, 20])

Unser Ball ruht nun in der Ecke und das Spiel wird extrem spannungslos ohne Bewegung (man könnte auch langweilig dazu sagen).

Ball bewegen (sprich animieren)

Jetzt können wir in unserer Hauptroutine diese 2 Werte bei jedem Schleifendurchlauf ändern und der Ball bewegt sich dann. Das muss innerhalb der Hauptroutine geschehen!

# Schleife Hauptprogramm
while spielaktiv:
    # Überprüfen, ob Nutzer eine Aktion durchgeführt hat
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            spielaktiv = False

    # Spielfeld/figuren zeichnen
    pygame.draw.ellipse(screen, WEISS, [ballpos_x, ballpos_y, 20, 20])

    # bewegen unseres Kreises
    ballpos_x += 4
    ballpos_y += 4

Hurra, unser Spielball bewegt sich nach unten rechts. Und hoppla – er verschwindet auf Nimmerwiedersehen aus dem sichtbaren Bereich (und rollt unter irgendeinen Schrank bestimmt).

Also müssen wir darauf reagiere, wenn der Ball den Spielrand erreicht. Über das Erstellen der Fenstergröße wissen wir, ab wann der Ball aus dem sichtbaren Bereich verschwindet:

# Fenster öffnen
screen = pygame.display.set_mode((640, 480))

Und auch hier sollten wir nicht direkt eingetragenen Werten (im Beispiel 640 und 480) arbeiten, sondern mit Variablen (oder in dem Fall Konstanten, da diese Größen sich ja während dem gesamten Spielablauf nicht ändern.

Setzen wir also hier diese Zahlen als Konstanten:

FENSTERBREITE = 640
FENSTERHOEHE = 480

# Fenster öffnen
screen = pygame.display.set_mode((FENSTERBREITE, FENSTERHOEHE))

Die gewählten Begriffe im deutschen sind zwar sehr lang, dafür sollte aber klar sein, welche Werte sich dahinter verbergen. Und jetzt können wir kontrollieren, ob der Ball das sichtbare Fenster verlassen möchte.

Ball am Fensterrand abprallen lassen

Wir kennen die Position des Balls über die Variablen ballpos_x und ballpos _y und wir kennen die Größe des Spielfelds anhand der Konstanten FENSTERBREITE und FENSTERHOEHE.

Überprüfen wir als erstes, ob der Ball am unteren Rand ankommt. Hier wissen wir, wenn die die Variable ballpos _y größer ist als die Kontante FENSTERHOEHE, dann sollten wir die Bewegungsrichtung ändern. Die Bewegungsrichtung ändern sich, indem wir die ballpos _y wieder verkleinern – sprich anstelle von „plus 4“ werden wir dann „minus 4“ rechnen.

Also benötigen wir eine Abfrage, bevor wir unseren Kreis bewegen. Bisher haben wir für unsere Bewegung:

    # bewegen unseres Kreises
    ballpos_x += 4
    ballpos_y += 4

Natürlich wollen wir auch hier nicht mit fest eingetragenen Werten wie im Beispiel „4“ arbeiten, sondern auch hier eine Variable einsetzen. Eigentlich sollten wir die Variablen irgendwas nennen wie „bewegungsaenderung_x“ – aber das ist deutlich zu lang. Wir nutzen dafür die Variablen mit der Bezeichnung bewegung_x und bewegung_y. Auch diese müssen außerhalb unserer Hauptschleife das erste Mal festgelegt werden um dann in der Hauptschleife genutzt zu werden.

# Definieren der Variablen
ballpos_x = 10
ballpos_y = 30

bewegung_x = 4
bewegung_y = 4

Und innerhalb der Hauptschleife ändern wir von „ballpos_x += 4“ zu:

    # bewegen unseres Kreises
    ballpos_x += bewegung_x
    ballpos_y += bewegung_y

Jetzt wollen wir über eine if-Abfrage die Berührung des Fensterrands kontrollieren:

    if ballpos_y < FENSTERHOEHE:
        ballpos_y += bewegung_y
    else:
        ballpos_y -= bewegung_y

Das wäre im ersten Augenblick eine Variante, die aber leider dazu führt, dass der Ball bei Verlassen des unteren Fensterbereichs nicht nach oben läuft sondern dann nach rechts wegrutscht.

Wir müssen also EINMAL die Bewegungsrichtung ändern. Dazu greifen wir auf den mathematischen Trick von * -1 . So können wir die Bewegungsrichtung „umschalten“. Zur Erinnerung an die alten Schulzeiten die 2 wichtigen Fälle von „mal minus 1“

Haben wir ein + wird durch * -1 aus dem Plus ein Minus.

Haben wir ein – wird durch * -1 aus dem Minus ein Plus.

Die benötigte Variable für die beiden Bewegungsrichtungen haben wir bereits und können dann unser Matematik darauf anwenden.

    if ballpos_y > FENSTERHOEHE:
        bewegung_y = bewegung_y * -1

Wenn man es testet, dann verschwindet der Ball fast unten (darum kümmern wir uns später) und springt dann nach oben, um rechts zu verschwinden. Also kümmern wir uns um den rechten Rand. Hier dieselbe Vorgehensweise, nur halt für unsere x-Bewegung:

    if ballpos_x > FENSTERBREITE:
        bewegung_x = bewegung_x * -1

Jetzt überlebt unser Ball unten und rechts, aber verschwindet oben aus dem Fensterrahmen bzw. links. Also auch hierfür die entsprechende Abfrage, wobei wir einfach mit 0 vergleichen (was den oberen und den linken Fensterrand darstellt:

Unsere bisher komplette Abfrage:


    if ballpos_y > FENSTERHOEHE:
        bewegung_y = bewegung_y * -1

    if ballpos_x > FENSTERBREITE:
        bewegung_x = bewegung_x * -1

    if ballpos_y < 0:
        bewegung_y = bewegung_y * -1

    if ballpos_x < 0:
        bewegung_x = bewegung_x * -1

Das funktioniert zwar fast schon, aber kürzer ist schöner – also machen wir aus den 8 Zeilen nur noch 4 und fassen die if-Anfragen zusammen:

    if ballpos_y > FENSTERHOEHE or ballpos_y < 0:
        bewegung_y = bewegung_y * -1

    if ballpos_x > FENSTERBREITE or ballpos_x < 0:
        bewegung_x = bewegung_x * -1

Jetzt haben wir noch den Schönheitsfehler, dass der Ball unten und rechts aus dem sichtbaren Bereich fast verschwindet. Unsere Rechnung ist bisher nicht exakt richtig: wir vergleich die Ballposition mit der Fensterbreite (bzw. Höhe). Aber eigentlich ist die Breite (bzw. Höhe des Balls) auch noch auschlaggebend. Daher sollten wir berechnen:

(FENSTERHOEHE – Balldurchmesser)

Und auch hier gehört eine Variable (bzw. Konstante her). Wir müssen diese Kontante bereits (wie die anderen auch) im Vorfeld definieren, können diese beim Zeichnen unseres Balls verwenden und dann bei der if-Abfrage einsetzen:

Wieder wollen wir einen sprechenden Namen haben – wer die mathematischen Bezeichnungen gewohnt ist, wird „d“ für Durchmesser nehmen, sprich die Konstantenbezeichnung wäre dann „BALL_D“ – wie machen für 100% Transparenz den wuchtigen Namen: „BALL_DURCHMESSER“

Also Vorneweg definieren:

# Definieren der Variablen/Konstanten
ballpos_x = 10
ballpos_y = 30

BALL_DURCHMESSER = 20

Und einsetzen beim Kreiszeichnen:


pygame.draw.ellipse(screen, WEISS, [ballpos_x, ballpos_y, BALL_DURCHMESSER, BALL_DURCHMESSER])

Und noch in der Abfrage wegen Fensterrand:

    if ballpos_y > FENSTERHOEHE - BALL_DURCHMESSER or ballpos_y < 0:
        bewegung_y = bewegung_y * -1

    if ballpos_x > FENSTERBREITE - BALL_DURCHMESSER or ballpos_x < 0:
        bewegung_x = bewegung_x * -1

Und somit haben wir einen wunderbar herumspringenden Ball und wir können die Spielfeldgröße, Ballgröße und Geschwindigkeit durch Ändern an der entsprechenden Variablen (bzw. Konstanten) schnell ändern und alle Programmteile reagieren darauf.

Hier der komplette Quellcode:


# Importieren der Pygame-Bibliothek
import pygame, math

# initialisieren von pygame
pygame.init()

# genutzte Farbe
ORANGE  = ( 255, 140, 0)
ROT     = ( 255, 0, 0)
GRUEN   = ( 0, 255, 0)
SCHWARZ = ( 0, 0, 0)
WEISS   = ( 255, 255, 255)

FENSTERBREITE = 640
FENSTERHOEHE = 480

# Fenster öffnen
screen = pygame.display.set_mode((FENSTERBREITE, FENSTERHOEHE))

# Titel für Fensterkopf
pygame.display.set_caption("Unser erstes Pygame-Spiel")

# solange die Variable True ist, soll das Spiel laufen
spielaktiv = True

# Bildschirm Aktualisierungen einstellen
clock = pygame.time.Clock()

# Definieren der Variablen/Konstanten
ballpos_x = 10
ballpos_y = 30

BALL_DURCHMESSER = 20

bewegung_x = 4
bewegung_y = 4

# Schleife Hauptprogramm
while spielaktiv:
    # Überprüfen, ob Nutzer eine Aktion durchgeführt hat
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            spielaktiv = False

    # Spiellogik hier integrieren

    # Spielfeld löschen
    screen.fill(SCHWARZ)

    # Spielfeld/figuren zeichnen
    pygame.draw.ellipse(screen, WEISS, [ballpos_x, ballpos_y, BALL_DURCHMESSER, BALL_DURCHMESSER])

    # bewegen unseres Kreises
    ballpos_x += bewegung_x
    ballpos_y += bewegung_y

    if ballpos_y > FENSTERHOEHE - BALL_DURCHMESSER or ballpos_y < 0:
        bewegung_y = bewegung_y * -1

    if ballpos_x > FENSTERBREITE - BALL_DURCHMESSER or ballpos_x < 0:
        bewegung_x = bewegung_x * -1

    # Fenster aktualisieren
    pygame.display.flip()

    # Refresh-Zeiten festlegen
    clock.tick(60)

pygame.quit()
quit()