I encountered an issue in my brick breaker game where the paddle has round edges and i want to reflect the ball with perfect angle, i was using angle and sin and cos for the direction, but i moved to vectors after a recommendation, i made a similar problem for the sake of simplicity.
here is the code:
import sys
import pygame
pygame.init()
w, h = 1200, 800
screen = pygame.display.set_mode((w, h))
clock = pygame.Clock()
class Bullet(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.image = pygame.Surface((10, 10))
self.image.set_colorkey((0,0,0))
pygame.draw.circle(self.image, (255, 0, 0), (5, 5), 3)
self.rect = self.image.get_rect(topleft = (x, y) )
self.mask = pygame.mask.from_surface(self.image)
self.velocity = pygame.Vector2(0, -10)
def update(self, circlex, circley, circle_mask):
if self.rect.y < 0 or self.rect.y > 800:
self.kill()
if self.rect.x < 0 or self.rect.x > 1200:
self.kill()
self.rect.y += self.velocity.y
offset = circlex - self.rect.x, circley - self.rect.y
if p := self.mask.overlap(circle_mask, offset):
v = pygame.Vector2(p)
self.velocity.reflect_ip(v)
def draw(self, screen):
screen.blit(self.image, self.rect)
circle = pygame.Surface((400, 400))
pygame.draw.circle(circle, (255, 255, 255), (200, 200), 150)
circle_rect = circle.get_rect(topleft = (400, 100))
circle.set_colorkey((0, 0, 0))
circle_mask = pygame.mask.from_surface(circle)
gun = pygame.Surface((100, 40))
gun_rect = gun.get_rect(topleft = (200, 750))
gun.fill("red")
bullets = pygame.sprite.Group()
while True:
mx, my = pygame.mouse.get_pos()
for e in pygame.event.get():
if e.type == pygame.QUIT:
pygame.quit()
sys.exit()
if e.type == pygame.MOUSEBUTTONDOWN:
b = Bullet(mx, 750)
bullets.add(b)
screen.fill((30, 30, 30))
gun_rect.centerx = mx
screen.blit(gun, gun_rect)
screen.blit(circle, circle_rect)
bullets.update(circle_rect.x, circle_rect.y, circle_mask)
bullets.draw(screen)
pygame.display.flip()
clock.tick(120)
the issue is the bullet is getting straight through the circle, if you could explain how vector works that would be great.

>Solution :
Change the position by both components of the direction vector
self.rect.center += self.velocity
Detect the collision of the circle with the bullet, not the other way around:
offset = self.rect.x - circle_rect.x, self.rect.y - circle_rect.y
if p := circle_mask.overlap(self.mask, offset):
The reflection vector is the vector from the center of the circle the the hit position:
v = pygame.Vector2(p) - (circle_rect.width/2, circle_rect.height/2)
Complete update method:
class Bullet(pygame.sprite.Sprite):
# [...]
def update(self, circle_rect, circle_mask):
if self.rect.y < 0 or self.rect.y > 800:
self.kill()
if self.rect.x < 0 or self.rect.x > 1200:
self.kill()
self.rect.center += self.velocity
offset = self.rect.x - circle_rect.x, self.rect.y - circle_rect.y
if p := circle_mask.overlap(self.mask, offset):
v = pygame.Vector2(p) - (circle_rect.width/2, circle_rect.height/2)
self.velocity.reflect_ip(v)
# [...]
while True:
# [...]
bullets.update(circle_rect, circle_mask)
# [...]
