Pygame 像素完美碰撞未按预期工作
Pygame pixel perfect collision not working as expected
我正在使用 pygame 创建俄罗斯方块。我想使用碰撞检测,以便当游戏中的形状与之前播放的任何其他形状接触时,我可以按照俄罗斯方块的逻辑停止该形状。我遇到了使用蒙版的像素完美碰撞。我已经在线学习了一些教程,但是每次出现新形状时像素检测 returns 为真,而不是任何形状发生碰撞时。对于长代码提前抱歉,它是代码实际并且仍然包含它的游戏元素的最低限度。我认为我的方法有问题导致了这个错误。我基本上有一个功能,每当游戏中的形状与 'floor' 接触时,该形状就会保持在该位置并创建一个新形状。我认为我把它复杂化了,反过来又造成了这个错误。提前致谢
import pygame
import sys
import shapelogic
pygame.init()
screensize = width, height = 800, 595
screen = pygame.display.set_mode(screensize)
background_image =pygame.image.load("/Users/marceason/PycharmProjects/Tetris/Wooden_background.jpg").convert_alpha()
myshape = 0
stop_movement = 0
blit_count = 0
stored_shapes = pygame.sprite.Group()
stored_shapes_with_coords = []
extra_blit_required = False
index = 0
count = 0
listofshapes = []
class shapemanager():
def __init__(self):
self.listofshapes = []
def create_another_instance(self):
global count
count += 1
string = "Shape_{0},".format(count)
another_shape = Shape(string)
self.listofshapes.append(another_shape)
global index
object = self.listofshapes[index]
index += 1
return object
def load_shape(self):
shape = self.create_another_instance()
shape.load_shapes()
class Shape(pygame.sprite.Sprite):
def __init__(self, name):
pygame.sprite.Sprite.__init__(self)
self.name = name
self.x = 50
self.y = 100
self.move_event = pygame.USEREVENT + 1
self.reached_bottom_event = pygame.USEREVENT + 2
self.one_sec_timer = 1000
self.half_sec_timer = 500
self.reachbottomflag = False
self.movement_possible = True
self.image = pygame.image.load(
"/Users/marceason/PycharmProjects/Tetris/Tetris_Shapes/Green_Shape_1_Position_1.png")
self.mask = pygame.mask.from_surface(self.image)
self.rect = self.image.get_rect()
def move_shape(self):
if self.movement_possible:
key_input = pygame.key.get_pressed()
if key_input[pygame.K_LEFT]:
self.x -= 16
if key_input[pygame.K_RIGHT]:
self.x += 16
if not self.reachbottomflag:
if key_input[pygame.K_DOWN]:
self.y += 16
def reachbottom(self):
if self.y >= 560:
self.reachbottomflag = True
def no_movement_possible(self):
self.movement_possible = False
def assign_shape():
global myshape
global stop_movement
myshape = sl.create_another_instance()
pygame.time.set_timer(myshape.move_event, myshape.one_sec_timer)
stop_movement = pygame.time.set_timer(myshape.reached_bottom_event, myshape.half_sec_timer)
def blit_used_shapes():
global screen
global blit_count
blit_count = len(stored_shapes_with_coords)
local_count = 0
while local_count < blit_count:
screen.blit(stored_shapes_with_coords[local_count][0], (stored_shapes_with_coords[local_count][1], stored_shapes_with_coords[local_count][2]))
local_count += 1
sl = shapemanager()
##### HERE IS THE PIXEL DETECTION #####
result = pygame.sprite.spritecollide(myshape, stored_shapes, False, pygame.sprite.collide_mask)
## Main loop ##
assign_shape()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT: sys.exit()
screen.blit(background_image, (0, 0))
screen.blit(myshape.image, (myshape.x, myshape.y))
myshape.move_shape()
key_input = pygame.key.get_pressed()
if key_input[pygame.K_SPACE]:
myshape.rotate_shape()
myshape.reachbottom()
if myshape.reachbottomflag:
if event.type == myshape.reached_bottom_event:
myshape.no_movement_possible()
stored_shape_tuple = [myshape.image, myshape.x, myshape.y]
stored_shapes_with_coords.append(stored_shape_tuple)
stored_shapes.add(myshape)
extra_blit_required = True
assign_shape()
####### PIXEL DETECTION IS HERE IN FOR LOOP ####
if result:
print("this should only execute when two shapes touch!!")
if extra_blit_required:
blit_used_shapes()
pygame.display.update()
问题是您没有更新精灵 rect
属性。精灵 rect
都具有位置 (0, 0)(因为您没有在对 self.image.get_rect()
的调用中设置它)因此蒙版将全部重叠和碰撞。
如果您阅读了它使用的 pygame.sprite.collide_mask you will note that it says that your sprites need to have mask
and rect
attributes. You have a rect
in your sprite and you set it in the __init__()
, but you do not keep it updated when you move the sprite. You just change the x
and y
attributes without adjusting the rect
position. The reason that the collide_mask
wants a rect
is that it uses that to determine the offset
parameter for the pygame.mask.Mask.overlap() 调用的文档。重要的是要意识到面具本身没有位置,他们需要 rect
s 来确定面具的相对位置。
这类似于 images/surfaces 没有位置并需要 rect
来为他们跟踪。
在一个单独的问题上,您将 sprite 传送到屏幕的方式毫无意义。您没有使用 sprite 组的能力进行绘制,更糟糕的是,您将 sprite 的图像、x 和 y 保存在单独的列表中,而不将其包含在 sprite 本身中。您应该查看一些基于 pygame 精灵的代码示例。那里有很多例子。
我正在使用 pygame 创建俄罗斯方块。我想使用碰撞检测,以便当游戏中的形状与之前播放的任何其他形状接触时,我可以按照俄罗斯方块的逻辑停止该形状。我遇到了使用蒙版的像素完美碰撞。我已经在线学习了一些教程,但是每次出现新形状时像素检测 returns 为真,而不是任何形状发生碰撞时。对于长代码提前抱歉,它是代码实际并且仍然包含它的游戏元素的最低限度。我认为我的方法有问题导致了这个错误。我基本上有一个功能,每当游戏中的形状与 'floor' 接触时,该形状就会保持在该位置并创建一个新形状。我认为我把它复杂化了,反过来又造成了这个错误。提前致谢
import pygame
import sys
import shapelogic
pygame.init()
screensize = width, height = 800, 595
screen = pygame.display.set_mode(screensize)
background_image =pygame.image.load("/Users/marceason/PycharmProjects/Tetris/Wooden_background.jpg").convert_alpha()
myshape = 0
stop_movement = 0
blit_count = 0
stored_shapes = pygame.sprite.Group()
stored_shapes_with_coords = []
extra_blit_required = False
index = 0
count = 0
listofshapes = []
class shapemanager():
def __init__(self):
self.listofshapes = []
def create_another_instance(self):
global count
count += 1
string = "Shape_{0},".format(count)
another_shape = Shape(string)
self.listofshapes.append(another_shape)
global index
object = self.listofshapes[index]
index += 1
return object
def load_shape(self):
shape = self.create_another_instance()
shape.load_shapes()
class Shape(pygame.sprite.Sprite):
def __init__(self, name):
pygame.sprite.Sprite.__init__(self)
self.name = name
self.x = 50
self.y = 100
self.move_event = pygame.USEREVENT + 1
self.reached_bottom_event = pygame.USEREVENT + 2
self.one_sec_timer = 1000
self.half_sec_timer = 500
self.reachbottomflag = False
self.movement_possible = True
self.image = pygame.image.load(
"/Users/marceason/PycharmProjects/Tetris/Tetris_Shapes/Green_Shape_1_Position_1.png")
self.mask = pygame.mask.from_surface(self.image)
self.rect = self.image.get_rect()
def move_shape(self):
if self.movement_possible:
key_input = pygame.key.get_pressed()
if key_input[pygame.K_LEFT]:
self.x -= 16
if key_input[pygame.K_RIGHT]:
self.x += 16
if not self.reachbottomflag:
if key_input[pygame.K_DOWN]:
self.y += 16
def reachbottom(self):
if self.y >= 560:
self.reachbottomflag = True
def no_movement_possible(self):
self.movement_possible = False
def assign_shape():
global myshape
global stop_movement
myshape = sl.create_another_instance()
pygame.time.set_timer(myshape.move_event, myshape.one_sec_timer)
stop_movement = pygame.time.set_timer(myshape.reached_bottom_event, myshape.half_sec_timer)
def blit_used_shapes():
global screen
global blit_count
blit_count = len(stored_shapes_with_coords)
local_count = 0
while local_count < blit_count:
screen.blit(stored_shapes_with_coords[local_count][0], (stored_shapes_with_coords[local_count][1], stored_shapes_with_coords[local_count][2]))
local_count += 1
sl = shapemanager()
##### HERE IS THE PIXEL DETECTION #####
result = pygame.sprite.spritecollide(myshape, stored_shapes, False, pygame.sprite.collide_mask)
## Main loop ##
assign_shape()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT: sys.exit()
screen.blit(background_image, (0, 0))
screen.blit(myshape.image, (myshape.x, myshape.y))
myshape.move_shape()
key_input = pygame.key.get_pressed()
if key_input[pygame.K_SPACE]:
myshape.rotate_shape()
myshape.reachbottom()
if myshape.reachbottomflag:
if event.type == myshape.reached_bottom_event:
myshape.no_movement_possible()
stored_shape_tuple = [myshape.image, myshape.x, myshape.y]
stored_shapes_with_coords.append(stored_shape_tuple)
stored_shapes.add(myshape)
extra_blit_required = True
assign_shape()
####### PIXEL DETECTION IS HERE IN FOR LOOP ####
if result:
print("this should only execute when two shapes touch!!")
if extra_blit_required:
blit_used_shapes()
pygame.display.update()
问题是您没有更新精灵 rect
属性。精灵 rect
都具有位置 (0, 0)(因为您没有在对 self.image.get_rect()
的调用中设置它)因此蒙版将全部重叠和碰撞。
如果您阅读了它使用的 pygame.sprite.collide_mask you will note that it says that your sprites need to have mask
and rect
attributes. You have a rect
in your sprite and you set it in the __init__()
, but you do not keep it updated when you move the sprite. You just change the x
and y
attributes without adjusting the rect
position. The reason that the collide_mask
wants a rect
is that it uses that to determine the offset
parameter for the pygame.mask.Mask.overlap() 调用的文档。重要的是要意识到面具本身没有位置,他们需要 rect
s 来确定面具的相对位置。
这类似于 images/surfaces 没有位置并需要 rect
来为他们跟踪。
在一个单独的问题上,您将 sprite 传送到屏幕的方式毫无意义。您没有使用 sprite 组的能力进行绘制,更糟糕的是,您将 sprite 的图像、x 和 y 保存在单独的列表中,而不将其包含在 sprite 本身中。您应该查看一些基于 pygame 精灵的代码示例。那里有很多例子。