问题描述
我正在尝试制作用于像素艺术的画布。
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
),以在单击时更改任何像素的颜色。
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()