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.
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)
# [...]