Sometimes the Ball Doesn't Bounce Off the Paddle in Pong Game

Sometimes the ball doesn't bounce off the paddle in pong game

The behavior occurs, when the ball doesn't hit the paddle at the front, but at the top or bottom. Actually the collision between the paddle and the ball is detected and the direction is changed. But the ball penetrated so deep into the paddle that the ball cannot leave the collision area with the paddle with it's next step. This causes that a collision is detected again in the next frame and the direction of the ball is changed again. Now the ball moves in the same direction as before the first collision. This process continues until the ball leaves the paddle at the bottom. This causes a zig zag movement along the front side of the paddle.

Sample Image

There are different solution. One option is not to reverse the direction, but to set the direction to the left when the right paddle is hit a nd to set the direction to the right when the left paddle is hit:

if ball.colliderect(paddleLeft):
move_x = abs(move_x)
if ball.colliderect(paddleRight):
move_x = -abs(move_x)

Sample Image

Another option is to adjust the position to the ball. If the right paddle is hit, the right side of the ball must be placed to the left of the paddle. If the left paddle is hit, then the left side of the ball must be placed to the right of the paddle:

if ball.colliderect(paddleLeft):
move_x *= -1
ball.left = paddleLeft.right
if ball.colliderect(paddleRight):
move_x *= -1
ball.right = paddleRight.left

Sample Image

How to stop a ball from phasing through the paddle?

The issue is caused by the fact, that the speed of the ball is grater than 1 and the ball doesn't exactly hit the paddle. That may cause multiple collisions and changes of the direction in consecutive frames.

You have to restrict the position of the ball to the borders of the paddel. Is the ball is left of the paddle, then the right of the ball has to be set to the left of the paddle (ball.rect.right = paddle.rect.left) and if the ball is at the right of the paddle, the the left of the ball has to be set to the right of the paddel (ball.rect.left = paddle.rect.right):

while running:
# [...]

for paddle in paddles:
if pygame.sprite.collide_rect(paddle, ball):
ball.angle = 360 - ball.angle
if ball.rect.x > paddle.rect.x:
ball.rect.left = paddle.rect.right
else:
ball.rect.right = paddle.rect.left
# [...]

Pong on pygame, ball always bouncing despite not colliding with the paddle

I recommend to use pygame.Rect objects and the method .colliderect()

Define rectangles for the ball, paddle and screen or get the rectangles from a pygame.Surface by .get_rect():

ball_rect = ball.get_rect(topleft = (self.bx, self.by))
right_paddle_rect = pygame.Rect(self.px, self.py, 38, 196)
screen_rect = self.sc.get_rect()

Test the collision of the ball and the borders of the screen:

if ball_rect.top < screen_rect.top or ball_rect.bottom > screen_rect.bottom:
self.speedy *= -1
if ball_rect.left < screen_rect.left:
self.speedx *= -1
if ball_rect.right > screen_rect.right:
self.bx = 250
self.by = 340

Be carful when you test the collision between the ball and the paddle. See Sometimes the ball doesn't bounce off the paddle in pong game:

if ball_rect.colliderect(right_paddle_rect):
self.speedx = -abs(self.speedx)

Complete method draw_ball:

def draw_ball(self):
self.sc.blit(ball, (self.bx, self.by))

self.bx += self.speedx
self.by += self.speedy

ball_rect = ball.get_rect(topleft = (self.bx, self.by))
right_paddle_rect = pygame.Rect(self.px, self.py, 38, 196)
screen_rect = self.sc.get_rect()

if ball_rect.top < screen_rect.top or ball_rect.bottom > screen_rect.bottom:
self.speedy *= -1
if ball_rect.left < screen_rect.left:
self.speedx *= -1
if ball_rect.right > screen_rect.right:
self.bx = 250
self.by = 340

if ball_rect.colliderect(right_paddle_rect):
self.speedx = -abs(self.speedx)

Minimal example:

Sample Image

import pygame

class Game:
def __init__(self, screen):
self.sc = screen
self.bx, self.by = 250, 340
self.speedx, self.speedy = 5, 5
self.px, self.py = 700, 200

def draw_ball(self):
self.sc.blit(ball, (self.bx, self.by))

self.bx += self.speedx
self.by += self.speedy

ball_rect = ball.get_rect(topleft = (self.bx, self.by))
right_paddle_rect = pygame.Rect(self.px, self.py, 38, 196)
screen_rect = self.sc.get_rect()

