Breakout Bricks nach 3 Hits verschwinden lassen

Hier werden alle anderen GUI-Toolkits sowie Spezial-Toolkits wie Spiele-Engines behandelt.
Antworten
kwon
User
Beiträge: 39
Registriert: Samstag 2. Mai 2020, 11:48

Hallo,
ich bemühe mich gerade in dem Breakoutspiel alle Bricks erst nach 3 Treffern (Hits) verschwinden zu lassen.
Leider haben manche Bricks das Verhalten - andere aber nicht (sie verschwinden nach einem Treffer).

Vielleicht ist jemand so freundlich und schaut sich den Code mal an...
Für Hinweise was ich falsch mache wäre ich sehr dankbar...
Ich bitte um Hilfe...

Code: Alles auswählen

# !/usr/bin/env python3
import pygame, random
from paddle import Paddle
from ball import Ball
from brick import Brick
pygame.init()
WHITE = (255, 255, 255)
DARKBLUE = (0, 0, 139)
ball_Width = 22
ball_Height = 22
Screen_Width = 800
Screen_Height = 600
screen = pygame.display.set_mode((Screen_Width, Screen_Height))
pygame.display.set_caption("Breakout")
pygame.mixer.init()
pygame.mixer.music.load('images/Ketsa - Holding The Line.mp3')
pygame.mixer.music.set_volume(0.7)
pygame.mixer.music.play(-1)


def main():
    try:
        all_sprites = pygame.sprite.Group()
        all_bricks = pygame.sprite.Group()
        score = 0
        lives = 5
        pygame.mouse.set_visible(1)
        paddle = Paddle(screen.get_rect())
        all_sprites.add(paddle)
        # Zufallsrichtung des Balles
        speed_x = random.randint(4, 6)
        speed_y = random.randint(4, 6)
        if speed_x != 0:
            pass
        else:
            speed_x = 5
        if speed_y != 0:
            pass
        else:
            speed_y = 1
        ball = Ball(speed_x, speed_y)
        ball.rect.x = 10
        ball.rect.y = 300
        all_sprites.add(ball)
        brick_coord_list = [
            32, 32, 32, 64, 32, 96, 32, 128, 32, 160, 32, 192, 32, 224, 64, 32, 64, 128, 96,
            32, 96, 64, 96, 96, 96, 128, 96, 160, 96, 192, 96, 224, 160, 32, 160, 64, 160,
            96, 160, 128, 160, 160, 160, 192, 160, 224, 192, 224, 224, 224, 288, 32, 288, 64, 288,
            96, 288, 128, 288, 160, 288, 192, 288, 224, 320, 32, 352, 32, 320, 128, 352, 128, 320,
            224, 352, 224, 416, 32, 448, 64, 480, 96, 512, 128, 544, 160, 576, 192, 608, 224,
            416, 224, 448, 192, 480, 160, 544, 96, 576, 64, 608, 32
                        ]

        i = 0
        while i < len(brick_coord_list):
            # Zufallszahl für blauen oder grünen Brick
            randomnumber = random.randint(1, 2)
            brick = Brick(randomnumber, 3, brick_coord_list[i], brick_coord_list[i + 1])
            all_sprites.add(brick)
            all_bricks.add(brick)
            i = i + 2

        # to control how fast the screen updates
        clock = pygame.time.Clock()
        while True:
            # Limit to 60 frames per second
            clock.tick(60)

            all_sprites.update()
            # Check if the ball is bouncing against the 4 walls
            if ball.rect.x >= Screen_Width - ball_Width / 2:
                ball.speed_x = -ball.speed_x
            if ball.rect.x <= 0:
                ball.speed_x = -ball.speed_x
            if ball.rect.y <= 0:
                ball.speed_y = -ball.speed_y
            if ball.rect.y >= Screen_Height - ball_Height / 2:
                ball.bounce()
                lives -= 1
                if lives == 0:
                    font = pygame.font.Font(None, 74)
                    text = font.render("Game over", 1, WHITE)
                    screen.blit(text, (250, 300))
                    pygame.display.flip()
                    pygame.time.wait(3000)
                    # stop the game
                    return
            # Detect collisions between the ball and the paddle
            # if pygame.sprite.spritecollide(ball, paddle):
            if ball.rect.colliderect(paddle):
                ball.bounce()
            # Check if the ball collides with any of the bricks
            brick_collision = pygame.sprite.spritecollide(ball, all_bricks, False)
            for brick in brick_collision:
                ball.bounce()
                brick.hit(score)
                if len(all_bricks) == 0:
                    font = pygame.font.Font(None, 74)
                    text = font.render("Level complete", 1, WHITE)
                    screen.blit(text, (200, 300))
                    pygame.display.flip()
                    pygame.time.wait(3000)
                    # stop the game
                    return
            screen.fill(DARKBLUE)
            font = pygame.font.Font(None, 34)
            text = font.render("Score: " + str(score), 1, WHITE)
            screen.blit(text, (20, 10))
            text = font.render("Lives: " + str(lives), 1, WHITE)
            screen.blit(text, (650, 10))

            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    return
                elif event.type == pygame.MOUSEMOTION:
                    paddle.update2(event.pos)
            all_sprites.draw(screen)
            # update the screen with what we have drawn
            pygame.display.flip()
    finally:
        pygame.quit()


