Kollisionskontrolle: Aktion bei Berührung der Spielerfigur
Unser Spiel macht natürlich nur Spaß, wenn im Spiel die Schläger auch eine Wirkung zeigen und der Ball dann entsprechend physikalisch korrekt abprallt.
Dazu müssen wir im Programm auch mitbekommen, ob der Ball den Schläger berührt oder verfehlt. Dazu ist eine Kollisionskontrolle im Pygame integriert, die das für uns als Programmierer sehr einfach macht. Diese Anweisung ist colliderect
. Wenn wir die Anweisung ansehen, steckt dort 2 Wörter darin: collide (engl. für „kollidieren“) und die Abkürzung rect (rectangle) steht für ein Rechteck.
Wir können also mit einem Befehl überprüfen, ob eine Kollision zwischen 2 Rechtecken vorliegt. Auch wenn unser Ball als Kreis gezeichnet ist, funktioniert die Kollisionskontrolle trotzdem hervorragend. Wir wollen unsere Spielerfigur player1
und unseren Spielball ball
auf eine Kollision überprüfen:
player1.colliderect(ball)
Wir erhalten bei der Kontrolle auf eine Kollision zwischen der Spielerfigur und dem Ball ein True
als Rückgabewert, wenn eine Kollision vorliegt. Also können wir hier eine if
-Abfrage durchführen:
if player1.colliderect(ball):
print("Zusammenstoß Spieler 1 und Ball")
Jetzt müssen wir darauf reagieren und die X-Bewegung umdrehen. Mathematisch funktioniert das über das Multiplizieren mit -1:
if player1.colliderect(ball):
print("Zusammenstoß Spieler 1 und Ball")
bewegung_x = bewegung_x * -1
Wenn wir den Code probieren, dann sieht die Bewegung sehr merkwürdig aus und wir bekommen als Rückmeldung in der Konsole, dass es mehrere Kollisionen gab. Was ist passiert. Da unser Ball sich mit einer Geschwindigkeit von 6 Pixeln bewegen kann, wird die Kollision irgendwo zwischen Pixel 1 und 6 vom Programm erst wahrgenommen. Pro Durchgang in der Hauptroutine liegt immer 6 Pixel Unterschied zur letzten Ballposition. Würde die gleich am Anfang liegen (Pixel 1) würde er wie gewünscht abprallen. Liegt die Kollision allerdings bei Pixel 2, ändert er die Richtung, aber es liegt sofort wieder eine Kollision vor und er ändert wieder die Richtung. Das kann einige Male passieren. So eine Reaktion wollen wir nicht wirklich.
Um uns nicht in ewigen Berechnungen zu verlieren, setzen wir die Ballposition ballpos_x
einfach vor dem Schläger und fertig.
if player1.colliderect(ball):
print("Zusammenstoß P1")
bewegung_x = bewegung_x * -1
ballpos_x = 40
In unserer if
-Schleife haben wir nun noch weitere Möglichkeiten:
- wir können die Anzahl der Ballwechsel zählen
- wir können bei jedem Ballwechsel die Größe des Schlägers verkleinern und somit das Spiel schwieriger machen
Um die Ballwechsel zu zählen, benötigen wir noch eine Variable ballwechsel
, die wir vor der Schleife erstellen müssen, damit wir diese bei Kollision erhöhen können.
# Definieren der Variablen/Konstanten
schlaegerhoehe = 220
ballwechsel = 0
# Schleife Hauptprogramm
while spielaktiv:
Und innerhalb der Kollisionskontrolle:
if player1.colliderect(ball):
print("Zusammenstoß P1")
bewegung_x = bewegung_x * -1
ballpos_x = 40
ballwechsel += 1
Wollen wir noch die Größe des Schlägers beeinflussen, dann verkleinern wir die Variable für die Schlägerhöhe:
if player1.colliderect(ball):
print("Zusammenstoß P1")
bewegung_x = bewegung_x * -1
ballpos_x = 40
ballwechsel += 1
schlaegerhoehe -= 5
Das Ganze müssen wir noch für den zweiten Spieler genauso machen, allerdings wird dort die Ballposition links vor dem Schläger gesetzt. Die brachiale Variante ist ballpos_x = 570
- besser wäre die Berechnung über unsere Konstante für Spielfeldbreite und Schlägerbreite (darf man machen, wenn alles so weit funktioniert – daher hier die brachiale Variante:
if player2.colliderect(ball):
print("Zusammenstoß P2")
bewegung_x = bewegung_x * -1
ballpos_x = 570
ballwechsel += 1
schlaegerhoehe -= 5
Und nun können wir endlich das Spiel richtig testen. Viel Spaß beim ersten Match:
Sinnvoll ist noch die Ausgabe der Anzahl der Ballwechsel:
ausgabetext = "Ballwechsel: " + str(ballwechsel)
font = pygame.font.SysFont(None, 70)
text = font.render(ausgabetext, True, ROT)
screen.blit(text, [100, 10])
# Fenster aktualisieren
pygame.display.flip()
Und der komplette Quellcode für unser Spiel Pong:
# Importieren der Pygame-Bibliothek
import pygame, math
from pygame.locals import *
# 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
windowSurface = pygame.display.set_caption("Unser erstes Pygame-Spiel")
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
spielfigur_1_x = 20
spielfigur_1_y = 20
spielfigur_1_bewegung = 0
spielfigur_2_x = FENSTERBREITE - (2 * 20)
spielfigur_2_y = 20
spielfigur_2_bewegung = 0
schlaegerhoehe = 220
ballwechsel = 0
# 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 Quit-Button angeklickt")
if event.type == pygame.KEYDOWN:
print("Spieler hat Taste gedrückt")
# Taste für Spieler 1
if event.key == pygame.K_UP:
print("Spieler hat Pfeiltaste runter gedrückt")
spielfigur_1_bewegung = -6
elif event.key == pygame.K_DOWN:
print("Spieler hat Pfeiltaste runter gedrückt")
spielfigur_1_bewegung = 6
# Taste für Spieler 2
elif event.key == pygame.K_w:
print("Spieler 2 hat w für hoch gedrückt")
spielfigur_2_bewegung = -6
elif event.key == pygame.K_s:
print("Spieler 2 hat s für runter gedrückt")
spielfigur_2_bewegung = 6
# zum Stoppen der Spielerbewegung
if event.type == pygame.KEYUP:
print("Spieler hat Taste losgelassen")
# Tasten für Spieler 1
if event.key == pygame.K_UP or event.key == pygame.K_DOWN:
print("Spieler 1 stoppt bewegung")
spielfigur_1_bewegung = 0
# Tasten für Spieler 2
elif event.key == pygame.K_w or event.key == pygame.K_s:
print("Spieler 1 stoppt bewegung")
spielfigur_2_bewegung = 0
# Spiellogik
if spielfigur_1_bewegung != 0:
spielfigur_1_y += spielfigur_1_bewegung
if spielfigur_1_y < 0:
spielfigur_1_y = 0
if spielfigur_1_y > FENSTERHOEHE - schlaegerhoehe:
spielfigur_1_y = FENSTERHOEHE - schlaegerhoehe
if spielfigur_2_bewegung != 0:
spielfigur_2_y += spielfigur_2_bewegung
if spielfigur_2_y < 0:
spielfigur_2_y = 0
if spielfigur_2_y > FENSTERHOEHE - schlaegerhoehe:
spielfigur_2_y = FENSTERHOEHE - schlaegerhoehe
# Spielfeld löschen
screen.fill(SCHWARZ)
# Spielfeld/figuren zeichnen
# -- Ball
ball = pygame.draw.ellipse(screen, WEISS, [ballpos_x, ballpos_y, BALL_DURCHMESSER, BALL_DURCHMESSER])
# -- Spielerfigur 1
player1 = pygame.draw.rect(screen, WEISS, [spielfigur_1_x, spielfigur_1_y, 20, schlaegerhoehe])
# -- Spielerfigur 2
player2 = pygame.draw.rect(screen, WEISS, [spielfigur_2_x, spielfigur_2_y, 20, schlaegerhoehe])
# bewegen unseres Balls/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
# if ball.colliderect(player1):
if player1.colliderect(ball):
print("Zusammenstoß P1")
bewegung_x = bewegung_x * -1
ballpos_x = 40
ballwechsel += 1
schlaegerhoehe -= 5
if player2.colliderect(ball):
print("Zusammenstoß P2")
bewegung_x = bewegung_x * -1
ballpos_x = 570
ballwechsel += 1
schlaegerhoehe -= 5
ausgabetext = "Ballwechsel: " + str(ballwechsel)
font = pygame.font.SysFont(None, 70)
text = font.render(ausgabetext, True, ROT)
screen.blit(text, [100, 10])
# Fenster aktualisieren
pygame.display.flip()
# Refresh-Zeiten festlegen
clock.tick(60)
pygame.quit()