if ball_rect.top < screen_rect.top or ball_rect.bottom > screen_rect.bottom:
self.speedy *= -1
if ball_rect.left < screen_rect.left:
self.speedx *= -1
if ball_rect.right > screen_rect.right:
self.bx = 250
self.by = 340

if ball_rect.colliderect(right_paddle_rect):
self.speedx = -abs(self.speedx)

pygame.init()
screen = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()

ball = pygame.Surface((38, 38), pygame.SRCALPHA)
pygame.draw.circle(ball, (255, 255, 255), (19, 19), 19)
game = Game(screen)

run = True
while run:
clock.tick(60)

# event loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
print(pygame.key.name(event.key))

keys = pygame.key.get_pressed()
pspeed = 5
if keys[pygame.K_UP]:
game.py = max(0, game.py - pspeed)
if keys[pygame.K_DOWN]:
game.py = min(screen.get_height()-196, game.py + pspeed)

screen.fill(0)
game.draw_ball()
pygame.draw.rect(screen, (255, 255, 255), (game.px, game.py, 38, 196))
pygame.display.flip()

Ball will stop when it collides with paddle and bounce off once paddle is moved in pong game

Move the ball before you do the collision test not after. Since a step of the ball is great than 1, the ball doesn't hit the paddle exactly. If the ball hits the left paddle, then you have to put the left side of the ball on the right side of the paddle. If the ball hits the right paddle the you have to put the right of the ball on the left of the paddle.

In a similar way you have to restrict the position of the ball to the window:

(Note ballX, ballY is the top left of the ball)

while True:
# [...]

ballX += ballXvel
ballY += ballYvel

if ball.colliderect(paddle1) :
ballXvel *= -1
ballX = paddle1.right

if ball.colliderect(paddle2) :
ballXvel *= -1
ballX = paddle2.left - ball.width

size = DISPLAY.get_size()
if not 0 <= ballX < size[0] - ball.width:
ballXvel *= -1
ballX = max(0, min(size[0] - ball.width, ballX))
if not 0 <= ballY < size[1] - ball.height:
ballYvel *= -1
ballY = max(0, min(size[1] - ball.height, ballY))

# [...]

ball.y = ballY
ball.x = ballX
# ballX += ballXvel <-- DELETE
# ballY += ballYvel

Rectangle Collision with each other

You must change self.velocity[0] when the ball touches the paddle. Since the movement of the ball is more than 1 pixel per frame, the ball does not exactly touch the paddle. This mans the condition self.rect.left - rect1.rect.right == 0 and self.rect.right - rect2.rect.left == 0 will not be fulfilled. If the movement in x direction is negative, the new movement needs to be positive (abs(self.velocity[0])). If the movement in x direction is positive, the new movement needs to be negative (-abs(self.velocity[0])):

class Ball():
# [...]

def move_ball(self):
self.rect.x += self.velocity[0]
self.rect.y += self.velocity[1]

# Collision with Screen
if self.rect.top <= 10 or self.rect.bottom >= screen_height - 10:
self.velocity[1] *= -1

# Collision with Rectangles
if self.rect.colliderect(rect1) or self.rect.colliderect(rect2):
if self.velocity[0] < 0:
self.velocity[0] = abs(self.velocity[0])
else:
self.velocity[0] = -abs(self.velocity[0])

Sample Image

See also Sometimes the ball doesn't bounce off the paddle in pong game

Pong paddles have some weird bug preventing from letting a player score

I recommend to compute the bounding rectangle of the ball and the paddles and to use pygame.Rect and colliderect() to detect the collision between a ball and a paddle.

See alos Sometimes the ball doesn't bounce off the paddle in pong game.

For instance:

def main():
# [...]

while not done:
# [...]

# Logic
for ball in ball_list:
# [...]

ball_rect = pygame.Rect(ball.x-ball_size, ball.y-ball_size, ball_size*2, ball_size*2)

left_paddle_rect = pygame.Rect(x, y, 25, 75)
if ball.change_x < 0 and ball_rect.colliderect(left_paddle_rect):
ball.change_x = abs(ball.change_x)

right_paddle_rect = pygame.Rect(x1, y1, 25, 75)
if ball.change_x > 0 and ball_rect.colliderect(right_paddle_rect):
ball.change_x = -abs(ball.change_x)

Furthermore, there the height of the window is 500 rather than 50_

if ball.y > 50 - ball_size or ball.y < ball_size:

if ball.y > 500 - ball_size or ball.y < ball_size:

I recommend to remove the multiple calls to pygame.display.flip() respectively pygame.display.update(). Do just one update of the display at the end of the main application loop. See the complete example:

Sample Image

# Importing libraries
import pygame
import random
import time

# Initializing PyGame
pygame.init()

# Creating a font
font = pygame.font.SysFont(None, 30)

# Set the height and width of the screen
window_width = 700
window_height = 500
size = [window_width, window_height]
game_win = pygame.display.set_mode(size)


# Creating a messaging system
def message(sentence, x, y):
sentence = font.render(sentence, True, white)
game_win.blit(sentence, [x, y])


# Creating a color
white = (225, 225, 225)
black = (0, 0, 0)

# Setting up ball
ball_size = 25


class Ball:
"""
Class to keep track of a ball's location and vector.
"""

def __init__(self):
self.x = 0
self.y = 0
self.change_x = 0
self.change_y = 0


def make_ball():
ball = Ball()
# Starting position of the ball.
# Take into account the ball size so we don't spawn on the edge.
ball.x = 350
ball.y = 250

# Speed and direction of rectangle
ball.change_x = 5
ball.change_y = 5

return ball


def main():
# Scores
left_score = 0
right_score = 0

pygame.init()

pygame.display.set_caption("Ping Pong")

# Loop until the user clicks the close button.
done = False

# Used to manage how fast the screen updates
clock = pygame.time.Clock()

ball_list = []

ball = make_ball()
ball_list.append(ball)

# Right paddle coordinates
y = 200
y_change = 0
x = 50
# Left paddle coordinates
y1 = 200
y1_change = 0
x1 = 650

while not done:
# --- Event Processing
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_w:
y_change = -5

elif event.key == pygame.K_s:
y_change = 5

elif event.key == pygame.K_UP:
y1_change = -5

elif event.key == pygame.K_DOWN:
y1_change = 5

elif event.type == pygame.KEYUP:
y_change = 0
y1_change = 0

y += y_change
y1 += y1_change

if y > window_height - 100:
y -= 5
if y < 50:
y += 5
if y1 > window_height - 100:
y1 -= 5
if y1 < 50:
y1 += 5

message("Left player score: " + str(left_score), 10, 10)
message("Right player score: " + str(right_score), 490, 10)


# Logic
for ball in ball_list:
# Move the ball's center
ball.x += ball.change_x
ball.y += ball.change_y

# Bounce the ball if needed
if ball.y > 500 - ball_size or ball.y < ball_size:
ball.change_y *= -1
if ball.x > window_width - ball_size:
ball.change_x *= -1
left_score += 1
if ball.x < ball_size:
ball.change_x *= -1
right_score += 1

# Here is the part where it all becomes weird and buggy
ball_rect = pygame.Rect(ball.x-ball_size, ball.y-ball_size, ball_size*2, ball_size*2)

left_paddle_rect = pygame.Rect(x, y, 25, 75)
if ball.change_x < 0 and ball_rect.colliderect(left_paddle_rect):
ball.change_x = abs(ball.change_x)

right_paddle_rect = pygame.Rect(x1, y1, 25, 75)
if ball.change_x > 0 and ball_rect.colliderect(right_paddle_rect):
ball.change_x = -abs(ball.change_x)

if right_score == 10:
message('RIGHT PLAYER HAS WON!!', 300, 200)
time.sleep(10)
done = True
elif left_score == 10:
message("LEFT PLAYER HAS WON!!", 300, 200)
time.sleep(10)
done = True

# Drawing
# Set the screen background
game_win.fill(black)

# Drawing a left paddle
pygame.draw.rect(game_win, white, [x, y, 25, 100])
# Drawing a right paddle
pygame.draw.rect(game_win, white, [x1, y1, 25, 100])

# Draw the balls
for ball in ball_list:
pygame.draw.circle(game_win, white, [ball.x, ball.y], ball_size)

# Go ahead and update the screen with what we've drawn.
pygame.display.flip()

# Wrap-up
# Limit to 60 frames per second
clock.tick(60)

# Close everything down
pygame.quit()


if __name__ == "__main__":
main()

Simple pong: but the ball goes into the corner and bounces the wrong way and then it keeps cycling how can I fix this?

See Sometimes the ball doesn't bounce off the paddle in pong game. When the ball hits the player, you need to reverse the velocity along the x-axis and limit the position of the ball ball through the paddle:

while True:
# [...]

if ball.colliderect(opponent):
ball.left = opponent.right
ball_speed_x *= -1
if ball.colliderect(player):
ball.right = player.left
ball_speed_x *= -1


Related Topics



Leave a reply



Submit