if __name__ == "__main__":
    main()
Paddle:

Code: Alles auswählen

import pygame


class Paddle(pygame.sprite.Sprite):
    def __init__(self, playfield_rect):
        super().__init__()
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load('images/ship_fertig.png').convert()
        self.rect = self.image.get_rect()
        self.rect.centerx = self.rect.x
        self.playfield_rect = playfield_rect
        self.rect = self.image.get_rect()
        self.rect.bottom = self.playfield_rect.bottom

    def update2(self, position):
        x, _ = position
        self.rect.centerx = x
        self.rect = self.rect.clamp(self.playfield_rect)
Brick:

Code: Alles auswählen

import pygame


class Brick(pygame.sprite.Sprite):
    # This class represents a brick. It derives from the "Sprite" class in Pygame.
    def __init__(self, randomnumber, hits, a, b):
        super().__init__()
        if randomnumber == 1:
            self.image = pygame.image.load('images/greenbrick.png').convert()
        else:
            self.image = pygame.image.load('images/bluebrick.png').convert()
        self.rect = self.image.get_rect()
        self.rect.x = a
        self.rect.y = b
        self.hits = hits

    def hit(self, score):
        self.hits -= 1
        if self.hits == 0:
            self.kill()
            score += 1
Ball:

Code: Alles auswählen

import pygame


class Ball(pygame.sprite.Sprite):
    def __init__(self, speed_x, speed_y):
        super().__init__()
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load('images/ballblue.png').convert()
        self.rect = self.image.get_rect()
        self.rect.centerx = self.rect.x
        self.rect.centery = self.rect.y
        self.speed_x = speed_x
        self.speed_y = speed_y

    def update(self):
        self.rect = self.rect.move(self.speed_x, self.speed_y)

    def bounce(self):
        self.speed_x = self.speed_x
        self.speed_y = -self.speed_y
kwon
User
Beiträge: 39
Registriert: Samstag 2. Mai 2020, 11:48

Hallo,
irgendwo muss ich die Treffer (Hits) ja speichern...
Was würdet ihr mir empfehlen (in einem Dictionary: (Paare = Blöcke und Hits) oder die Hits in den Blöcken selbst speichern)?
Letzteres habe ich versucht (in den Blöcken (Bricks) die Hits zu speichern (siehe Klasse Bricks)) - allerdings scheint das nicht zu funktionieren...

Vielleicht könnt ihr mir einen Hinweis geben...

Dankeschön...

Viele Grüße,
kwon
__deets__
User
Beiträge: 11922
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das gehoert schon in die Bricks. Das darin ein Fehler ist, weil score nicht bekannt ist, das ist klar? Und statt diesem Gewuerge mit den Koordinaten der Bricks macht man das so:

Code: Alles auswählen

for x, y in [
    (100, 200),
    (123, 455),
     ....]:
    brick = Brick(.., x, y)
kwon
User
Beiträge: 39
Registriert: Samstag 2. Mai 2020, 11:48

