pygame.Surface和鼠标之间的碰撞检测不起作用

问题描述

我正在尝试制作用于像素艺术的画布。

class Canvas:
    def __init__(self):
        self.__blocks = []
        self.__positions = []
        for i in range(1830):
            self.__blocks.append(pygame.Surface((20,20)).convert())
        for y in range(30):
            y *= 20
            for x in range(61):
                x = x* 20
                self.__positions.append([x,y])
        self.__color = False

    def draw(self,window):
        for i in range(1830):
            self.__color = not self.__color
            if self.__color:
                self.__blocks[i].fill((200,200,200))
            else:
                self.__blocks[i].fill((50,50,50))
            window.blit(self.__blocks[i],(self.__positions[i][0],self.__positions[i][1]))

在这里,我试图生成和绘制1830个唯一的曲面,并且可以正常工作。然后,我尝试在每个块与鼠标之间实施碰撞检测,但失败了。

def collided(self,pos):
      for i in range(1380):
          block = self.__blocks[i].get_rect()
          if block.collidepoint(pos[0],pos[1]):
              print(block.x,block.y)

然后,我对失败的原因进行了不同的测试。这是其中之一。我将单个块的颜色更改为红色,在本例中为self.__blocks[10].fill((255,0)),将其更改为红色,以便我们知道要单击哪个框。然后,我们将尝试检查该特定块的碰撞情况。

def testBlock(self,pos):
    block = self.__blocks[10].get_rect()
    if block.collidepoint(pos[0],pos[1]):
        print(block.x)

它不起作用,但是奇怪的是,无论我测试哪个表面,它都适用于第一个块(在第0个索引中),并且仅适用于第一个块。任何有关如何解决此问题的想法将不胜感激。以下是复制和粘贴代码

import pygame
pygame.init()

win = pygame.display
D = win.set_mode((1220,600))

class Canvas:
    def __init__(self):
        self.__blocks = []
        self.__positions = []
        for i in range(1830):
            self.__blocks.append(pygame.Surface((20,y])
        self.__color = False
        self.testBlock = 10

    def draw(self,50))
            self.__blocks[self.testBlock].fill((255,0)) # Changing the color for testing 
                                                
            window.blit(self.__blocks[i],self.__positions[i][1]))


    def test(self,pos):
        block = self.__blocks[self.testBlock].get_rect()
        if block.collidepoint(pos[0],pos[1]):
            print(block.x,block.y)


canvas = Canvas()
while True:
    D.fill((0,0))
    pygame.event.get()
    mousepos = pygame.mouse.get_pos()
    canvas.draw(D)
    canvas.test(mousepos)
    win.flip()

解决方法

在Surface上调用.get_rect()时,它不知道其当前位置,因为这不是Surface信息。因此,您需要在碰撞检测之前将位置分配给Rect。

使用当前的代码布局,您可以在构建期间执行此操作。现在,在__rects列表中保留了Canvass块位置,__positions列表变得多余。

class Canvass:
    
    def __init__(self):
        self.__blocks    = []
        self.__rects     = []

        for y in range( 30 ):
            for x in range( 61 ):
                self.__blocks.append(pygame.Surface((20,20)).convert())
                self.__rects.append( self.__blocks[-1].get_rect() )
                self.__rects[-1].topleft = ( x,y )

        self.__color = False
        self.testBlock = 10

这为您提供了一个简单的测试:

def collided(self,pos):
    hit = False
    for i in range( len( self.__rects ) ):
        if ( self.__rects[i].collidepoint( pos[0],pos[1] ) ):
            print( "Click on block %d" % ( i ) )
            hit = True
            break
    return hit,i
    
,

.get_rect()给出矩形的大小,但位置为(0,0)

您在__positions中有实际职位,您将需要

  .get_rect(topleft=self.__positions[self.testBlock])

def test(self,pos):
        block = self.__blocks[self.testBlock].get_rect(topleft=self.__positions[self.testBlock])
        if block.collidepoint(pos[0],pos[1]):
            print(block.x,block.y)

但是最好先获得rect并在开始时设置其位置,然后再不使用get_rect()

您还可以创建类似于类Pixel的类Sprite,其中self.image用于保留表面,self.rect用于保留其大小和位置。然后,您可以使用Group检查与所有像素的碰撞。


编辑:

使用类pygame.sprite.Sprite创建类Pixel并将所有像素保留在pygame.sprite.Group中的示例

它还处理事件(MOUSEBUTTONDOWN),以在单击时更改任何像素的颜色。

enter image description here

import pygame

# --- classes ---

class Pixel(pygame.sprite.Sprite):
    
    def __init__(self,x,y,color,width=20,height=20):
        super().__init__()
        self.color_original = color
        
        self.color = color

        self.image = pygame.Surface((20,20)).convert()
        self.image.fill(self.color)

        self.rect = pygame.Rect(x,width,height)
        
    def handle_event(self,event):
        if event.type == pygame.MOUSEBUTTONDOWN:

            if self.rect.collidepoint(event.pos):
                if self.color != self.color_original:
                    self.color = self.color_original
                else:
                    self.color = (255,0)
                self.image.fill(self.color)
                # event handled
                return True
            
        # event not handled
        return False
         
class Canvas:
    
    def __init__(self):
        # create group for sprites
        self.__blocks = pygame.sprite.Group()

        # create sprites
        self.__color = False
    
        for y in range(30):
            y *= 20
            for x in range(61):
                x *= 20
                self.__color = not self.__color
                if self.__color:
                    color = (200,200,200)
                else:
                    color = (50,50,50)
                self.__blocks.add(Pixel(x,color))
                    
        # changing the color for testing 
        self.testBlock = 10
        
        all_sprites = self.__blocks.sprites()
        block = all_sprites[self.testBlock]
        
        block.image.fill((255,0))

    def draw(self,window):
        # draw all sprites in group
        self.__blocks.draw(window)

    def test(self,pos):
        # test collision with one sprite
        all_sprites = self.__blocks.sprites()
        block = all_sprites[self.testBlock]
        if block.rect.collidepoint(pos):
            print(block.rect.x,block.rect.y)
            
    def handle_event(self,event):
        for item in self.__blocks:
            if item.handle_event(event):
                # don't check other pixels if event already handled
                return True
            
# --- main ---

pygame.init()

win = pygame.display
D = win.set_mode((1220,600))

canvas = Canvas()
while True:

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            exit()
        canvas.handle_event(event)
        
    #mousepos = pygame.mouse.get_pos()
    #canvas.test(mousepos)

    # draws (without updates,etc)
    #D.fill((0,0)) # no need clean screen if it will draw all elements again
    canvas.draw(D)
    win.flip()