Search code examples
pythonpygamedrawing

Repeat drawing in pygame


I am making a BrickBreaker/Breakout game using pygame. I want my game to create rows of bricks until the a brick hits the paddle but I can't figure out how to do so. Currently it only creates 1 row of bricks. I also want to make the bricks dissappear when the ball hits them. Currently when the bricks are hit they just move off the screen, but I want them to get permanently deleted.

Thanks!

My current code:

# Brick Breaker Game

import pygame
import random

# Initialize the pygame
pygame.init()

# create the screen
screen = pygame.display.set_mode((800, 600))

# background
background = pygame.image.load("realBackground.png")

# Title and Icon
pygame.display.set_caption("Brick Breaker")
icon = pygame.image.load("Brick Breaker Icon.png")
pygame.display.set_icon(icon)

# Paddle
paddleImage = pygame.image.load("scaledPaddle.png")
paddleX = 335
paddleY = 550
paddleX_change = 0

# BrickCoordinates
brickX = []
brickY = []
brickX_change = []
brickY_change = []
numOfBricks = 6

brickXValue = 15
for i in range(numOfBricks):
    brickX.append(brickXValue)
    brickY.append(0)
    brickX_change.append(0.3)
    brickY_change.append(0)
    # Add 120 if thick lines in middle bricks
    # Add 110 if uniform thickness
    brickXValue += 130


#Bricks
yellowBrickImage = pygame.image.load("yellowBrick.png")
greenBrickImage = pygame.image.load("greenBrick.png")
blueBrickImage = pygame.image.load("blueBrick.png")
pinkBrickImage = pygame.image.load("pinkBrick.png")

# ball
ballImage = pygame.image.load("Ball.png")
ballX = 380
ballY = 280
ballX_change = 1.5
ballY_change = 1.5

#Score
scoreValue = 0
font = pygame.font.Font("Neufreit-ExtraBold.otf",24)
textX = 10
textY = 10

def showScore(x,y):
    score = font.render("Score : " + str(scoreValue), True, (255,255,255))
    screen.blit(score,(x,y))

def paddle(x, y):
    screen.blit(paddleImage, (x, y))

def yellowBrick(x, y, i):
    screen.blit(yellowBrickImage, (x, y))

def greenBrick(x, y, i):
    screen.blit(greenBrickImage, (x, y))

def blueBrick(x, y, i):
    screen.blit(blueBrickImage, (x, y))

def pinkBrick(x, y, i):
    screen.blit(pinkBrickImage, (x, y))

def ball(x, y):
    screen.blit(ballImage, (x, y))

#To pick random brick colours
colourOfBrick = []
for i in range(numOfBricks):
    colourOfBrick.append(random.randint(1,4))



# Game Loop (makes sure game is always running)
running = True

while running:
    # To change background colour
    screen.fill((128, 128, 128))
    # background image
    screen.blit(background, (0, 0))

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

        # If keystroke is pressed check whether left or right
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                paddleX_change = -5
            if event.key == pygame.K_RIGHT:
                paddleX_change = 5
        if event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
                paddleX_change = 0

    # Checking boudries of paddle
    paddleX += paddleX_change

    if paddleX <= 0:
        paddleX = 0
    elif paddleX >= 669:
        paddleX = 669

    #Draw Rectangles around bricks
    brickRect = []
    for i in range(numOfBricks):
        brickRect.append(pygame.draw.rect(screen, (0, 0, 0), (brickX[i], brickY[i], 120, 42),1))

    # Brick Movement
    for i in range(numOfBricks):
        brickY[i] += brickY_change[i]
        if brickY[i] <= 0:
            brickY_change[i] = 0.3
        elif brickY[i] >= 500:
            brickY_change[i] = -0.3
        # Makes brick show up on screen
        if colourOfBrick[i] == 1:
            yellowBrick(brickX[i], brickY[i], i)
        elif colourOfBrick[i] == 2:
            greenBrick(brickX[i], brickY[i], i)
        elif colourOfBrick[i] == 3:
            blueBrick(brickX[i], brickY[i], i)
        elif colourOfBrick[i] == 4:
            pinkBrick(brickX[i], brickY[i], i)

    # Ball Movement and boundary checking
    ballX += ballX_change

    if ballX <= 0:
        ballX_change *= -1
    elif ballX >= 760:
        ballX_change *= -1

    ballY += ballY_change

    if ballY <= 0:
        ballY_change *= -1
    elif ballY >= 560:
        ballX = 380
        ballY = 280


    # Paddle and Ball Collision
    if ballY > 530 and ballY < 535 and (ballX+20) < paddleX + 131 and (ballX+20) > paddleX:
        ballY_change *= -1

    paddle(paddleX, paddleY)
    ballCircle = pygame.draw.circle(screen, (255,0,0), (int(ballX+20),int(ballY+20)) ,20)
    ball(ballX, ballY)

    #Ball and Brick Collision
    for i in range (numOfBricks):
        if ballCircle.colliderect(brickRect[i]):
            if abs(ballCircle.top - brickRect[i].bottom < 10) and ballY_change < 0:
                brickX[i] = -400
                ballY_change *= -1
                scoreValue += 1

    showScore(textX,textY)
    pygame.display.update()


