Zum Hauptinhalt springen

Kollision mit Blocks

Wir haben nun bereits so viel Vorarbeit geleistet, dass das Kollidieren von Ball und Blocks ein Kinderspiel ist!

Wir brauchen wieder eine neue Methode auf der Ball-Klasse und nennen diese hits_blocks() (Zeilen 57-58). Analog zu hits_paddle() verwenden wir darin auch wieder die pygame.sprite.spritecollide()-Funktion, nur diesmal eben mit der blocks-Gruppe. Wenn der Ball auch nur einen einzigen Block berührt, wird hier True zurückgegeben. Zudem übergeben wir als drittes Argument hier jetzt eben True: wir wollen, dass getroffene Blocks automatisch verschwinden.

Das war's auch schon fast. Wir müssen die Abfrage nur noch verwenden und bei einem Treffer wieder die y-Richtung des Balls umdrehen (Zeilen 150-151).

Logik verbessern

Diese Logik liesse sich auch noch verbessern. Sinnvollerweise würde ein Treffer an der Seite eines Blocks nicht die y-, sondern x-Richtung umdrehen.

Leider sagt uns die pygame.sprite.spritecollide() aber nichts darüber aus, wo genau ein Block getroffen wurde. Im Falle eines Treffers bräuchte es also noch etwas zusätzlichen Code im Stil von hits_side_of_window(), um die Kollision hier realistischer zu gestalten. Der Einfachheit halber belassen wir es in diesem Tutorial aber bei der aktuellen Lösung.

import pygame

# Spiel initialisieren
pygame.init()

# Einstellungen für das Fenster
window_width = 800
window_height = 600

# Hintergrundfarbe festlegen
background_color = pygame.color.Color("black")

# Einstellungen für das Paddle
paddle_y = 540
paddle_speed = 3

# Pygame-Fenster vorbereiten
pygame.display.set_caption("Breakout 👾")
screen = pygame.display.set_mode([window_width, window_height])

# Zugriff auf die Uhr -> damit können wir die Geschwindigkeit des Spiels anpassen
clock = pygame.time.Clock()

class Ball(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
ball_image = pygame.image.load("sprites/58-Breakout-Tiles.png")
self.image = pygame.transform.scale(ball_image, (50, 50))
self.rect = self.image.get_rect()
self.rect.center = (x, y)
self.direction = (-1, -2)

def move(self):
new_x = self.rect.center[0] + self.direction[0]
new_y = self.rect.center[1] + self.direction[1]
self.rect.center = (new_x, new_y)

def flip_x_direction(self):
new_x_direction = self.direction[0] * -1
new_y_direction = self.direction[1]
self.direction = (new_x_direction, new_y_direction)

def flip_y_direction(self):
new_x_direction = self.direction[0]
new_y_direction = self.direction[1] * -1
self.direction = (new_x_direction, new_y_direction)

def hits_top_of_window(self):
return self.rect.top <= 0

def hits_side_of_window(self):
return self.rect.left <= 0 or self.rect.right >= window_width

def hits_paddle(self):
return pygame.sprite.spritecollide(self, paddles, False)

def hits_block(self):
return pygame.sprite.spritecollide(self, blocks, True)

class Paddle(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
paddle_image = pygame.image.load("sprites/49-Breakout-Tiles.png")
self.image = pygame.transform.scale(paddle_image, (150, 40))
self.rect = self.image.get_rect()
self.rect.center = (x, y)

def left(self):
new_x = self.rect.center[0] - paddle_speed
new_y = self.rect.center[1]
self.rect.center = (new_x, new_y)

def right(self):
new_x = self.rect.center[0] + paddle_speed
new_y = self.rect.center[1]
self.rect.center = (new_x, new_y)

class Block(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("sprites/01-Breakout-Tiles.png")
self.image = pygame.transform.scale(self.image, (150, 40))
self.rect = self.image.get_rect()
self.rect.center = (x, y)

ball = Ball(500, 300)
balls = pygame.sprite.Group()
balls.add(ball)

paddle = Paddle(window_width / 2, paddle_y)
paddles = pygame.sprite.Group(paddle)

blocks = pygame.sprite.Group()
blocks.add([
Block(100, 50),
Block(250, 50),
Block(400, 50),
Block(550, 50),
Block(700, 50),

Block(100, 90),
Block(250, 90),
Block(400, 90),
Block(550, 90),
Block(700, 90),

Block(100, 130),
Block(250, 130),
Block(400, 130),
Block(550, 130),
Block(700, 130),

Block(100, 170),
Block(250, 170),
Block(400, 170),
Block(550, 170),
Block(700, 170),

Block(100, 210),
Block(250, 210),
Block(400, 210),
Block(550, 210),
Block(700, 210),
])

run = True
while run:
clock.tick(100)
screen.fill(background_color)

ball.move()
balls.update()
balls.draw(screen)

paddles.update()
paddles.draw(screen)

blocks.update()
blocks.draw(screen)

if ball.hits_top_of_window():
ball.flip_y_direction()

if ball.hits_side_of_window():
ball.flip_x_direction()

if ball.hits_paddle():
ball.flip_y_direction()

if ball.hits_block():
ball.flip_y_direction()

pygame.display.flip()

keys_pressed = pygame.key.get_pressed()
if keys_pressed[pygame.K_LEFT]:
paddle.left()
if keys_pressed[pygame.K_RIGHT]:
paddle.right()

for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False

Wir nähern uns dem Ende — und zwar wortwörtlich. In den nächsten beiden Schritten wollen wir nämlich noch dafür sorgen, dass das Spiel ein sauberes Ende hat, wenn der Spieler entweder alle Blocks zerstört hat (Sieg) oder den Ball verliert (Niederlage).