0

I'm having issues with making the ball bounce off the walls and the rectangle the is put in the middle I tried using if statements but couldn't get it to work any help would be appreciated or if there is any other mistake that could be fixed to optimize the code further that would be greatly appreciated as well.

import pygame, random

pygame.init()
screen = pygame.display.set_mode([700,500])

# Set up global variables
class Ball():
    x = y = xspeed = yspeed = colour = rad = 0
class rec():
    x = y = w = h = colour = 0
w = screen.get_width()
h = screen.get_height()
rec.x = w/2
rec.y = h/2
rec.w = w/7
rec.h = h/5  

# Create a list of balls
balls = []

# Create ball and include in the list of balls
for i in range(1):
    ball = Ball()
    ball.x = random.randint(0,w-ball.rad-ball.rad)
    ball.y = random.randint(0,h-ball.rad-ball.rad)
    ball.xspeed = random.randint(-2,2)
    ball.yspeed = random.randint(-2,2)
    while ball.xspeed == 0 or ball.yspeed == 0:
        ball.xspeed = random.randint(-2,2)
        ball.yspeed = random.randint(-2,2)
    ball.rad = random.randint(5,30)
    ball.colour = (random.randint(0,255),random.randint(0,255),random.randint(0,255))
    if ball.x + ball.rad <= w and ball.y + ball.rad <= h:
        balls.append(ball)
#Game loop
while True:
    # ===================== HANDLE EVENTS (DO NOT EDIT) ===================== #
    done = False
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            done = True
            break

    if done == True:
        break
    # ============================== MOVE STUFF ============================= #
    for ball in balls:
        ball.x = ball.x + ball.xspeed
        ball.y = ball.y + ball.yspeed
    # ============================== COLLISION ============================== #
    for ball in balls:
        if ball.x - ball.rad <= 0 :
            ball.xspeed = ball.xspeed * -1
        elif ball.x + ball.rad >= w:
            ball.xspeed = ball.xspeed * -1
        elif ball.y - ball.rad<= 0 :
            ball.yspeed = ball.yspeed * -1
        elif ball.y + ball.rad >= h:
            ball.yspeed = ball.yspeed * -1
        elif ball.x + ball.rad <= rec.x or ball.x - ball.rad >= rec.x + rec.w:
            ball.xspeed = ball.xspeed * -1
        elif ball.y + ball.rad <= rec.y or ball.y - ball.rad <= rec.y + rec.h:
            ball.yspeed = ball.yspeed * -1
        elif ball.x + ball.rad == rec.x + rec.w:
            ball.xspeed = ball.xspeed * -1
        elif ball.x + ball.rad == rec.x:
            ball.xspeed = ball.xspeed * -1
        elif ball.y +ball.rad == rec.y + rec.h:
            ball.yspeed = ball.yspeed * -1
        elif ball.y + ball.rad == rec.y:
            ball.yspeed = ball.yspeed * -1          
    # ============================== DRAW STUFF ============================= #                               
    screen.fill ((255,255,255))
    for ball in balls:
        pygame.draw.circle (screen, (ball.colour), (ball.x,ball.y), ball.rad)
    pygame.draw.rect(screen, (0,255,0), (rec.x, rec.y, rec.w, rec.h))
    # ====================== PYGAME STUFF (DO NOT EDIT) ===================== #
    pygame.display.flip()
    pygame.time.delay(20)

1 Answers1

0

Compute the distance of the center of the ball to the center of the obstacle

dx = ball.x - rect.centerx
dy = ball.y - rect.centery

Move the ball in the way, that it is touching the player but not intersecting the player once a collision is detected. Reflect the movement of the ball only if its movement vector points in a direction "against" the ball. The ball can be reflected on the obstacle with pygame.math.Vector2.reflect_ip:

v = pygame.math.Vector2(ball.xspeed, ball.yspeed)
if abs(dx) > abs(dy):
    ball.x = rect.left-ball.rad if dx < 0 else rect.right+ball.rad
    if (dx < 0 and v[0] > 0) or (dx > 0 and v[0] < 0):
        v.reflect_ip(pygame.math.Vector2(1, 0))
