colliderect()意外触发

时间:2014-12-25 02:05:59

标签: pygame collision-detection python-3.4 rect surface

我正在制作经典的Snake游戏。我想绘制一个绿色矩形(green_rect),每当我的蛇接触它时,该绿色矩形移动到一个新的随机位置。当我在蠕虫colliderect()上运行surface时,由于某种原因,它总是返回True,即使蠕虫没有触及矩形。

到目前为止,我已经尝试过:

width  = 640 
height = 400
screen = pygame.display.set_mode((width, height))
green  = pygame.Surface((10,10)) #it's the rectangle
green.fill((0,255,0))
green_rect = pygame.Rect((100,100), green.get_size())

  # Our worm.
w = Worm(screen, width/2, height/2, 100)   # I have a class called Worm, described later...

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

        elif event.type == pygame.KEYDOWN:
            w.key_event(event)   # direction control method in the Worm class

    if green_rect.colliderect(w.rect):  # w.rect is coming from Worm class: self.rect=self.surface.get_rect()
        # surface variable is 100 coming from: w = Worm(screen, width/2, height/2, 100) -- it describes the worm's length in segments / pixels.
        green_rect.x, green_rect.y = (random.randrange(420),random.randrange(300))


    screen.fill((0, 0, 0))
    screen.blit(green, green_rect)

    w.move() #function for movement in Worm class
    w.draw() #function for drawing the snake in Worm class

但是这不起作用,绿色矩形无法控制地移动,即使我没有用蛇触摸它。 colliderect一定不能正常工作,因为绿色矩形的x-y坐标在没有蛇实际接触的情况下发生变化。它只应在蛇接触绿色矩形时改变位置。

  

我没有显示我的所有代码,因为它的篇幅很长。如果有必要,我可以   写出类蠕虫。那么 rect 方法不起作用   列表,所以我无法弄清楚如何解决这个问题。

编辑:我也想知道如何调整蛇的片段大小,因为我的默认大小是一个像素(点)。我希望它更大,所以如果它碰到一个大矩形,即使在矩形的一角或一边,矩形仍然会移动。

  

编辑-2:这是我的Worm课程:

class Worm():
    def __init__(self, surface, x, y, length):
        self.surface = surface
        self.x = x
        self.y = y
        self.length  = length
        self.dir_x   = 0
        self.dir_y   = -1
        self.body    = []
        self.crashed = False


    def key_event(self, event):
        """ Handle key events that affect the worm. """
        if event.key == pygame.K_UP:
            self.dir_x = 0
            self.dir_y = -1
        elif event.key == pygame.K_DOWN:
            self.dir_x = 0
            self.dir_y = 1
        elif event.key == pygame.K_LEFT:
            self.dir_x = -1
            self.dir_y = 0
        elif event.key == pygame.K_RIGHT:
            self.dir_x = 1
            self.dir_y = 0


    def move(self):
        """ Move the worm. """
        self.x += self.dir_x
        self.y += self.dir_y

        if (self.x, self.y) in self.body:
            self.crashed = True

        self.body.insert(0, (self.x, self.y))

        if len(self.body) > self.length:
            self.body.pop()


    def draw(self):
        for x, y in self.body:
            self.surface.set_at((int(x),int(y)), (255, 255, 255))

1 个答案:

答案 0 :(得分:1)

我想我明白这里发生了什么: 您的类Worm由一个对象组成,该对象包含一个xy元组列表,每个像素占一个像素。 '蠕虫'穿过游戏区域,在其路径中添加下一个像素到该列表,并在它达到最大长度时丢弃多余的像素位置。但Worm从未定义自己的矩形,而Worm.surface实际上是显示区域的表面。

你的目标green_rect一直在跳跃的原因是因为w.surface指的是screen,而不是蠕虫自己的像素区域。因此,w.surface.get_rect()会为整个显示区域返回一个矩形。绿色矩形无处可去没有与该矩形发生冲突。

考虑到这一点,这里有一个解决方案:

  # in class Worm...
def __init__(self, surface, x, y, length):
    self.surface = surface
    self.x = x
    self.y = y
    self.length = length
    self.dir_x = 0
    self.dir_y = -1
    self.body = []
    self.crashed = False

    self.rect = pygame.Rect(x,y,1,1)  # the 1,1 in there is the size of each worm segment!
      # ^ Gives the 'head' of the worm a rect to collide into things with.

  #...

