popping a particular element from a list in pygame

Question:

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()
Asked By: Stqrosta

||

Answers:

it is a bad practice to pop from a list while you are iterating over this will cause unexpected behaviour, here:

for i, block in enumerate(blocks):

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

you can use:

new_blocks = []
for i, block in enumerate(blocks):

        if isCollision2(ballx, bally, blockx[i], blocky[i]):
            balldy *= -1
            score += 1
        else:
            new_blocks.append(block)

blocks = new_blocks
Answered By: kederrac

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
Answered By: Rabbid76
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.