# Bounding Box/Rectangle collision glitches

I've been working on this golf game for a couple of weeks and have been trying to create a reliable collision system for whenever a golf ball (mini golf) hits a block. I'm using regular rectangular box collision to help me with, but I have constantly been running into glitching issues, eg, the golf ball can clip through blocks. I've tried many methods to solve this but none of them work well.

The way I handle moving the ball is quite simple, is that the player clicks on the ball, drags and releases, which provides an initial velocity and angle, I then get x & y components and decrease or increase the position of the golf ball, I also have variables which can make the components negative. Code for this is beneath.

    def shooting(self):

if self.x_inverse:
self.x_vel *= -1
if self.y_inverse:
self.y_vel *= -1
self.rect.centerx -= self.x_vel*self.dt
self.rect.centery -= self.y_vel*self.dt
self.initial_vel -=  self.resistance * self.dt
if self.initial_vel <= 10:
self.shoot = False


My method for calculating collisions with a block is just regular bounding box/rectangular box collision, code is beneath.

def bounced(self, golf_ball):

if golf_ball.rect.colliderect(self.rect):
if abs(self.rect.left - golf_ball.rect.right) <= 5:

if golf_ball.rect.right > self.rect.left:
golf_ball.rect.right = self.rect.left
golf_ball.rect.right -= 1
if golf_ball.x_inverse:
golf_ball.x_inverse = False
else:
golf_ball.x_inverse = True  # Left

if abs(self.rect.right - golf_ball.rect.left) <= 5:

if golf_ball.rect.left < self.rect.right:
golf_ball.rect.left = self.rect.right
golf_ball.rect.left += 1

if golf_ball.x_inverse:
golf_ball.x_inverse = False
else:
golf_ball.x_inverse = True

if abs(self.rect.bottom - golf_ball.rect.top) <= 5:  # Bottom

if golf_ball.rect.top < self.rect.bottom:
golf_ball.rect.top = self.rect.bottom
golf_ball.rect.top += 1
if golf_ball.y_inverse:
golf_ball.y_inverse = False
else:
golf_ball.y_inverse = True

if abs(self.rect.top - golf_ball.rect.bottom) <= 5:
if golf_ball.rect.bottom > self.rect.top:
golf_ball.rect.bottom = self.rect.top
golf_ball.rect.bottom -= 1

if golf_ball.y_inverse:
golf_ball.y_inverse = False
else:
golf_ball.y_inverse = True


My issue arises when the ball is launched at high speeds near the block, the golf ball can glitch through the block, which is obviously undesirable.

Below is my code and image for golf ball and box/block, the specific parts of code worth looking at is the shooting method and bounce_detection in the golf ball class, and the bounced method in the block class, the only reason I added all other code, is so that you can test out the code, and attempt to solve the issue.

import pygame
import math

pygame.init()
screen = pygame.display.set_mode((640, 480))
clock = pygame.time.Clock()
mouse_pos = pygame.mouse.get_pos()
dt = clock.tick(70) / 1000

class Golf_Ball:
def __init__(self, x_pos, y_pos, mass, w, h, mouse_pos, screen, dt):
self.screen = screen
self.resisting = False

# self.dt = dt
self.mass = mass
self.w = w
self.h = h
self.visible = True
self.initial_vel = 0
self.x_vel, self.y_vel = 0, 0
self.resultant = 0
self.mouse_pos = mouse_pos
self.fired = True
self.hit_time = 0
self.held_down = False
self.line = False
self.fired_resultant = 0
self.x_pos = x_pos
self.y_pos = y_pos
self.angle = 0
self.shoot = False
self.resistance = 100
self.velocity_constant = 0.3
self.spin_angel = 0
self.image_copy = pygame.transform.rotate(self.image, 0)
self.rect = self.image.get_rect(center=(self.x_pos, self.y_pos))
self.x_inverse, self.y_inverse = False, False

def mouse_click_manager(self):
if pygame.mouse.get_pressed()[0] == True and self.shoot == False:

if self.rect.collidepoint(self.mouse_pos):

if self.line == False and self.held_down == False:
self.held_down = True
self.line = True
if self.line and self.held_down == False:
self.held_down = True
self.line = False

self.fired_resultant = self.resultant * self.velocity_constant
self.initial_vel = int(round(((self.fired_resultant / self.mass) * 1), 0))
print("Hit", self.initial_vel)
self.shoot = True
self.x_inverse, self.y_inverse = False, False

elif not pygame.mouse.get_pressed()[0]:
self.held_down = False

def line_manager(self):
if self.line:

if abs(self.rect.centerx - self.mouse_pos[0]) != 0:
self.angle = math.degrees(
math.atan(abs(self.rect.centery - self.mouse_pos[1]) / abs(self.rect.centerx - self.mouse_pos[0])))
if self.mouse_pos[1] < self.rect.centery:
self.angle = self.angle * -1
if self.mouse_pos[0] < self.rect.centerx:
self.angle = abs(self.angle - 90) + 90
elif abs(self.rect.centerx - self.mouse_pos[0]) == 0:
if self.mouse_pos[1] > self.rect.centery:
self.angle = 90
else:
self.angle = -90