else:
    ballposy = rect.top-ball.rad if dy < 0 else rect.bottom+ball.rad
    if (dy < 0 and v[1] > 0) or (dy > 0 and v[1] < 0):
        v.reflect_ip(pygame.math.Vector2(0, 1))
ball.xspeed, ball.yspeed = v.x, v.y

See also How do I avoid an glitchy collision between circle and rectangle in PyGame?


Minimal example:

import pygame, random

pygame.init()
screen = pygame.display.set_mode([700,500])
clock = pygame.time.Clock()

# Set up global variables
class Ball():
    x = y = xspeed = yspeed = colour = rad = 0
class rec():
    x = y = w = h = colour = 0
w = screen.get_width()
h = screen.get_height()

rect_list = [
    pygame.Rect(w//4, h//4, w//7, h //5), 
    pygame.Rect(w*3//4, h*3//4, w//7, h //5)] 

# Create a list of balls
balls = []

# Create ball and include in the list of balls
for i in range(1):
    ball = Ball()
    ball.x = random.randint(0,w-ball.rad-ball.rad)
    ball.y = random.randint(0,h-ball.rad-ball.rad)
    ball.xspeed = random.randint(-2,2)
    ball.yspeed = random.randint(-2,2)
    while ball.xspeed == 0 or ball.yspeed == 0:
        ball.xspeed = random.randint(-2,2)
        ball.yspeed = random.randint(-2,2)
    ball.rad = random.randint(5,30)
    ball.colour = (random.randint(0,255),random.randint(0,255),random.randint(0,255))
    if ball.x + ball.rad <= w and ball.y + ball.rad <= h:
        balls.append(ball)
#Game loop
while True:
    # ===================== HANDLE EVENTS (DO NOT EDIT) ===================== #
    done = False
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            done = True
            break

    if done == True:
        break
    # ============================== MOVE STUFF ============================= #
    for ball in balls:
        ball.x = ball.x + ball.xspeed
        ball.y = ball.y + ball.yspeed
    # ============================== COLLISION ============================== #
    for ball in balls:
        if ball.x - ball.rad <= 0:
            ball.xspeed = abs(ball.xspeed)
        elif ball.x + ball.rad >= w:
            ball.xspeed = -abs(ball.xspeed)
        if ball.y - ball.rad<= 0 :
            ball.yspeed = abs(ball.yspeed)
        elif ball.y + ball.rad >= h:
            ball.yspeed = -abs(ball.yspeed)

        for rect in rect_list:
            ball_rect = pygame.Rect((0,0), (ball.rad*2, ball.rad*2))
            ball_rect.center = int(ball.x),int(ball.y)
            if rect.colliderect(ball_rect):
                v = pygame.math.Vector2(ball.xspeed, ball.yspeed)
                dx = ball.x - rect.centerx
                dy = ball.y - rect.centery
                if abs(dx) > abs(dy):
                    ball.x = rect.left-ball.rad if dx < 0 else rect.right+ball.rad
                    if (dx < 0 and v[0] > 0) or (dx > 0 and v[0] < 0):
                        v.reflect_ip(pygame.math.Vector2(1, 0))
                else:
                    ballposy = rect.top-ball.rad if dy < 0 else rect.bottom+ball.rad
                    if (dy < 0 and v[1] > 0) or (dy > 0 and v[1] < 0):
                        v.reflect_ip(pygame.math.Vector2(0, 1))
                ball.xspeed, ball.yspeed = v.x, v.y

    # ============================== DRAW STUFF ============================= #                               
    screen.fill ((255,255,255))
    for ball in balls:
        pygame.draw.circle (screen, (ball.colour), (ball.x,ball.y), ball.rad)
    for rect in rect_list:
        pygame.draw.rect(screen, (0,255,0), rect)
    # ====================== PYGAME STUFF (DO NOT EDIT) ===================== #
    pygame.display.flip()
    clock.tick(100)
Rabbid76
  • 202,892
  • 27
  • 131
  • 174