I am using pygame to create a Brick Breaker game. I have gotten everything else to work except the collisions. I tried to use collide_rect but it didn't work, but it did not give me error messages. I tried to use spritecollide, but it gives me the error message Ball object is not iterable. My goal is to detect the blocks, remove it, and increase the score. Sorry, my code is a little messy. Here is my code (The collision detection is near the bottom):
import pygame
import paddle
import ball
import block
import time
import random
import math
screenW, screenH = 1280, 720
backroundColor = [0,0,0]
offset = 30
PaddleW = 200
PaddleH = 25
PaddleX = screenW / 2 - PaddleW / 2
PaddleY = screenH - PaddleH - offset
n = 10
blockW = screenW / n #128 per block
blockH = blockW /4 + 10
pygame.init()
pygame.display.set_caption("Brick Breaker")
Display = pygame.display.set_mode((screenW, screenH))
Display.fill(backroundColor)
player = paddle.Paddle(PaddleX, PaddleY, PaddleW, PaddleH)
ballSize = 12
ballX = PaddleX
ballY = PaddleY-20
ballSpeed = -4
ballMoveY = ballSpeed
ballMoveX = ballSpeed
balls = pygame.sprite.Group()
balls = ball.Ball(ballX, ballY, ballSize)
#ballDraw = ball.Ball.draw(Display)
lives = 3
score = 0
shiftX = 0
shiftY = 0
#blocks init
'''blocks = pygame.sprite.Group()
blocks = []'''
#block1 = pygame.sprite.Group()
block1 = block.Block(10,60,blockW,blockH)
counter = 0
def displayText(text, color, posX, posY, size):
str(text)
font = pygame.font.SysFont('impact', size)
textWrite = font.render(text, True, color)
Display.blit(textWrite, (posX, posY))
def waitForKey():
wait = True
while wait:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
keys = pygame.key.get_pressed()
if keys[pygame.K_SPACE]:
wait = False
'''m=3
for y in range (4): #Spawn blocks
for i in range (0, int(screenW), int(blockW)):
blocks.append(block.Block(i+2, blockH*(y+2), blockW-4, blockH-4))'''
block1.draw(Display)
#blocks.draw = (14,14,14,14)
FPSClock = pygame.time.Clock()
FPS = 144
GameOver = False
Display.fill([130,140,230])
displayText('Brick Breaker', [200,230,100], 450,100, 80)
displayText('Use the mouse to move the paddle and bounce the ball. Try to break the bricks and not die.', [220,10,10], 120, 300,30)
displayText('Press space to begin', [0,0,0],520,510, 30)
pygame.display.flip()
waitForKey()
while not GameOver:
counter += 1/FPS
for event in pygame.event.get():
if event.type == pygame.QUIT:
if GameOver == False:
GameOver = True
mouseX = pygame.mouse.get_pos()[0]
Display.fill(backroundColor)
player.draw(Display)
player.move(mouseX)
balls.move(ballX, ballY) #pygame.draw.circle(Display, [0, 120, 120], [int(ballX), int(ballY)], ballSize)
balls.draw(Display)
'''
for x in range(len(blocks)):
blocks[x].draw(Display)
'''
#Draw blocks
block1.draw(Display)
if ballX <= 0:
ballMoveX = -ballSpeed
pygame.mixer.music.load("GungaGinga.mp3")
pygame.mixer.music.play(1)
#shiftX = random.randint(-2,2)
#shiftY = -shiftX
elif ballX >= screenW:
ballMoveX = ballSpeed
pygame.mixer.music.load("GungaGinga.mp3")
pygame.mixer.music.play(1)
#shiftX = random.randint(-2, 2)
#shiftY = -shiftX
if ballY <= 0:
ballMoveY = -ballSpeed
pygame.mixer.music.load("GungaGinga.mp3")
pygame.mixer.music.play(1)
#shiftY = random.randint(-2, 2)
#shiftX = -shiftY
elif ballY >= screenH:
pygame.mixer.music.load("OOF.mp3")
pygame.mixer.music.play(1)
ballMoveY = ballSpeed
lives -= 1
ballX = PaddleX
ballY = PaddleY - 20
balls.move(ballX, ballY)
#shiftX = 0
#shiftY = 0
time.sleep(1)
if ballY <= PaddleY + PaddleW and ballY +ballSize >= PaddleY:
if (ballX >= mouseX or ballX <= mouseX) and (ballX + ballSize <= mouseX + PaddleW/2 and ballX + ballSize >= mouseX - PaddleW/2): # or ballX + ballSize >= mouseX - PaddleW/2
ballMoveY = -ballMoveY#ballSpeed
pygame.mixer.music.load("GungaGinga.mp3")
pygame.mixer.music.play(1)
pygame.mixer.music.load("OOF.mp3")
pygame.mixer.music.play(1)
ballX += ballMoveX
ballX = ballX +shiftX
ballY += ballMoveY
ballY = ballY + shiftY
displayText('Lives: %s'%lives, [90,190,90], 20, 10, 40)
displayText('Time: %.1f' %counter, [20,190,90], 1100, 10, 40)
displayText('Score: %s' % score, [90, 190, 90], 570, 10, 40)
if counter == 5:
ballSpeed -= 1
#Collision Test
#if pygame.sprite.spritecollideany(balls, blocks):
#blocks.kill(blocks)
#hitBlock = pygame.sprite.collide_mask(blocks, player)
''' def hit(self):
hit_block = pygame.sprite.groupcollide(blocks, self.balls, False, True)
if hit_block:
return True
else:
return False'''
#is_a_collision = pygame.sprite.collide_mask(blocks, balls)
'''
if self.hit():
print("collision")
score += 10
blocks.kill(blocks)
'''
#collision
#hit_block = pygame.sprite.spritecollide(balls, block1, False, True)
#hit_block()
#pygame.Surface.get_rect(block1)
#Current colliderect not working
if pygame.sprite.collide_rect(block1, balls):
score += 10
#block1.kill()
print("collision")
#Spritecollide not working - gives error message
if pygame.sprite.spritecollide(block1, balls,False):
#block1.kill()
score += 10
pygame.display.flip()
pygame.display.update()
FPSClock.tick(FPS)
if (lives <= 0):
#You died
Display.fill([130, 140, 230])
displayText('You Lost', [200, 230, 100], 500, 100, 80)
displayText('Thanks for trying',[250, 10, 10], 540, 300, 35)
displayText('Press space to exit', [0, 0, 0], 540, 510, 30)
pygame.display.flip()
waitForKey()
GameOver = True
#balls.hit()
'''
if balls.collide():
print("collide")
score += 10
'''
pygame.quit()
Here is a sample of my classes:
import pygame
class Block(pygame.sprite.Sprite):
def __init__(self, posx, posy, width, height):
pygame.sprite.Sprite.__init__(self)
super().__init__()
#self.rect = self.image.get_rect()
self.rect = pygame.Rect(posx, posy, width, height)
self.posx = posx
self.posy = posy
self.height = height
self.width = width
def draw (self, Display):
pygame.draw.rect(Display, [6,130,183], [self.posx, self.posy, self.width, self.height])
I am using PyCharm Please let me know what to do. Thanks
The issue is the condition
if (ballX >= mouseX or ballX <= mouseX) and (ballX + ballSize <= mouseX + PaddleW/2 and ballX + ballSize >= mouseX - PaddleW/2):
The result of the 1st condition ((ballX >= mouseX or ballX <= mouseX)
) is always True
and the 2nd condition is screwed up.
If you have a range for x1
to x1+w1
and a 2nd range from x2
to x2+w2
, then the 2 ranges are overlapping if
x1 <= x2+w and x2 <= x1+w1
So the collisiton test has to be:
if ballY <= PaddleY + PaddleH and PaddleY <= ballY + ballSize:
if ballX <= mouseX + PaddleW/2 and mouseX - PaddleW/2 <= ballX + ballSize:
ballMoveY = -ballMoveY
score += 10
Anyway I recommend to use pygame.Rect
objects and the method .colliderect()
. e.g:
ball_rect = pygame.Rect(ballX, ballY, ballSize, ballSize)
paddle_rect = pygame.Rect(mouseX-PaddleW/2, PaddleY, PaddleW, PaddleH)
if ball_rect.colliderect(paddle_rect):
ballMoveY = -ballMoveY
score += 10
Since your objects are Sprites
, I recommend to to keep the .rect
attribute up to date.e.g:
class Ball(pygame.sprite.Sprite):
# [...]
def move(self, x, y):
self.rect.topleft = (x, y)
self.posx, self.posy = self.rect.topleft
class Paddle(pygame.sprite.Sprite):
# [...]
def move(self, x):
self.rect.centerx = x
self.posx = self.rect.left
Now you can use the .rect
attributes of the objects for the collision test:
if ball.rect.colliderect(player.rect):
ballMoveY = -ballMoveY
score += 10
Since both objects are sprites it is rcommend tor use pygame.sprite.collide_rect()
:
if pygame.sprite.collide_rect(ball, player):
ballMoveY = -ballMoveY
score += 10
Note, a collision of a Sprite
object and a Group
or event 2 Group
s can be found by pygame.sprite.spritecollide()
respectively pygame.sprite.groupcollide()
. e.g.:
blocks = pygame.sprite.Group()
blocks.add(block.Block(10,block1Y,blockW, blockH, blockColor))
if pygame.sprite.spritecollide(ball, blocks, False):
ballMoveY = -ballMoveY
score += 10
for block in blocks:
block.draw(Display)