self.resultant = math.sqrt(
abs(self.rect.centerx - self.mouse_pos[0]) ** 2 + abs(self.rect.centery - self.mouse_pos[1]) ** 2)

pygame.draw.line(self.screen, "Black", (self.rect.centerx, self.rect.centery), self.mouse_pos, 3)

def shooting(self):

if self.x_inverse:
self.x_vel *= -1
if self.y_inverse:
self.y_vel *= -1
self.rect.centerx -= self.x_vel * self.dt
self.rect.centery -= self.y_vel * self.dt
self.initial_vel -= self.resistance * self.dt
if self.initial_vel <= 10:
self.shoot = False

def bounce_detection(self):
if self.rect.left <= 0:  # Left
if self.rect.left < 0:
self.rect.left = 1

if self.x_inverse:
self.x_inverse = False
else:
self.x_inverse = True

if self.rect.right >= 640:  # Right
if self.rect.right > 640:
self.rect.right = 639
if self.x_inverse:
self.x_inverse = False
else:
self.x_inverse = True

if self.rect.top <= 0:  # Top
if self.rect.top < 0:
self.rect.top = 1
if self.y_inverse:
self.y_inverse = False
else:
self.y_inverse = True

if self.rect.bottom >= 480:  # Bottom
if self.rect.bottom > 480:
self.rect.bottom = 479
if self.y_inverse:
self.y_inverse = False
else:
self.y_inverse = True

def update(self, mouse_pos, dt):
self.mouse_pos = mouse_pos
self.dt = dt
if self.visible:
if self.shoot:
self.shooting()
self.bounce_detection()
self.spin_angel += self.initial_vel * 10 * self.dt
self.image_copy = pygame.transform.rotate(self.image, self.spin_angel)

else:
self.mouse_click_manager()
self.line_manager()
self.resistance = 300
self.screen.blit(self.image_copy, (self.rect.centerx - int(self.image_copy.get_width() / 2),
self.rect.centery - int(self.image_copy.get_height() / 2)))
pygame.draw.rect(self.screen, 'White', self.rect, 1)

class Block:

def __init__(self, x_pos, y_pos, width, height, screen, disabled, dt):
self.x_pos = x_pos
self.y_pos = y_pos
self.screen = screen
self.disabled = disabled
self.width = width
self.height = height
(self.width, self.height))
self.rect = self.image.get_rect(topleft=(self.x_pos, self.y_pos))
self.dt = dt

self.clock = 0
self.counter = 0
self.dummy_rect = pygame.Rect((self.x_pos - 5, self.y_pos - 5), (self.rect.width + 10, self.rect.height + 10))
self.direction = True
self.in_block = False

def bounced(self, golf_ball):

if golf_ball.rect.colliderect(self.rect):
if abs(self.rect.left - golf_ball.rect.right) <= 5:

if golf_ball.rect.right > self.rect.left:
golf_ball.rect.right = self.rect.left
golf_ball.rect.right -= 1
if golf_ball.x_inverse:
golf_ball.x_inverse = False
else:
golf_ball.x_inverse = True  # Left

if abs(self.rect.right - golf_ball.rect.left) <= 5:

if golf_ball.rect.left < self.rect.right:
golf_ball.rect.left = self.rect.right
golf_ball.rect.left += 1

if golf_ball.x_inverse:
golf_ball.x_inverse = False
else:
golf_ball.x_inverse = True

if abs(self.rect.bottom - golf_ball.rect.top) <= 5:  # Bottom

if golf_ball.rect.top < self.rect.bottom:
golf_ball.rect.top = self.rect.bottom
golf_ball.rect.top += 1
if golf_ball.y_inverse:
golf_ball.y_inverse = False
else:
golf_ball.y_inverse = True

if abs(self.rect.top - golf_ball.rect.bottom) <= 5:
if golf_ball.rect.bottom > self.rect.top:
golf_ball.rect.bottom = self.rect.top
golf_ball.rect.bottom -= 1

if golf_ball.y_inverse:
golf_ball.y_inverse = False
else:
golf_ball.y_inverse = True

def update(self, golf_ball):
self.bounced(golf_ball)

self.screen.blit(self.image, self.rect)
pygame.draw.rect(self.screen, 'Black', self.rect, 1)

golf_ball = Golf_Ball(200, 200, 0.04593, 20, 20, mouse_pos, screen, dt)
block_1 = Block(100, 300, 50, 20, screen, False, dt)

block = pygame.transform.rotate(
block_rect = block.get_rect(topleft=(300, 200))

running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:

key = event.key
if event.key == pygame.K_ESCAPE:
running = False

mouse_pos = pygame.mouse.get_pos()
screen.fill('Green')
golf_ball.update(mouse_pos, dt)
block_1.update(golf_ball)

# screen.blit(block, block_rect)
clock.tick(70)
dt = clock.tick(70) / 1000
pygame.display.flip()

pygame.quit()
`

• Please lookup continuous collision detection. Commented Dec 8, 2023 at 23:47
• Thanks, will do.
– Jet
Commented Dec 8, 2023 at 23:55