You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 02:06:25