Hallo,
leider bekomme ich es nicht hin, dass alle Bricks 3 Hits vertragen und dann erst verschwinden.
Macht man das so?:

Brick:

Code: Alles auswählen

import pygame


class Brick(pygame.sprite.Sprite):
    # This class represents a brick. It derives from the "Sprite" class in Pygame.

    def __init__(self, randomnumber, a, b):
        super().__init__()
        if randomnumber == 1:
            self.image = pygame.image.load('images/greenbrick.png').convert()
        else:
            self.image = pygame.image.load('images/bluebrick.png').convert()
        self.rect = self.image.get_rect()
        self.rect.x = a
        self.rect.y = b
        self.hits = 3

    def hit(self, score):
        self.hits -= 1
        if self.hits == 0:
            self.kill()
            score += 1
        return score
Ich bitte um Hilfe...
kwon
User
Beiträge: 39
Registriert: Samstag 2. Mai 2020, 11:48

Das ist der aktuelle Zwischenstand, den ich habe...
Alleine komme ich nicht mehr weiter...
Über Hilfe würde ich mich freuen...

main:

Code: Alles auswählen

# !/usr/bin/env python3
import pygame, random
from paddle import Paddle
from ball import Ball
from brick import Brick
pygame.init()
WHITE = (255, 255, 255)
DARKBLUE = (0, 0, 139)
ball_Width = 22
ball_Height = 22
Screen_Width = 800
Screen_Height = 600
screen = pygame.display.set_mode((Screen_Width, Screen_Height))
pygame.display.set_caption("Breakout")
pygame.mixer.init()
pygame.mixer.music.load('images/Ketsa - Holding The Line.mp3')
pygame.mixer.music.set_volume(0.7)
pygame.mixer.music.play(-1)


def main():
    try:
        all_sprites = pygame.sprite.Group()
        all_bricks = pygame.sprite.Group()
        score = 0
        lives = 5
        hits = 3
        pygame.mouse.set_visible(1)
        paddle = Paddle(screen.get_rect())
        all_sprites.add(paddle)
        # random direction of the ball
        speed_x = random.randint(4, 6)
        speed_y = random.randint(4, 6)
        if speed_x != 0:
            pass
        else:
            speed_x = 5
        if speed_y != 0:
            pass
        else:
            speed_y = 1
        ball = Ball(speed_x, speed_y, all_bricks)
        ball.rect.x = 10
        ball.rect.y = 300
        all_sprites.add(ball)
        brick_coord_list = [
            32, 64, 32, 96, 32, 128, 32, 160
                        ]
        i = 0
        while i < len(brick_coord_list):
            # random number for blue and green Brick
            randomnumber = random.randint(1, 2)
            brick = Brick(randomnumber, brick_coord_list[i], brick_coord_list[i + 1], 3)
            all_sprites.add(brick)
            all_bricks.add(brick)
            i = i + 2
        # to control how fast the screen updates
        clock = pygame.time.Clock()
        while True:
            # Limit to 60 frames per second
            clock.tick(60)
            all_sprites.update()
            all_bricks.update()
            # Check if the ball is bouncing against the 4 walls
            if ball.rect.x >= Screen_Width - ball_Width / 2:
                ball.speed_x = -ball.speed_x
            if ball.rect.x <= 0:
                ball.speed_x = -ball.speed_x
            if ball.rect.y <= 0:
                ball.speed_y = -ball.speed_y
            if ball.rect.y >= Screen_Height - ball_Height / 2:
                ball.bounce()
                lives -= 1
                if lives == 0:
                    font = pygame.font.Font(None, 74)
                    text = font.render("Game over", 1, WHITE)
                    screen.blit(text, (250, 300))
                    pygame.display.flip()
                    pygame.time.wait(3000)
                    # stop the game
                    return
            # Detect collisions between the ball and the paddle
            #if pygame.sprite.spritecollide(ball, paddle):
            if ball.rect.colliderect(paddle):
                ball.bounce()
            # Check if the ball collides with any of the bricks
            hit_list = []
            hit_list = pygame.sprite.spritecollide(ball, all_bricks, False)
            if hit_list:
                ball.collision(hit_list, ball, score)
                #if pygame.sprite.spritecollide(ball, all_bricks, False):
                # calculate the list of bricks hit
                # score = brick.takehit(ball, score)
                if len(all_bricks) == 0:
                    font = pygame.font.Font(None, 74)
                    text = font.render("Level complete", 1, WHITE)
                    screen.blit(text, (200, 300))
                    pygame.display.flip()
                    pygame.time.wait(3000)
                    # stop the game
                    return
            screen.fill(DARKBLUE)
            font = pygame.font.Font(None, 34)
            text = font.render("Score: " + str(score), 1, WHITE)
            screen.blit(text, (20, 10))
            text = font.render("Lives: " + str(lives), 1, WHITE)
            screen.blit(text, (650, 10))

            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    return
                elif event.type == pygame.MOUSEMOTION:
                    paddle.update2(event.pos)
            all_sprites.draw(screen)
            # update the screen with what we have drawn
            pygame.display.flip()
    finally:
        pygame.quit()


