Menu
CoddyTech

Vùng va chạm hình tròn

Thuộc phần Sprite và hình ảnh trong hành trình Pygame của Coddy. Bài học 25 trên 39.

Kiểm tra từng pixel rất chính xác, nhưng chính xác không phải lúc nào cũng tạo cảm giác công bằng. Một chiếc gai sượt qua đầu cánh cũng được tính là va chạm. Thay vào đó, nhiều trò chơi kiểm tra một hình dạng nhỏ hơn, đơn giản hơn gọi là vùng va chạm, để những pha suýt trúng không bị tính.

Với các vật thể tròn, hãy kiểm tra bằng hình tròn. Gán cho mỗi sprite một radius, rồi pygame.sprite.collide_circle tính là va chạm khi khoảng cách giữa hai tâm nhỏ hơn tổng hai bán kính:

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

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

Ở đây, khoảng cách giữa hai tâm là 24 + 22 = 46 pixel. Các hình tròn nhỏ hơn hình ảnh, nên các cạnh ngoài cùng có thể sượt qua nhau mà không tính là va chạm. Kiểm tra bằng hình tròn cũng rất nhẹ: chỉ cần tính một khoảng cách, không cần xét pixel.

Nếu không có radius, collide_circle dùng một hình tròn bao quanh toàn bộ Rect, lớn hơn chính Rect. Với sprite hình hộp, Rect nhỏ hơn cũng cho kết quả tương tự: self.rect.inflate(-16, -16) là Rect hẹp hơn và ngắn hơn 16 pixel, nhưng vẫn giữ nguyên tâm.

challenge icon

Thử thách

Trung bình

Kiểm tra va chạm bằng hình tròn thay vì mặt nạ.

  1. Gán cho Player self.radius = 24 và cho Ball self.radius = 22.
  2. Kiểm tra bằng pygame.sprite.collide_circle thay vì collide_mask.

Nhấn Chơi: đầu cánh có thể sượt qua gai nhọn mà Hit! không xuất hiện. Sau đó nhấn Kiểm tra.

Tự làm thử

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 iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Sprite và hình ảnh

Tự luyện tập: Playground Pygame