Pygame sprite.collide_circle使用外接圆而非内切圆的碰撞问题
Pygame collide_circle 使用外接圆而非内切圆的问题解析
问题重现代码
import pygame pygame.init() window = pygame.display.set_mode((250, 250)) sprite1 = pygame.sprite.Sprite() sprite1.image = pygame.Surface((80, 80), pygame.SRCALPHA) pygame.draw.circle(sprite1.image, (255, 0, 0), (40, 40), 40) sprite1.rect = pygame.Rect(*window.get_rect().center, 0, 0).inflate(80, 80) #sprite1.rect = pygame.Rect(*sprite1.rect).inflate(-23.44,-23.44) sprite2 = pygame.sprite.Sprite() sprite2.image = pygame.Surface((80, 80), pygame.SRCALPHA) pygame.draw.circle(sprite2.image, (0, 255, 0), (40, 40), 40) sprite2.rect = pygame.Rect(125, 125, 0, 0).inflate(80, 80) all_group = pygame.sprite.Group([sprite2, sprite1]) test_group = pygame.sprite.Group(sprite2) run = True while run: for event in pygame.event.get(): if event.type == pygame.QUIT: run = False if event.type == pygame.KEYDOWN: print(pygame.mouse.get_pos()) sprite1.rect.center = pygame.mouse.get_pos() collide = pygame.sprite.spritecollide(sprite1, test_group, False, pygame.sprite.collide_circle) window.fill(0) all_group.draw(window) pygame.draw.rect(window, (255,255,255), sprite1.rect, 1) pygame.draw.rect(window, (255,255,255), sprite2.rect, 1) pygame.draw.circle(window, (255, 255, 255), sprite1.rect.center, 56.5, 1) pygame.draw.circle(window, (255, 255, 255), sprite2.rect.center, 56.5, 1) for s in collide: pygame.draw.circle(window, (255, 255, 255), s.rect.center, s.rect.width // 2, 5) pygame.display.flip() pygame.quit() exit()
问题原因
pygame.sprite.collide_circle的默认逻辑是:如果精灵没有自定义radius属性,就会自动计算精灵rect的外接圆半径——也就是以rect中心为圆心,rect对角线长度的一半为半径的圆(对应你代码里画的白色外层圆环),而非你期望的图像内切圆。
你尝试缩小rect的方式会导致绘制偏移,是因为sprite.draw()是基于rect的左上角位置对齐图像的,修改rect大小后,图像的绘制原点和rect的原点不再匹配,自然会错位。
解决方案:给精灵添加自定义radius属性
不需要修改rect的大小,只要给每个圆形精灵添加radius属性,collide_circle就会优先使用这个值作为碰撞圆的半径,完美匹配你画的内切圆,同时不会影响精灵的绘制位置:
# 修改sprite1的定义,添加radius属性 sprite1 = pygame.sprite.Sprite() sprite1.image = pygame.Surface((80, 80), pygame.SRCALPHA) pygame.draw.circle(sprite1.image, (255, 0, 0), (40, 40), 40) sprite1.rect = pygame.Rect(*window.get_rect().center, 0, 0).inflate(80, 80) sprite1.radius = 40 # 自定义碰撞半径,和图像里的圆半径一致 # 同样修改sprite2 sprite2 = pygame.sprite.Sprite() sprite2.image = pygame.Surface((80, 80), pygame.SRCALPHA) pygame.draw.circle(sprite2.image, (0, 255, 0), (40, 40), 40) sprite2.rect = pygame.Rect(125, 125, 0, 0).inflate(80, 80) sprite2.radius = 40
关于mask碰撞的补充
如果后续遇到更复杂的非规则形状碰撞,使用mask是精准的方案,但对于圆形来说,自定义radius是更轻量、高效的选择,性能比mask碰撞更好。
内容的提问来源于stack exchange,提问作者Pauley
相关产品推荐
相关产品推荐

