I recently started making Atari Breakout in Pygame and I encountered a weird bug. Whenever the ball touches the left side of the paddle, it bounces off normally but when it's on the right, it flies through the paddle. Please help, because I don't know how to fix it.
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')
ballx, bally = 400, 300
balldx, balldy = 2,2
def isCollision(x1,y1,x2,y2):
distance = math.sqrt(math.pow(x1 - x2, 2) + math.pow(y1 - y2, 2))
if distance <= 32:
return True
else:
return False
running = True
while running:
screen.fill((0,0,0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pos = pygame.mouse.get_pos()
if isCollision(ballx, bally, pos[0], 525):
balldy *= -1
if bally >= 0:
balldy *= -1
if bally <= 570:
balldy *= -1
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))
pygame.display.update()
Actually isCollision computes the Euclidean distance between the top left of the rectangle which surrounds the ball and the top left of the paddle.
The shape of the player (paddle) is a rectangle with a very long and a very short side. Thus the collision algorithm doesn't work at all. I recommend to use pygame.Rect objects and colliderect, to detect a collision between the ball and the player. e.g.:
def isCollision(x1,y1,x2,y2):
ballRect = ball.get_rect(topleft = (x1, y1))
playerRect = player.get_rect(topleft = (x2, y2))
return ballRect.colliderect(playerRect)
while running:
# [...]
pos = pygame.mouse.get_pos()
if isCollision(ballx, bally, pos[0], 525):
balldy *= -1
Related
I want to create a program where the player has to jump on horizontally moving platforms to reach their high score. The player can move left and right and can jump.
import pygame
from pygame.locals import *
pygame.init()
run = True
width = 500
height = 500
x = 250
y = 475
vel = 10
x_plat = 100
y_plat = 400
platform_vel = 5
clock = pygame.time.Clock()
isjump = False
jumpcount = 7.5
collision_tolerance = 10
gravity = 8
surface = pygame.display.set_mode((width, height))
rect = Rect(x_plat, y_plat, 150, 20)
player = Rect(x, y, 25, 25)
while run:
clock.tick(30)
if rect.left >= 355 or rect.left < 1:
platform_vel *= -1
rect.left += platform_vel
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and x > 0:
x -= vel
if keys[pygame.K_RIGHT] and x < (500 - 25):
x += vel
if not(isjump):
if keys[pygame.K_SPACE]:
isjump = True
else:
if jumpcount >= -7.5:
y -= (jumpcount * abs(jumpcount)) * 1
jumpcount -= 1
else:
jumpcount = 7.5
isjump = False
collide = pygame.Rect.colliderect(rect, player)
if collide:
player.bottom = rect.top
player.left += platform_vel
rect.left += platform_vel
pygame.draw.rect(surface, (0, 0, 0), rect)
pygame.draw.rect(surface, (255, 255, 255), player)
pygame.display.update()
surface.fill((255, 222, 173))
for event in pygame.event.get():
if event.type == QUIT:
run = False
If you run my code, when the player jumps onto the moving platform, the platform moves but the player doesn't move with it, making the player float in mid air.
I would really appreciate it if someone could help!
The player didn't move at all with the code provided so I updated x and y references to player.x and player.y, then made some changes to implement gravity and collision checking with the platform and floor.
It's important to note that when on the platform, gravity still moves the player downward into the platform so they collide with the platform each clock tick and the "collide" logic runs. It also allows the player to fall off the platform if moving left or right.
This seems to do what you want:
import pygame
from pygame.locals import *
pygame.init()
run = True
width = 500
height = 500
vel = 10
x_plat = 100
y_plat = 400
platform_vel = 5
clock = pygame.time.Clock()
isjump = False
jumpcount = 7.5
collision_tolerance = 10
gravity = 1
player_vy = 0 # players vertical velocity
surface = pygame.display.set_mode((width, height))
rect = Rect(x_plat, y_plat, 150, 20)
player = Rect(250, 475, 25, 25)
while run:
clock.tick(30)
if rect.left >= 355 or rect.left < 1:
platform_vel *= -1
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and player.x > 0:
player.x -= vel
if keys[pygame.K_RIGHT] and player.x < (500 - 25):
player.x += vel
if not(isjump):
if keys[pygame.K_SPACE]:
isjump = True # suppress spacebar jumping until landing
player_vy = -16 # give upward velocity
player_vy += gravity # accelerate downward due to gravity
player.y += player_vy # compute new height
collide = pygame.Rect.colliderect(rect, player)
if collide and player_vy >= 0: # falling and collide with platform?
player.bottom = rect.top # land on platform.
player_vy = 0 # no longer falling
player.left += platform_vel # move with platform
isjump = False # enable jump
if player.y > 475: # don't fall off bottom of screen.
player.y = 475 # land on bottom of screen
player_vy = 0 # stop falling
isjump = False # allow jumping again
rect.left += platform_vel # move platform
pygame.draw.rect(surface, (0, 0, 0), rect)
pygame.draw.rect(surface, (255, 255, 255), player)
pygame.display.update()
surface.fill((255, 222, 173))
for event in pygame.event.get():
if event.type == QUIT:
run = False
I am currently developing a platform game which depends on objects (in the form of platforms) to be their collision in order to prevent the player from exiting the window.
Here's my code for collision with platforms/blocks:
#check for collision
self.in_air = True
for tile in world.tile_list:
#collison x
if tile[1].colliderect(self.rect.x + dx,self.rect.y , self.width, self.height):
dx = 0
# collision y
if tile[1].colliderect(self.rect.x, self.rect.y + dy, self.width, self.height):
# below ground?
if self.vel_y < 0:
dy = tile[1].bottom - self.rect.top
self.vel_y = 0
# above ground?
elif self.vel_y >= 0:
dy = tile[1].top - self.rect.bottom
self.vel_y = 0
self.in_air = False
However, this looks unprofessional and I would like to add code which introduces an invisible barrier which stops the player from leaving the screen.
I have tried different methods but am currently unsure, any advice would be appreciated.
If you only want to limit the player to a bounding rectangle, I recommend using pygame.Rect.clamp_ip
moves the rectangle inside another, in place
Define the bounding respectively border rectangle. If the player is limited to the screen, you can get a rectangle that defines the border from the display Surface with pygame.display.get_surface() and pygame.Surface.get_rect:
border_rect = pygame.display.get_surface().get_rect()
Clamp the player pygame.Rect by border_rect:
self.rect.clamp_ip(border_rect)
You can do this with 1 line of code:
self.rect.clamp_ip(pygame.display.get_surface().get_rect())
Minimal example
import pygame
pygame.init()
window = pygame.display.set_mode((200, 200))
clock = pygame.time.Clock()
rect = pygame.Rect(0, 0, 40, 40)
rect.center = window.get_rect().center
speed = 10
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
rect.x += (keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * speed
rect.y += (keys[pygame.K_DOWN] - keys[pygame.K_UP]) * speed
rect.clamp_ip(pygame.display.get_surface().get_rect())
window.fill(0)
pygame.draw.rect(window, (255, 0, 0), rect)
pygame.display.flip()
clock.tick(60)
pygame.quit()
exit()
Before you criticize me for not Googling or doing research before asking, I did research beforehand but to no avail.
I am trying to create the Atari Breakout game. I am currently stuck with making the ball bounce off walls. I did research on this and I found a lot of blogs and YouTube videos (and also Stack Overflow questions: this and this) talking about PyGame's vector2 class. I also read the PyGame documentation on vector2 but I can't figure out how to make it work.
I am currently writing a script to make the ball bounce off walls. In the beginning, the player is requested to press the spacebar and the ball will automatically move towards the north-east direction. It should bounce off the top wall when it hits it, but instead, it went inside. This is my approach:
import pygame
pygame.init()
screenWidth = 1200
screenHeight = 700
window = pygame.display.set_mode((screenWidth,screenHeight))
pygame.display.set_caption('Atari Breakout')
class Circle():
def __init__(self, x, y, radius):
self.x = x
self.y = y
self.radius = radius
self.vel_x = 1
self.vel_y = 1
def check_hit():
global hit
if (((screenWidth-box.x)<=box.radius) or ((box.x)<=box.radius) or ((box.y)<=box.radius) or ((screenHeight-box.y)<=box.radius)):
# meaning hit either four walls
if (((screenWidth-box.x)<=box.radius) or ((box.x)<=box.radius)):
# hit right, left
print('hit right, left')
hit = True
elif (((box.y)<=box.radius) or ((screenHeight-box.y)<=box.radius)):
# hit top, bottom
print('hit top, bottom')
hit = True
# main loop
run = True
box = Circle(600,300,10)
hit = False
# (screenWidth-box.x)<=box.radius hit right wall
while run: # (box.x)<=box.radius hit left wall
# (box.y)<=box.radius hit top wall
pygame.time.Clock().tick(60) # (screenHeight-box.y)<=box.radius hit bottom wall
for event in pygame.event.get():
if event == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_SPACE] and (box.y)>box.radius:
while True:
box.y -= box.vel_y
box.x += box.vel_x
window.fill((0,0,0))
pygame.draw.circle(window, (44,176,55), (box.x, box.y), box.radius)
pygame.display.update()
check_hit()
if hit == False:
continue
elif hit == True:
break
if (box.y)<=box.radius or (screenHeight-box.y)<=box.radius:
# hit top, bottom
box.vel_x *= 1
box.vel_y *= -1
print('changed')
if (box.y)<=box.radius:
# hit top
print('hi')
while True:
box.x += box.vel_x # <-- myguess is this is the problem
box.y += box.vel_y
window.fill((0,0,0))
pygame.draw.circle(window, (44,176,55), (box.x, box.y), box.radius)
pygame.display.update()
elif (screenWidth-box.x)<=box.radius or (box.x)<=box.radius:
# hit right, left
box.vel_x *= -1
box.vel_y *= 1
window.fill((0,0,0))
pygame.draw.circle(window, (44,176,55), (box.x, box.y), box.radius)
pygame.display.update()
print('Where are you going')
pygame.quit()
I guess the problem is where I marked. Which is here:
if (box.y)<=box.radius or (screenHeight-box.y)<=box.radius:
# hit top, bottom
box.vel_x *= 1
box.vel_y *= -1
print('changed')
if (box.y)<=box.radius:
# hit top
print('hi')
while True:
box.x += box.vel_x # <-- myguess is this is the problem
box.y += box.vel_y
window.fill((0,0,0))
pygame.draw.circle(window, (44,176,55), (box.x, box.y), box.radius)
pygame.display.update()
but I don't know why. My theory is: the ball travels upwards, it hit the top wall, check_hit() kicks in and make hit = True, then the vel_x and vel_y is changed accordingly (if hit top wall, vel_x should remain the same while vel_y should be multiplied by -1). Then it will move down, hence "bounce" off the top wall.
Note: for now I only have the top wall working. The other three will be done when I can figure out how to bounce off the top wall first.
Can you help me see what's the problem? And if this kind of operation requires the use of the vector2 class, can you explain it to me or give me a place to learn it?
The issue are the multiple nested loops. You have an application loop, so use it.
Continuously move the ball in the loop:
box.y -= box.vel_y
box.x += box.vel_x
Define a rectangular region for the ball by a pygame.Rect object:
bounds = window.get_rect() # full screen
or
bounds = pygame.Rect(450, 200, 300, 200) # rectangular region
Change the direction of movement when the ball hits the bounds:
if box.x - box.radius < bounds.left or box.x + box.radius > bounds.right:
box.vel_x *= -1
if box.y - box.radius < bounds.top or box.y + box.radius > bounds.bottom:
box.vel_y *= -1
See the example:
box = Circle(600,300,10)
run = True
start = False
clock = pygame.time.Clock()
while run:
clock.tick(120)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_SPACE]:
start = True
bounds = pygame.Rect(450, 200, 300, 200)
if start:
box.y -= box.vel_y
box.x += box.vel_x
if box.x - box.radius < bounds.left or box.x + box.radius > bounds.right:
box.vel_x *= -1
if box.y - box.radius < bounds.top or box.y + box.radius > bounds.bottom:
box.vel_y *= -1
window.fill((0,0,0))
pygame.draw.rect(window, (255, 0, 0), bounds, 1)
pygame.draw.circle(window, (44,176,55), (box.x, box.y), box.radius)
pygame.display.update()
My teacher gave us this code to analyze and I am not sure why he put 27 there for collision.. I asked him he said if the distance between the player and the enemy is less than 27 then I will call it a collision, but I still don't understand, can someone please kindly explain it to me in simpler terms. I don't understand where the number 27 comes from.. when my dimensions are so big?
import math
import random
import pygame
# Intialize the pygame
pygame.init()
# create the screen
screen = pygame.display.set_mode((1000, 700))
# Background
background = pygame.image.load('undersea.png')
# Player
playerImg = pygame.image.load('space-invaders.png')
playerX = 500
playerY = 600
playerX_change = 0
# Enemy
enemyImg = []
enemyX = []
enemyY = []
enemyX_change = []
enemyY_change = []
num_of_enemies = 5
for i in range(num_of_enemies):
enemyImg.append(pygame.image.load('plastic.png'))
enemyX.append(random.randint(0, 636))
enemyY.append(random.randint(50, 150))
enemyX_change.append(4)
enemyY_change.append(40)
# Bullet
# Ready - You can't see the bullet on the screen
# Fire - The bullet is currently moving
bulletImg = pygame.image.load('bullet (1).png')
bulletX = 0
bulletY = 480
bulletX_change = 0
bulletY_change = 10
bullet_state = "ready"
# Score
score_value = 0
font = pygame.font.Font('freesansbold.ttf', 32)
textX = 10
testY = 10
# Game Over
over_font = pygame.font.Font('freesansbold.ttf', 64)
def show_score(x, y):
score = font.render("Score : " + str(score_value), True, (255, 255, 255))
screen.blit(score, (x, y))
def game_over_text():
over_text = over_font.render("GAME OVER", True, (255, 255, 255))
screen.blit(over_text, (200, 250))
def player(x, y):
screen.blit(playerImg, (x, y))
def enemy(x, y, i):
screen.blit(enemyImg[i], (x, y))
def fire_bullet(x, y):
global bullet_state
bullet_state = "fire"
screen.blit(bulletImg, (x + 16, y + 10))
def isCollision(enemyX, enemyY, bulletX, bulletY):
distance = ((enemyX - bulletX) ** 2 + (enemyY - bulletY) ** 2)
distance = math.sqrt(distance)
print(distance)
if distance < 27:
return True
else:
return False
# Game Loop
running = True
while running:
# RGB = Red, Green, Blue
screen.fill((0, 0, 0))
# 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 its right or left
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
playerX_change = -5
if event.key == pygame.K_RIGHT:
playerX_change = 5
if event.key == pygame.K_SPACE:
if bullet_state is "ready":
# Get the current x cordinate of the spaceship
bulletX = playerX
fire_bullet(bulletX, bulletY)
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
playerX_change = 0
# 5 = 5 + -0.1 -> 5 = 5 - 0.1
# 5 = 5 + 0.1
playerX += playerX_change
if playerX <= 0:
playerX = 0
elif playerX >= 636:
playerX = 636
# Enemy Movement
for i in range(num_of_enemies):
# Game Over
if enemyY[i] > 440:
for j in range(num_of_enemies):
enemyY[j] = 2000
game_over_text()
break
enemyX[i] += enemyX_change[i]
if enemyX[i] <= 0:
enemyX_change[i] = 4
enemyY[i] += enemyY_change[i]
elif enemyX[i] >= 736:
enemyX_change[i] = -4
enemyY[i] += enemyY_change[i]
# Collision
collision = isCollision(enemyX[i], enemyY[i], bulletX, bulletY)
if collision:
bulletY = 480
bullet_state = "ready"
score_value += 1
enemyX[i] = random.randint(0, 736)
enemyY[i] = random.randint(50, 150)
enemy(enemyX[i], enemyY[i], i)
# Bullet Movement
if bulletY <= 0:
bulletY = 480
bullet_state = "ready"
if bullet_state is "fire":
fire_bullet(bulletX, bulletY)
bulletY -= bulletY_change
player(playerX, playerY)
show_score(textX, testY)
pygame.display.update()
The value is 27, because you compute the Euclidean distance between (enemyX, enemyY) and (bulletX, bulletY) in the function isCollision:
def isCollision(enemyX, enemyY, bulletX, bulletY):
distance = ((enemyX - bulletX) ** 2 + (enemyY - bulletY) ** 2)
distance = math.sqrt(distance)
print(distance)
The Euclidean distance between 2 points (Ax, Ay) and (Bx, By) is
d = sqrt((Bx-Ax)**2 + (By-Ay)**2) = hypot(Bx-Ax, By-Ay)
In 2 dimensional space this is the same as the Pythagorean theorem. The length of the diagonal in a square with a side length of 19 is approximately 27.
Try changing the '27' to a small no. like '5' and you will see that the collision is happening too late on screen. OR if you replace the enemy/bullet image you are loading with some other small/large image you will notice that you will have to change you '27' to some other number to make things work as intended.
Simple Words
The reason for the hard-coding '27' could be because the width of enemy image and the width of bullet image you load in your code seem to touch (collide) eachother at the distance '27' from the point where they are drawn.
Explanation
When we load an image pygame draws it from top-left corner. For Example your background image is so large in code but when you ask your code where is this 'background' located it well tell you it is on (0,0) although it's on your full screen not a single point.
Infered from screen.blit(background, (0, 0)) line in your code
Same for the images/sprites of enemy and bullets. The position you use in collision enemyX, enemyY, bulletX, bulletY are the top-left coordinates. Now to show them actually touching/colliding you may have to tell the code that 'My enemy is more than one pixel on the position. So I want to take care of it by using the constant '27' (depends on width of enemy/bullet). As soon as the both positions have a Euclidean distance of '27' this means they are colliding on my screen hence proceed as true.
If you are still confused about the positioning issue and how center of image or top-left of image matters read these, it will help.
https://stackoverflow.com/a/51182238/11672352
Why is the pygame rect not in the right place?
It seems to me like the game triggers the collision once the bullet enters a circle of radius 27 pixels around the enemy. Although your enemy may not be a perfect circle, it is a lot simpler to treat them as if they were when calculating collisions as it is relatively easy to check whether a point has collided with a circle, in this case, the bullet with the enemy.
the lines:
distance = ((enemyX - bulletX) ** 2 + (enemyY - bulletY) ** 2)
distance = math.sqrt(distance)
are used to calculate the distance between the middle of the enemy and the bullet by using Pythagoras' theorem (a^2 + b^2 = c^2 where a and b are two different sides of a right angled triangle and c is your hypotenuse, aka the longest side)
The value 27 is likely used because the enemy is likely around 52 pixels (27 * 2) wide and tall
I am creating a physics-based game with Pygame in which the player controls a ball. As you control the ball, it accelerates in the specified direction (holding the left arrow adds x pixels per frame to its movement speed). Since the ball is... well... a ball, and Pygame doesn't support ball collision detection, I created a new class with its own collision method. The method has two parts: if the ball runs into the corner of a rectangle, or if it runs into the side of the rectangle. The problem concerns the circle-to-side collision.
The ball is based on a rect object, and therefore has those pesky corners. I cannot use the simple colliderect method, otherwise the situation above would detect a collision where there should be none, and it would overlap the first part of my collision detection method. Instead, I opted to use collidepoint between the rectangle and the midpoints on each side of the ball's rectangle.
Finally, the heart of the issue. I mentioned earlier that the ball accelerates. When the ball accelerates to the point that (even though it appears to be standing still) it moves far enough into the rectangle for another midpoint on the circle to detect a "collision." This problem likely stems from the fact that (for a collision on the left side of the ball) my code sets the ball's left equal to the rectangle's right, so that when the ball accelerates enough to be inside the rectangle, it gets moved to another face of the rectangle.
Thank you so much for bearing with me, any and all suggestions are welcome. I would either be looking for a fix to my specific problem, or a cleaner way to handle the collision detection. My full code is below:
import pygame, sys, math
global Color
Color = {}
Color['white'] = (255,255,255)
Color['black'] = ( 0, 0, 0)
Color['red'] = (255, 0, 0)
Color['green'] = ( 0,255, 0)
Color['blue'] = ( 0, 0,255)
global WINDOWWIDTH, WINDOWHEIGHT
WINDOWWIDTH, WINDOWHEIGHT = 500, 500
class Ball():
def __init__(self, x, y, r):
self.rect = pygame.Rect(x, y, r, r)
self.radius = r/2
self.speed = [0, 0]
self.b_fact = 1
self.move = {'left':False, 'right':False, 'up':False, 'down':False}
self.new_dir = {'left':False, 'right':False, 'up':False, 'down':False}
def move_self(self):
if self.move['left']:
self.speed[0] -= 2
if self.move['up']:
self.speed[1] -= 2
if self.move['right']:
self.speed[0] += 2
if self.move['down']:
self.speed[1] += 2
if self.speed[0] < 0:
self.speed[0] += 1
if self.speed[1] < 0:
self.speed[1] += 1
if self.speed[0] > 0:
self.speed[0] -= 1
if self.speed[1] > 0:
self.speed[1] -= 1
self.rect.left += self.speed[0]
self.rect.top += self.speed[1]
def bounce(self, rectList):
for rect in rectList:
self.collide_rect(rect)
if self.rect.left <= 0:
self.rect.left = 0
self.new_dir['right'] = True
if self.rect.right >= WINDOWWIDTH:
self.rect.right = WINDOWWIDTH
self.new_dir['left'] = True
if self.rect.top <= 0:
self.rect.top = 0
self.new_dir['down'] = True
if self.rect.bottom >= WINDOWHEIGHT:
self.rect.bottom = WINDOWHEIGHT
self.new_dir['up'] = True
for key in self.new_dir:
if self.new_dir[key] and key=='left':
self.speed[0] *= (-1)*self.b_fact
if self.new_dir[key] and key=='right':
self.speed[0] *= (-1)*self.b_fact
if self.new_dir[key] and key=='up':
self.speed[1] *= (-1)*self.b_fact
if self.new_dir[key] and key=='down':
self.speed[1] *= (-1)*self.b_fact
self.new_dir[key] = False
def collide_rect(self, rect):
x1, y1, r = self.rect.centerx, self.rect.centery, self.radius
foundSide = 0
foundCorner = 0
side_list = ['left', 'right', 'bottom', 'top']
corner_list = ['topleft', 'topright', 'bottomleft', 'bottomright']
collision_list = []
for side in side_list:
if rect.collidepoint(eval('self.rect.mid'+side)):
collision_list.append(side)
for corner in corner_list:
x2, y2 = eval('rect.'+corner)[0], eval('rect.'+corner)[1]
dist = math.sqrt((x2 - x1)**2 + (y2 - y1)**2)
if dist < r:
if corner.find('left') > -1:
corner = corner.replace('left','right')
else:
corner = corner.replace('right','left')
if corner.find('top') > -1:
corner = corner.replace('top','bottom')
else:
corner = corner.replace('bottom','top')
collision_list.append(corner)
for direction in collision_list:
if direction.find('left') > -1:
self.rect.left = rect.right
self.new_dir['left'] = True
if direction.find('top') > -1:
self.rect.top = rect.bottom
self.new_dir['top'] = True
if direction.find('right') > -1:
self.rect.right = rect.left
self.new_dir['right'] = True
if direction.find('bottom') > -1:
self.rect.bottom = rect.top
self.new_dir['bottom'] = True
class BallGame():
def __init__(self):
pygame.display.set_caption("Ball is life")
pygame.init()
self.ball = Ball(0, 0, 30)
self.allRects = []
rect = pygame.Rect(60,60,50,50)
self.allRects.append(rect)
self.mainClock = pygame.time.Clock()
self.screen = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
self.basicFont = pygame.font.SysFont(None, 50)
def drawScreen(self):
self.screen.fill(Color['green'])
pygame.draw.ellipse(self.screen, Color['white'], self.ball.rect)
for rect in self.allRects:
pygame.draw.rect(self.screen, Color['black'], rect)
def mainloop(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
for i in range(2):
k = (pygame.KEYUP, pygame.KEYDOWN)
if event.type == k[i]:
if event.key == pygame.K_LEFT:
self.ball.move['left'] = i
elif event.key == pygame.K_UP:
self.ball.move['up'] = i
elif event.key == pygame.K_RIGHT:
self.ball.move['right'] = i
elif event.key == pygame.K_DOWN:
self.ball.move['down'] = i
self.ball.move_self()
self.ball.bounce(self.allRects)
self.drawScreen()
pygame.display.update()
self.mainClock.tick(20)
Game = BallGame()
while True:
Game.mainloop()
Another way to think about the collision is to consider an enlarged version of the black rectangle. This would be a rounded rectangle with corner radius r. The collision between the ball and black rectangle is equivalent to the collision between the center of the ball and the rounded rectangle. This can help make the analysis of the situation easier.
When it bounces a more accurate way of determining the new position is to consider the line from the previous position to the current position. You can calculate where this line crosses the boundary and where a prefect reflection should be.