为什么 class 碰撞在 PyGame 中不起作用?

Why is class collision not working in PyGame?

我正在制作一款向雪人扔雪球的游戏。以下是 Snowman class 和 Snowball class 的定义:

class Snowman:
    def __init__(self,start,end,a,b):
        self.x = random.randrange(0,310)
        self.y = 0
        self.r = pygame.Rect(self.x,self.y,a,b)

class Snowball:
    def __init__(self,a,b):
        self.x = mousex
        self.y = mousey
        self.r = pygame.Rect(self.x,self.y,a,b)

如您所见,classes 的 r 属性是它们的矩形。

下面是一个控制雪人AI的脚本。

for s in snowmen:
    s.y += 0.5
    #above_makes_the_snowmen_approach_the_bottom_of_the_screen.
    #below_is_supposed_to_check_for_snowball_collision.
    for b in bullets:
        if s.r.colliderect(b.r):
            snowmen.remove(s)
            bullets.remove(b)
        print(b.r.colliderect(s.r))
        #above_is_a_debugger_that_reports_the_collision.
    if s.y < 640:
        blit(asnowman,(50,78),(s.x,s.y))
    else:
        snowmen.remove(s)

问题是当我向其中一个雪人扔雪球时,调试器报告 False 并且雪人没有死。我该如何解决这个问题?

参见 How do I detect collision in pygame?。您需要在碰撞测试之前更新矩形的位置:

s.r.topleft = round(s.x), round(s.y)
b.r.topleft = round(b.x), round(b.y)
if s.r.colliderect(b.r):

请注意,您不能去掉 xy 属性,因为 pygame.Rect 应该代表屏幕上的一个区域,而 pygame.Rect 对象可以只存储积分数据。

The coordinates for Rect objects are all integers. [...]


另请阅读How to remove items from a list while iterating?。一种可能的解决方案是遍历列表的副本:

for s in snowmen[:]:
    s.y += 0.5
    s.r.topleft = round(s.x), round(s.y)
    for b in bullets[:]:
        b.r.topleft = round(b.x), round(b.y)
        if s.r.colliderect(b.r):
            snowmen.remove(s)
            bullets.remove(b)
            break

虽然您在每个 类 中初始化 Rect 对象以匹配创建对象时的 xy 属性,但它们不会保留在同步。这意味着您只会在矩形 的初始位置 碰撞矩形,而不是物体后来移动到的位置。

解决这个问题的最简单方法可能是添加几行代码来更新 s.r and/or b.r,但您也可以开始使用 pygame.sprite.Sprite,这将更新其 rect 属性以匹配其 xy 属性(我认为反之亦然)。如果你想让自己的 类 表现得有点像 Sprite 而不是一路走下去,我建议将 xy 变成 property 描述符,它引用 Rect,它成为唯一的真实来源:

class Snowman:
    def __init__(self,start,end,a,b):
        self.r = pygame.Rect(random.randrange(0,310),0,a,b)

    @property
    def x(self):
        return self.r.left
    @x.setter
    def x(self, value):
        self.r.left = value

    @property
    def y(self):
        return self.r.top
    @y.setter
    def y(self, value):
        self.r.top = value