Skip to content Skip to sidebar Skip to footer

How To Make Ball Bounce Off Triangle In Pygame?

Hello I'm a pretty new programmer and I'm trying to make a ball bounce off a 45 degree triangle. Here is my code: This program makes the ball bounce when it hits the sides of the w

Solution 1:

Interesting task. A triangle can be defined by a simple list:

triangle = [(250, 220), (400, 300), (100, 300)]

The triangle can be drawn by pygame.draw.polygon()

pygame.draw.polygon(WINDOW, RED, triangle, 0)

Use pygame.math.Vector2 to define the position and the motion vector of the ball:

ballvec = pygame.math.Vector2(1, 1)
ballpos = pygame.math.Vector2(150, 250)
balldiameter = 64

Create a function, which does the collision detection. The function has to detect if the ball hits a line. If the line is hit, then the motion vector of the ball is reflected on the line. The line is represented by 2 points (lp0, lp1), which are pygame.math.Vector2 objects. The position of the ball (pt) and the motion vector (dir) are pygame.math.Vector2 objects, too:

defisect(lp0, lp1, pt, dir, radius):
    # direction vector of the line
    l_dir = (lp1 - lp0).normalize()
    # normal vector to the line
    nv = pygame.math.Vector2(-l_dir[1], l_dir[0])
    # distance to line
    d = (lp0-pt).dot(nv)
    # intersection point on endless line
    ptX = pt + nv * d
    # test if the ball hits the lineifabs(d) > radius ordir.dot(ptX-pt) <= 0:
        returndirif (ptX-lp0).dot(l_dir) < 0or (ptX-lp1).dot(l_dir) > 0:
        returndir# reflect the direction vector on the line (like a billiard ball)
    r_dir = dir.reflect(nv)
    return r_dir

Append the window rectangle and the triangle to a list of lines. Ech line is represented by a tuple of 2 pygame.math.Vector2 objects:

# add screen rect
screen_rect = [(0, 0), (0, 300), (500, 300), (500, 0)]
for i inrange(len(screen_rect)):
    p0, p1 = screen_rect[i], screen_rect[(i+1) % len(screen_rect)]
    line_list.append((pygame.math.Vector2(p0[0], p0[1]), pygame.math.Vector2(p1[0], p1[1])))

# add red trianlge
triangle = [(250, 220), (400, 300), (100, 300)]
for i inrange(len(triangle)):
    p0, p1 = triangle[i], triangle[(i+1) % len(triangle)]
    line_list.append((pygame.math.Vector2(p0[0], p0[1]), pygame.math.Vector2(p1[0], p1[1])))

Do the collision detection in a loop, which traverse the lines. If the ball hits a line, then the motion vector is replaced by the reflected motion vector:

for line in line_list:
    ballvec = isect(*line, ballpos, ballvec, balldiameter/2)

Finally update the position of the ball an the ball rectangle:

ballpos = ballpos + ballvec
ballRect.x, ballRect.y = ballpos[0]-ballRect.width/2, ballpos[1]-ballRect.height/2

See the example code, where I applied the suggested changes to your original code. My ball image has a size of 64x64. The ball diameter has to be set to this size (balldiameter = 64):


Minimal example

import pygame

pygame.init()
window = pygame.display.set_mode((500, 300))

try:
    ball = pygame.image.load("Ball64.png")
except:
    ball = pygame.Surface((64, 64), pygame.SRCALPHA)
    pygame.draw.circle(ball, (255, 255, 0), (32, 32), 32)
ballvec = pygame.math.Vector2(1.5, 1.5)
ballpos = pygame.math.Vector2(150, 250)
balldiameter = ball.get_width()

defreflect_circle_on_line(lp0, lp1, pt, dir, radius):
    l_dir = (lp1 - lp0).normalize()                 # direction vector of the line
    nv = pygame.math.Vector2(-l_dir[1], l_dir[0])   # normal vector to the line
    d = (lp0-pt).dot(nv)                            # distance to line
    ptX = pt + nv * d                               # intersection point on endless lineif (abs(d) > radius ordir.dot(ptX-pt) <= 0or# test if the ball hits the line   
        (ptX-lp0).dot(l_dir) < 0or (ptX-lp1).dot(l_dir) > 0):
        returndir 
    r_dir = dir.reflect(nv)                         # reflect the direction vector on the line (like a billiard ball)                       return r_dir
      
triangle1 = [(250, 220), (400, 300), (100, 300)]
triangle2 = [(250, 80), (400, 0), (100, 0)]
screen_rect = [(0, 0), (0, window.get_height()), window.get_size(), (window.get_width(), 0)]

line_list = []
for p0, p1 inzip(triangle1, triangle1[1:] + triangle1[:1]):
    line_list.append((pygame.math.Vector2(p0), pygame.math.Vector2(p1)))
for p0, p1 inzip(triangle2, triangle2[1:] + triangle2[:1]):
    line_list.append((pygame.math.Vector2(p0), pygame.math.Vector2(p1)))
for p0, p1 inzip(screen_rect, screen_rect[1:] + screen_rect[:1]):
    line_list.append((pygame.math.Vector2(p0), pygame.math.Vector2(p1)))

clock = pygame.time.Clock()
run = Truewhile run:
    clock.tick(250)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = Falsefor line in line_list:
        ballvec = reflect_circle_on_line(*line, ballpos, ballvec, balldiameter/2)
    ballpos = ballpos + ballvec
    
    window.fill((64, 64, 64))
    pygame.draw.polygon(window, (255, 0, 0), triangle1, 0)
    pygame.draw.polygon(window, (0, 0, 255), triangle2, 0)
    window.blit(ball, (round(ballpos[0]-balldiameter/2), round(ballpos[1]-balldiameter/2)))
    pygame.display.flip()

pygame.quit()

Post a Comment for "How To Make Ball Bounce Off Triangle In Pygame?"