Menu
CoddyTech

Круглые хитбоксы

Часть раздела Спрайты и изображения путешествия по Pygame на Coddy. Урок 24 из 36.

Проверка пикселей точна, но точность не всегда кажется справедливой. Шип, задевший кончик крыла, считается попаданием. Во многих играх вместо этого проверяют фигуру поменьше и попроще — область попадания — и не считают попаданием случаи, когда объекты едва разминулись.

Для круглых объектов проверяйте круги. Задайте каждому спрайту radius, и pygame.sprite.collide_circle сочтёт столкновение попаданием, если расстояние между центрами меньше суммы двух радиусов:

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

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

В этом случае расстояние между центрами равно 24 + 22 = 46 пикселям. Круги меньше изображений, поэтому их самые края могут задевать друг друга без попадания. Проверка кругов также обходится дёшево: одно расстояние, никаких пикселей.

Если не задан radius, collide_circle использует круг вокруг всего Rect, который больше самого Rect. Для спрайта прямоугольной формы ту же задачу решит прямоугольник Rect поменьше: self.rect.inflate(-16, -16) — это Rect на 16 пикселей уже и короче, с тем же центром.

challenge icon

Задание

Средне
  1. Задай Player self.radius = 24, а Ball — self.radius = 22.
  2. Проверь с помощью pygame.sprite.collide_circle вместо collide_mask.

Попробуйте сами

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 iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Спрайты и изображения

Потренируйтесь самостоятельно: Песочница Pygame