Circle Hitboxes
Part of the Sprites and Images section of Coddy's Pygame journey. Lesson 24 of 36.
A pixel test is exact, but exact is not always what feels fair. A spike grazing the tip of a wing counts as a hit. Many games test a smaller, simpler shape instead, a hitbox, and let near misses go.
For round things, test circles. Give each sprite a radius, and pygame.sprite.collide_circle calls it a hit when the centres are closer than the two radii added together:
self.radius = 24 # in Player.__init__
self.radius = 22 # in Ball.__init__
hits = pygame.sprite.spritecollide(player, balls, False, pygame.sprite.collide_circle)Here that is 24 + 22 = 46 pixels between the centres. The circles are smaller than the pictures, so the very edges can brush without a hit. A circle test is also cheap: one distance, no pixels.
Without a radius, collide_circle uses a circle around the whole Rect, bigger than the Rect itself. For a box-shaped sprite, a smaller Rect does the same job: self.rect.inflate(-16, -16) is the Rect 16 pixels narrower and shorter, with the same centre.
Challenge
Medium- Give
Playerself.radius = 24andBallself.radius = 22. - Test with
pygame.sprite.collide_circleinstead ofcollide_mask.
Try it yourself
import pygame
pygame.init()
screen = pygame.display.set_mode((640, 480))
pygame.display.set_caption("Spike Field")
clock = pygame.time.Clock()
ship_image = pygame.image.load("assets/images/ship.png").convert_alpha()
ball_image = pygame.image.load("assets/images/spike_ball.png").convert_alpha()
font = pygame.font.Font(None, 40)
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = ship_image
self.rect = self.image.get_rect(topleft=(420, 320))
self.mask = pygame.mask.from_surface(self.image)
def update(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
self.rect.x -= 4
if keys[pygame.K_RIGHT]:
self.rect.x += 4
if keys[pygame.K_UP]:
self.rect.y -= 4
if keys[pygame.K_DOWN]:
self.rect.y += 4
class Ball(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.image = ball_image
self.rect = self.image.get_rect(center=(x, y))
self.mask = pygame.mask.from_surface(self.image)
player = Player()
balls = pygame.sprite.Group(Ball(160, 240), Ball(320, 240), Ball(480, 240))
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
player.update()
hits = pygame.sprite.spritecollide(player, balls, False, pygame.sprite.collide_mask)
screen.fill((20, 22, 40))
balls.draw(screen)
screen.blit(player.image, player.rect)
if hits:
text = font.render("Hit!", True, (240, 240, 240))
screen.blit(text, (10, 10))
pygame.display.flip()
clock.tick(60)
pygame.quit()
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Sprites and Images
3Classes for Game Objects
Your First ClassMethodsSelf Is the ObjectA List of ObjectsObjects with ImagesA SubclassPractice on your own: Pygame playground