def move(self):
    """ Move the worm. """
    self.x += self.dir_x
    self.y += self.dir_y

    if (self.x, self.y) in self.body:
        self.crashed = True

    self.body.insert(0, (self.x, self.y))

    if len(self.body) > self.length:
        self.body.pop()

    self.rect.topleft = self.x, self.y
      # ^ Add this line to move the worm's 'head' to the newest pixel.

请记住代码顶部的import pygame, random, sys并在最后输入pygame.display.flip()(或update,如果您有更新请求列表),则应设置。

就目前而言,崩溃的蠕虫代码会自动运行,但不起作用。

还有一个建议:您可能希望将Worm.surface重命名为Worm.display_surface,因为pygame也有一个名为Surface的东西,它可能会让其他人更容易了解这两者是否更明确。


如果您正在寻找由list Rect组成的更复杂的蠕虫,那么这也是可能的。只需使用Worm.body替换rect列表中的xy元组,并将if (self.x, self.y) in self.body:替换为if self.rect.collidelist(self.body) != -1:,它应该以相同的方式工作。 (collidelist(<list of Rects>)返回该列表中第一个碰撞rect的索引,如果没有则返回-1。)

请确保您也更改了self.rect中的class Worm,以便它更像

`self.rect = pygame.Rect(x,y, segment_width, segment_height)`

...并调整self.dir_xself.dir_y,使其分别等于+/- segment_widthsegment_height。您需要fill每个细分,或使用pygame.draw.rects,而不是set_at使用Worm.draw()


更新:可变大小的蠕虫段

这里有一些代码使用Rect代替蠕虫的身体代替xy元组。我设置它以便蠕虫在初始化期间定义每个段的大小,然后以该大小的步长向前移动(取决于行进轴)。请注意,蠕虫移动速度非常快,没有任何阻止它逃离游戏区域,并且长度为100个单位!

这是我的解决方案,无论如何 - 首先是修订后的class Worm代码。我已经用注释标记了更改的行,包括原始答案中的新行。

class Worm(pygame.sprite.Sprite): # MODIFIED CONTENTS!
    def __init__(self, surface, x, y, length, (seg_width, seg_height)): # REVISED
        self.surface = surface
        self.x = x
        self.y = y
        self.length = length
        self.dir_x = 0
        self.dir_y = -seg_width # REVISED
        self.body = []
        self.crashed = False

        self.rect = pygame.Rect(x,y,seg_width,seg_height) # NEW/REVISED
        self.segment = pygame.Rect(0,0,seg_width,seg_height) # NEW


    def key_event(self, event):
        """ Handle key events that affect the worm. """
        if event.key == pygame.K_UP:
            self.dir_x = 0
            self.dir_y = -self.segment.height # REVISED
        elif event.key == pygame.K_DOWN:
            self.dir_x = 0
            self.dir_y = self.segment.height # REVISED
        elif event.key == pygame.K_LEFT:
            self.dir_x = -self.segment.width # REVISED
            self.dir_y = 0
        elif event.key == pygame.K_RIGHT:
            self.dir_x = self.segment.width # REVISED
            self.dir_y = 0


    def move(self):
        """ Move the worm. """
        self.x += self.dir_x
        self.y += self.dir_y

        self.rect.topleft = self.x, self.y # NEW/MOVED

        if self.rect.collidelist(self.body) != -1: # REVISED
            self.crashed = True
            print "CRASHED!"

        new_segment = self.segment.copy() # NEW
        self.body.insert(0, new_segment.move(self.x, self.y)) # REVISED

        if len(self.body) > self.length:
            self.body.pop()


    def draw(self): # REVISED
        for SEGMENT in self.body:   # REPLACEMENT
            self.surface.fill((255,255,255), SEGMENT) # REPLACEMENT

...当您指定w以包含所需的蠕虫段维度时,您还必须修改参数。在这种情况下,我将4像素宽,4像素高。

w = Worm(screen, width/2, height/2, 100, (4,4)) # REVISED

这就是我的所作所为。请注意,您可以指定您喜欢的任何段大小(只要每个维度都是大于零的整数),即使您喜欢使用单像素或矩形(即非方形&#39;)段