Solution

  • In your code all bricks have brickY.append(0) so all bricks are in one row. You have to create bricks with different Y values to create other rows.

    You may need nested for-loops for this - like this

    row_number = 3
    
    brickYValue = 0
    
    for row in range(row_number):
    
        brickXValue = 15
    
        for column in range(numOfBricks):
            brickX.append(brickXValue)
    
            brickY.append(brickYValue)
    
            brickX_change.append(0.3)
            brickY_change.append(0)
            brickXValue += 130
    
        # after `for column`    
        brickYValue += 15  # row height
    

    But it will create more bricks then numOfBricks - you will have numOfBricks*row_number bricks so you would have to change other for-loops and use range(numOfBricks*row_number) instead of range(numOfBricks)

    Or you should learn how to use for-loop without range()

    brickRect = []
    
    for x, y in zip(brickX, brickY):
        brickRect.append(pygame.draw.rect(screen, (0, 0, 0), x, y, 120, 42),1))
    

    BTW: you should also learn how to use pygame.Rect() to keep size and position of brick, paddle and ball. Rect() has methods to check collisions and you would no need long if ... and ... and ...


    EDIT: I added rows in this code but I made many other changes so it may not be good example.

    I draw surfaces instead of loading images so everyone can run it without images.

    import pygame
    import random
    
    # --- classes ---
    
    class Brick():
    
        def __init__(self, x, y, image):
            self.image = image
            self.rect = self.image.get_rect(x=x, y=y)
            self.x = x
            self.y = y
            self.x_change = 0
            self.y_change = 1
    
        def draw(self, screen):
            self.rect.x = int(self.x)
            self.rect.y = int(self.y)
            screen.blit(self.image, self.rect)
            pygame.draw.rect(screen, (0, 0, 0), self.rect, 1)
    
        def update(self):
            self.y += self.y_change
            self.rect.y = int(self.y)
    
            if self.rect.y <= 0:
                self.y_change = 1
            elif self.rect.y >= 500:
                self.y_change = -1
    
    class Ball():
    
        def __init__(self):
            #self.image = pygame.image.load("Ball.png")
    
            self.image = pygame.Surface((16, 16)).convert_alpha()
            self.image.fill((0,0,0,0)) # transparent background
            pygame.draw.circle(self.image, (255,255,255), (8, 8), 8)
    
            self.rect = self.image.get_rect(centerx=380, centery=280)
            self.x = 380
            self.y = 280
            self.x_change = 3
            self.y_change = 3
    
        def reset(self):
            self.x = 380
            self.y = 280
    
        def draw(self, screen):
            self.rect.centerx = int(self.x)
            self.rect.centery = int(self.y)
            screen.blit(self.image, self.rect)
    
        def update(self):
            # Ball Movement and boundary checking
            self.x += self.x_change
            self.rect.centerx = int(self.x)
    
            if self.rect.left <= 0:
                self.x_change *= -1
            elif self.rect.right >= 800:
                self.x_change *= -1
    
            self.y += self.y_change
            self.rect.centery = int(self.y)
    
            if self.rect.top <= 0:
                self.y_change *= -1
            elif self.rect.bottom >= 600:
                self.reset()
    
    class Paddle():
    
        def __init__(self):
            #self.image = pygame.image.load("scaledPaddle.png")
            self.image = pygame.Surface((100, 30))
            self.image.fill((255,0,0))
    
            self.rect = self.image.get_rect(x=335, y=550)
            self.x_change = 0
            self.y_change = 0
    
        def reset(self):
            self.rect.x = 335
            self.rect.y = 550
    
        def draw(self, screen):
            screen.blit(self.image, self.rect)
    
        def update(self):
            # Checking boudries of paddle
            self.rect.x += self.x_change
    
            if self.rect.left <= 0:
                self.rect.left = 0
            elif self.rect.right >= 800:
                self.rect.right = 800
    
    class Score():
    
        def __init__(self):
            #self.font = pygame.font.Font("Neufreit-ExtraBold.otf", 24)
            self.font = pygame.font.SysFont(None, 24)
            self.value = 0
            self.x = 10
            self.y = 10
    
        def reset(self):
            self.value = 0
    
        def draw(self, screen):
            self.image = self.font.render("Score : " + str(self.value), True, (255,255,255))
            self.rect = self.image.get_rect(x=self.x, y=self.y)
            screen.blit(self.image, self.rect)
    
    # --- functions ---
    
    # empty
    
    # --- main ---
    
    pygame.init()
    screen = pygame.display.set_mode((800, 600))
    
    pygame.display.set_caption("Brick Breaker")
    #icon = pygame.image.load("Brick Breaker Icon.png")
    #pygame.display.set_icon(icon)
    
    # Background Image
    
    #background_image = pygame.image.load("realBackground.png")
    
    background_image = pygame.Surface((800,600))
    for y in range(5, 600, 25):
        for x in range(5, 800, 25):
            color = random.choice([(255,128,128), (128,255,128), (128,128,255)])
            background_image.fill(color, [x,y,15,15])
    
    # Brick Images
    
    #brick_images = [
    #    pygame.image.load("yellowBrick.png"),
    #    pygame.image.load("greenBrick.png"),
    #    pygame.image.load("blueBrick.png"),
    #    pygame.image.load("pinkBrick.png"),
    #]
    
    brick_images = [
        pygame.Surface((100, 30)),
        pygame.Surface((100, 30)),
        pygame.Surface((100, 30)),
        pygame.Surface((100, 30)),
        pygame.Surface((100, 30)),
        pygame.Surface((100, 30)),
    ]    
    
    brick_images[0].fill((255,0,0))
    brick_images[1].fill((0,255,0))
    brick_images[2].fill((0,0,255))
    brick_images[3].fill((255,255,0))
    brick_images[4].fill((255,0,255))
    brick_images[5].fill((0,255,255))
    
    # Objects
    
    paddle = Paddle()
    ball   = Ball()
    score  = Score()
    
    # bricks
    rows_number = 5
    cols_number = 7
    
    all_bricks = []
    
    y = 0
    for row in range(rows_number):
        x = 50
        for col in range(cols_number):
            color_image = random.choice(brick_images)
            brick = Brick(x, y, color_image)
            all_bricks.append(brick)
            x += 100
        y += 30
    
    # Game Loop
    
    clock = pygame.time.Clock()
    running = True
    
    while running:
    
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
    
            # If keystroke is pressed check whether left or right
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT:
                    paddle.x_change = -5
                if event.key == pygame.K_RIGHT:
                    paddle.x_change = 5
            if event.type == pygame.KEYUP:
                if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
                    paddle.x_change = 0
    
        # --- updates ---
    
        paddle.update()
        ball.update()
    
        # Bricks Update
        for brick in all_bricks:
            brick.update()
    
        # Ball and Paddle Collision
        if ball.rect.colliderect(paddle):
            ball.y_change *= -1
    
        # Ball and Bricks Collision
        for brick in all_bricks:
            if ball.rect.colliderect(brick):
                brick.x = -400
                ball.y_change *= -1
                score.value += 1
    
        # --- draws ---
    
        # To change background colour
        # screen.fill((128, 128, 128)) # you don't need it if background fill all screen
    
        # background image
        screen.blit(background_image, (0, 0))
    
        for brick in all_bricks:
            brick.draw(screen)
    
        paddle.draw(screen)
        ball.draw(screen)
    
        score.draw(screen)
    
        pygame.display.flip()
    
        clock.tick(60) # 60 FPS (Frames Per Second) on all computers
    
    # --- end ---
    
    pygame.quit() 
    

    enter image description here