Menu
CoddyTech

Hitboxes circulares

Parte de la sección Sprites e imágenes del Journey de Pygame de Coddy. Lección 24 de 36.

Una prueba píxel a píxel es exacta, pero lo exacto no siempre parece justo. Si una espiga roza la punta de un ala, cuenta como impacto. En muchos juegos se prueba en su lugar con una forma más pequeña y sencilla, una caja de colisión, y se dejan pasar los fallos por poco.

Para los objetos redondos, prueba con círculos. Dale a cada sprite un radius, y pygame.sprite.collide_circle lo considera un impacto cuando los centros están más cerca que la suma de los dos radios:

self.radius = 24  # en Player.__init__
self.radius = 22  # en Ball.__init__

hits = pygame.sprite.spritecollide(player, balls, False, pygame.sprite.collide_circle)

En este caso, son 24 + 22 = 46 píxeles entre los centros. Los círculos son más pequeños que las imágenes, así que los bordes pueden rozarse sin que haya impacto. Una prueba con círculos también es económica: una distancia, ningún píxel.

Sin un radius, collide_circle usa un círculo alrededor de todo el Rect, más grande que el propio Rect. Para un sprite con forma de caja, un Rect más pequeño hace lo mismo: self.rect.inflate(-16, -16) es el Rect 16 píxeles más estrecho y bajo, con el mismo centro.

challenge icon

Desafío

Intermedio
  1. Asigna a Player self.radius = 24 y a Ball self.radius = 22.
  2. Prueba con pygame.sprite.collide_circle en lugar de collide_mask.

Pruébalo tú mismo

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()
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Sprites e imágenes

Practica por tu cuenta: Playground de Pygame