if __name__ == "__main__":
    main()
ball:

Code: Alles auswählen

import pygame


class Ball(pygame.sprite.Sprite):
    def __init__(self, speed_x, speed_y, all_bricks):
        super().__init__()
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load('images/ballblue.png').convert()
        self.rect = self.image.get_rect()
        self.rect.centerx = self.rect.x
        self.rect.centery = self.rect.y
        self.speed_x = speed_x
        self.speed_y = speed_y

    def update(self):
        self.rect = self.rect.move(self.speed_x, self.speed_y)

    def bounce(self):
        self.speed_x = self.speed_x
        self.speed_y = -self.speed_y

    def collision(self, hit_list, score):
        for brick in hit_list:
            brick.takehit(self, score)
brick:

Code: Alles auswählen

import pygame


class Brick(pygame.sprite.Sprite):
    # This class represents a brick. It derives from the "Sprite" class in Pygame.

    def __init__(self, randomnumber, a, b, hits):
        pygame.sprite.Sprite.__init__(self)
        #super().__init__()
        if randomnumber == 1:
            self.image = pygame.image.load('images/greenbrick.png').convert()
        else:
            self.image = pygame.image.load('images/bluebrick.png').convert()
        self.rect = self.image.get_rect()
        self.rect.x = a
        self.rect.y = b
        self.hits = hits

    def takehit(self, ball, score):
        # the ball has collided with *this* brick
        self.hits -= 1
        if self.hits == 0:
            self.kill()
            score += 1
        ball.bounce()
        return score
rogerb
User
Beiträge: 877
Registriert: Dienstag 26. November 2019, 23:24

@kwon,

ich kann nur vermuten: Wenn ein Ball mit einem Brick kollidiert wird das wahrscheinlich dadurch erkannt, dass sich beide Sprites überlappen.
Die Methode bounce() sorgt dafür, dass sich die Geschwindigkeit umkehrt. Das bedeutet, aber nicht, dass es im nächsten Schleifendurchlauf keine Überlappung mehr gibt. Es hat sich ja lediglich die Bewegungsrichtung geändert.
Dadurch kann sofort eine weitere Kollision erkannt werden, die wiederum zu einer Bewegungsumkehr führt. Das geschieht so schnell, dass in Sekundenbruchteilen der hit-counter auf Null reduziert wird.

Es reicht also nicht die Geschwindigkeit umzukehren sondern. der Ball muss auch an eine Position gesetzt werden, die nicht mit einem Brick überlappt.
Beispiel:
Der Ball bewegt sich mit einer Geschwindigkeit von 3 Pixel pro Frame. Es kann passieren, das sich der Ball Sprite mit dem Brick Sprite zum Beispiel 2 Pixel überlappt. Bei einer Kollision muss der Ball also an eine Position gesetzt werden, die 2 Pixel vom Brick entfernt ist.

Ich hoffe das macht Sinn, ist auch nur geraten.
Du kannst ja mal einen Breakpoint an der Stelle setzen wo die Kollision stattfindet und dir die Positionen ausgeben lassen.
Antworten