圆形碰撞箱
本课属于 Coddy Pygame 学习旅程的“精灵与图像”单元。第 25 课,共 39 课。
像素测试很精确,但精确并不总是让人觉得公平。尖刺擦到翅膀尖端也算命中。许多游戏会改为测试一个更小、更简单的形状,也就是碰撞箱,并让擦边未命中不算命中。
对于圆形物体,可以测试圆形。为每个精灵设置一个 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 像素,中心位置不变。
挑战
中等使用圆形而不是遮罩测试碰撞。
- 为
Player设置self.radius = 24,为Ball设置self.radius = 22。 - 使用
pygame.sprite.collide_circle代替collide_mask进行测试。
按“开始”:飞船的翼尖可能会擦过尖刺,但不会显示 Hit!。然后点击“检查”。
亲手试试
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()
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
精灵与图像中的所有课
自己练习: Pygame 在线编辑器