Search code examples
pythonpython-3.xlistpygame

popping a particular element from a list in pygame


I recently started making Atari Breakout in Pygame. I encountered a problem that I don't know how to fix. Whenever I run my code, and the ball comes in contact with the block, the block that's colliding with it, doesn't disappear as it should, but the last block from the list. (I made a list of 21 pygame.Surfaces and iterate over it and blit every single one). Obviously, if the ball touches the last block from the list, the correct one disappears. Please can you help me?

This is my code:

import pygame, random, math

pygame.init()

screen = pygame.display.set_mode((800,600))
pygame.display.set_caption('Atari Breakout')

player = pygame.image.load('player copy.png')
ball = pygame.image.load('poland.png')
blocks = [pygame.image.load('block.png') for i in range(21)]


x, y = 25,25
blockx, blocky = [], []
for i in range(21):
    if i % 7 == 0:
        y += 52
        x = 25
    blockx.append(x)
    blocky.append(y)
    x += (50 + 64)

ballx, bally = 400, 300

balldx, balldy = 4,4

score = 0
score_font = pygame.font.Font('freesansbold.ttf', 32)

def show_score():
    score_text = score_font.render('Score : {}'.format(score), True, (255,255,255))
    screen.blit(score_text, (0,0))

def isCollision(x1,y1,x2,y2):
    ballRect = ball.get_rect(topleft = (x1, y1))
    playerRect = player.get_rect(topleft = (x2, y2))
    return ballRect.colliderect(playerRect)

def isCollision2(x1,y1,x2,y2):
    ballRect = ball.get_rect(topleft = (x1,y1))
    blockRect = blocks[i].get_rect(topleft = (x2,y2))
    return ballRect.colliderect(blockRect)

def blit_blocks():
    for i in range(len(blocks)):
        screen.blit(blocks[i], (blockx[i], blocky[i]))
running = True

while running:

    if score >= 21:
        running = False
    screen.fill((0,0,0))


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

    pos = pygame.mouse.get_pos()

    for i, block in enumerate(blocks):

        if isCollision2(ballx, bally, blockx[i], blocky[i]):
            balldy *= -1
            score += 1
            blocks.pop(i)

    if isCollision(ballx, bally, pos[0], 525):
        balldy *= -1
    if bally <= 0:
        balldy *= -1
    if bally >= 570:
        running = False
    if ballx <= 0:
        balldx *= -1
    if ballx >= 770:
        balldx *= -1

    ballx += balldx
    bally += balldy

    screen.blit(player, (pos[0], 525))
    screen.blit(ball, (ballx,bally))
    blit_blocks()
    show_score()
    pygame.display.update()

Solution

  • What you actually do ist manipulate a list (by pop) while you iterate it. I recommend to travers a copy of the list (blocks[:]) and to manipulate the original list. e.g:

    i = 0
    for x, y, block in zip(blockx[:], blocky[:], blocks[:]):
    
            if isCollision2(ballx, bally, x, y):
                blockx.pop(i)
                blocky.pop(i)
                blocks.pop(i)
                balldy *= -1
                score += 1
            else:
                i += 1
    

    Anyway, I recommend to create a class Block:

    class Block():
        def __init__(self, image, x, y)
            self.image = image
            self.x = x
            self.y = y
    
    image = pygame.image.load('block.png')
    x, y = 25,25
    blocks = []
    for i in range(21)
        if i % 7 == 0:
            y += 52
            x = 25
        blocks.append(Block(image, x, y))
        x += (50 + 64)
    
    i = 0
    for block in block[:]:
        if isCollision2(ballx, bally, block.x, block.y):
            balldy *= -1
            score += 1
            block.pop(i)
        else:
            i += 1