Python Tutorial
- jetzt Python programmieren lernen

Breakout-Ball im Spiel zeichnen und bewegen

Als nächstes wollen wir unseren Ball für unser Breakout-Spiel zeichnen. Hierbei werden wird wieder unsere bisherige Größe nutzen. Der Ball wird eine Größe von 20 auf 20 Pixel haben.

Auch für das Zeichnen des Balls erstellen wir eine Funktion mit dem Namen „ball_zeichnen“:

def ball_zeichnen(x,y):
    pygame.draw.ellipse(fenster, SCHWARZ, [kor(x), kor(y),kor(1), kor(1)], 0)

Unser Ball benötigt eine Position in unserem Fenster. Dazu erstellen wir 2 Variablen: „ball_x“ und „ball_y“. Diese könnten wir zwar per Zufall setzen, der Einfachheit halber setzen wir diese für den Anfang einfach fix:

# Spielball Variablen
ball_x = 10
ball_y = 23

Der Ball hat noch eine Bewegungsrichtung, die durch 2 Variablen gespeichert wird. Die verwendeten Variablen sind: „ball_x_richtung“ und „ball_y_richtung“.

Dabei können diese Variablen positiv wie negativ sein und somit sind alle 4 Bewegungsrichtungen abgedeckt:

ball_x_richtung ball_y_richtung Bewegungsrichtung
1 1 recht unten
1 -1 rechts oben
-1 1 links unten
-1 -1 links oben

Der Ball muss sich immer schräg bewegen, da bei einer geraden Bewegung das Abprallen vom Schläger denn Ball exakt in die Richtung zurückwerfen würde, woher er gerade kam (was für das Spiel nicht besonders sinnvoll wäre).

Wir ergänzen unsere Variablen:

# Spielball Variablen
ball_x = 10
ball_y = 23
ball_x_richtung = 1
ball_y_richtung = 1

Wer mehr Zufall an der ersten Position haben möchte, versieht einfach der Position ball_x mit einer zufälligen Zahl zwischen 3 und 16 (ganz an den Rand macht auch nicht wirklich Sinn am Anfang):

# Spielball Variablen
ball_x = random.randint(3,16)
ball_y = 23
ball_x_richtung = 1
ball_y_richtung = 1

Innerhalb unserer Hauptprogramm-Schleife zeichnen wir nun den Ball:

# Schleife Hauptprogramm
while spielaktiv:
    # Überprüfen, ob Nutzer eine Aktion durchgeführt hat
    for event in pygame.event.get():
        if event.type == pygame.QUIT or event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
            spielaktiv = False
            print("Spieler hat beendet")

    # Ball zeichnen
    ball_zeichnen(ball_x, ball_y)

Keine Bewegung vom Ball macht natürlich wenig Spaß in einem Spiel. Daher werden wir denn Ball pro Durchgang weiterbewegen über unsere 2 Variablen ball_x_richtung und ball_y_richtung.

    ball_x += ball_x_richtung
    ball_y += ball_y_richtung

    # Ball zeichnen
    ball_zeichnen(ball_x, ball_y)

Während dem entwickeln unseres Breakout-Spiels ist diese Bewegung nun ein wenig schnell. Daher schalten wir auf Zeitlupe um. Unser Refresh-Zeiten sind zu schnell – also schalten wir die Zeitlupe ein über clock.tick(3).

Jetzt sieht man schön, wie unser Ball in Zeitlupe aus dem Bildschirmfenster verschwindet und eine Spur hinterlässt.

Ball flüchtet aus Bildschirmfenster
Ball flüchtet aus Bildschirmfenster

Zur Kontrolle lassen wir uns in der Konsole die aktuelle Position ausgeben:

    # Ball zeichnen
    ball_zeichnen(ball_x, ball_y)
    print("Ballposition X=", ball_x, " / Y = ", ball_y)

Wir sehen in der Konsole, dass ab einer Position von Y=30 der Ball als nächstes unten verschwindet. Solange wir noch keinen Schläger haben, soll der Ball im Spiel bleiben. Daher ändern wir die Y-Richtung des Balls, sobald er die untere Bildschirmkante erreicht.

    # Spiellogik
    if ball_x >= 19:
        ball_x_richtung = -1

    ball_x += ball_x_richtung
    ball_y += ball_y_richtung

    # Ball zeichnen
    ball_zeichnen(ball_x, ball_y)
    print("Ballposition X=", ball_x, " / Y = ", ball_y)

Aber bevor unser Ball an allen 4 Seiten abprallt, müssen wir alle 4 Seiten kontrollieren. Auch das kommt an den Punkt innerhalb der Hauptschleife bei der Spiellogik (genaugenommen Bewegungslogik).

Ball prallt an allen 4 Seiten ab
Ball prallt an allen 4 Seiten ab
# Schleife Hauptprogramm
while spielaktiv:
    # Überprüfen, ob Nutzer eine Aktion durchgeführt hat
    for event in pygame.event.get():
        if event.type == pygame.QUIT or event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
            spielaktiv = False
            print("Spieler hat beendet")

    # Spiellogik
    if ball_x <= 0:
        ball_x_richtung = 1
    if ball_x >= 19:
        ball_x_richtung = -1
    if ball_y <= 0:
        ball_y_richtung *= -1
    if ball_y >= 29:
        ball_y_richtung *= -1

    ball_x += ball_x_richtung
    ball_y += ball_y_richtung

    # Ball zeichnen
    ball_zeichnen(ball_x, ball_y)
    print("Ballposition X=", ball_x, " / Y = ", ball_y)

    # Fenster aktualisieren
    pygame.display.flip()

    # Refresh-Zeiten festlegen
    clock.tick(3)

pygame.quit()

Nun wollen wir die Felder löschen, wo der Ball sich davor befunden hat. Dazu zeichnen wir einfach ein weißes Viereck an die alte Position. Würden wir es mit der Trennung zwischen Spielelogik und Ausgabelogik (Ball zeichnen etc.) nicht genau nehmen, würden wir keine weiteren Variable benötigen. Wir wollen aber so nachvollziehbar wie möglichprogrammieren und daher trennen wir. Es gibt 2 neue Variablen für die alte Position des Balls.

# Spielball Variablen
ball_x = random.randint(3,16)
ball_y = 23
ball_x_richtung = 1
ball_y_richtung = 1
ball_x_alt = 0
ball_y_alt = 0

In diesen Variablen werden immer die alten Positionen gespeichert, bevor diese neu gesetzt werden:

    ball_x_alt = ball_x
    ball_y_alt = ball_y
    ball_x += ball_x_richtung
    ball_y += ball_y_richtung

    # Ball zeichnen
    ball_zeichnen(ball_x, ball_y)

Und nun müssen wir nur noch die alte Position löschen. Dazu erstellen wir uns eine Funktion „element_loeschen“ (ähnlich der Zeichenfunktion für unsere Mauersteine (nur mit Weiß als Farbe):

def element_loeschen(spalte,reihe):
    pygame.draw.rect(fenster, WEISS, [kor(spalte), kor(reihe),kor(1),kor(1)])

Und diese Funktion wir vor zeichnen der neuen Ballposition aufgerufen:

    # Ball zeichnen
    element_loeschen(ball_x_alt, ball_y_alt)
    ball_zeichnen(ball_x, ball_y)

Wir haben ein interessantes Ergebnis:

Ball prallt an Mauersteinen nicht ab sondern hinterlässt eine Schneise
Ball prallt an Mauersteinen nicht ab sondern hinterlässt eine Schneise