【问题标题】:Pygame pixel perfect collision not working as expectedPygame像素完美碰撞没有按预期工作
【发布时间】:2020-08-04 21:31:56
【问题描述】:

我正在使用 pygame 创建俄罗斯方块。我想使用碰撞检测,以便当游戏中的形状与任何其他先前播放的形状接触时,我可以按照俄罗斯方块的逻辑停止该形状。我遇到了使用蒙版的像素完美碰撞。我已经在线学习了一些教程,但是每次新形状发挥作用时,像素检测都会返回真,而不是在任何形状发生碰撞时。提前为长代码抱歉,它是代码实际上并且仍然包含它的游戏元素的最低限度。我认为我的方法有问题导致此错误。我基本上有一个功能,每当游戏中的形状与“地板”接触时,形状就会保持在那个位置并创建一个新的形状。我认为我把它复杂化了,反过来又造成了这个错误。提前致谢

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()

【问题讨论】:

  • 为什么要对正方形使用完美碰撞?这只是浪费时间。
  • 您应该使用print() 来检查变量中的值——它被称为print debuging。或者学习如何使用真正的调试器。也许它会检查与您期望的不同元素的碰撞,这总是给出True。有时人们使用.x.y 来移动对象但他们忘记了pygame 使用.rect 来检查碰撞,他们忘记了更改.rect.x.rect.y。您应该只使用.rect.x.rect.y 来移动和绘制它blit(shape.image, shape.rect)
  • 顺便说一句:你可以检查self.rect.bottom &gt;= 595(寡妇的身高)而不是检查self.x &gt;= 560
  • 顺便说一句:如果你使用类而不是全局 indexcount 你可以使用 self.indexself.count
  • 您的代码有错误的缩进,所以它没有用 - 我们无法运行它。始终使用正确缩进的代码并使用按钮 {} 或快捷方式 Ctrl+K 来格式化它。

标签: python pygame collision-detection tetris pixel-perfect


【解决方案1】:

问题是您没有更新精灵rect 属性。精灵rects 都有位置 (0, 0)(因为您没有在对self.image.get_rect() 的调用中设置它),因此蒙版将全部重叠和碰撞。

如果您阅读pygame.sprite.collide_mask 的文档,您会注意到它说您的精灵需要具有maskrect 属性。您的 sprite 中有一个 rect,并且您在 __init__() 中设置了它,但是当您移动 sprite 时您不会保持更新。您只需更改xy 属性而不调整rect 位置。 collide_mask 想要 rect 的原因是它使用它来确定它使用的 pygame.mask.Mask.overlap() 调用的 offset 参数。重要的是要意识到面具本身没有位置,他们需要rects来确定面具的相对位置。

这类似于没有位置并且需要rect 来为它们跟踪的图像/表面。

在一个单独的问题上,将精灵 blit' 到屏幕上的方式是没有意义的。您没有使用精灵组的能力进行绘制,更糟糕的是,您将精灵的图像、x 和 y 保存在单独的列表中,而不是将其包含在精灵本身中。你应该去看一些基于 pygame sprite 的代码示例。那里有很多例子。

【讨论】:

  • 感谢您提供详细的回答,您帮助我进一步了解了掩码。我已经修改了代码以更新 rects 并更改了图像显示在屏幕上的方式(使用 for 循环通过精灵组)。有趣的是现在碰撞永远不会激活.....
  • @Pyson rect 也用于简化碰撞。进行基于掩码的碰撞是一项相对昂贵的操作,因此首先检查矩形是否发生碰撞。如果矩形没有碰撞,那么甚至不需要检查掩码。
猜你喜欢
  • 2012-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多