[ad_1]
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):
self.x_vel = self.initial_vel * (math.cos(math.radians(self.angle)))
self.y_vel = self.initial_vel * (math.sin(math.radians(self.angle)))
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 = pygame.image.load('Assets/golf.png').convert_alpha()
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):
self.x_vel = self.initial_vel * (math.cos(math.radians(self.angle)))
self.y_vel = self.initial_vel * (math.sin(math.radians(self.angle)))
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.image = pygame.transform.smoothscale(pygame.image.load('Assets\Wooden_Block.png'),
(self.width, self.height))
self.rect = self.image.get_rect(topleft=(self.x_pos, self.y_pos))
self.mask = pygame.mask.from_surface(self.image)
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(
pygame.transform.smoothscale(pygame.image.load('Assets\Wooden_Block.png').convert_alpha(), (100, 50)), 45)
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()
[ad_2]