在主屏幕以外的表面上进行 blitting 时奇怪的碰撞检测

weird collision detection when blitting on a surface other than the main screen

当我用鼠标点击发射导弹时它与其中一个方块发生碰撞,我希望移除方块和导弹。 但是,我想把形状放在透明的表面上以便于移动它,这就是问题所在,因为块没有被移除并且检测到碰撞很晚。

from math import *
import sys
import pygame
from pygame.locals import *

pygame.init()

fps = 60
fpsClock = pygame.time.Clock()
ADD_MISSILE = pygame.USEREVENT + 1
width, height = 1244, 740
screen = pygame.display.set_mode((width, height))


shape = [
'              **************              ',
'          ******          ******          ',
'        ****                  ****        ',
'      **                          **      ',
'    **                              **    ',
'  ****                              ****  ',
'  **                                  **  ',
'****                                  ****',
'**                                      **',
'**                                      **',
'**                                      **',
'**                                      **',
'**                                      **',
'****                                  ****',
'  **                                  **  ',
'  ****                              ****  ',
'    **                              **    ',
'      **                          **      ',
'        ****                  ****        ',
'          ******          ******          ',
'              **************              ',
]

BLOCKSIZE = 8


class Block(pygame.sprite.Sprite):
    def __init__(self, size, color, x, y):
        super().__init__()
        self.image = pygame.Surface((size, size))
        self.image.fill(color)
        self.rect = self.image.get_rect(topleft=(x, y))

class Missile(pygame.sprite.Sprite):
    def __init__(self, pos):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface((6, 2))
        self.image.fill('red')
        self.rect = self.image.get_rect(center=(pos))
        self.vx = 4

    def update(self):
        self.rect.x -= self.vx

def setup_blocks():
    for row_index, row in enumerate(shape):
        for col_index, col in enumerate(row):
            if col == '*':
                x = BLOCKSIZE*col_index
                y = BLOCKSIZE*row_index
                blocks_sprite.add(Block(BLOCKSIZE,'red',x, y))

def collisions():
    if pygame.sprite.groupcollide(blocks_sprite,missiles_sprite,True,True):
        print("collision")
            

blocks_sprite = pygame.sprite.Group()
missiles_sprite = pygame.sprite.Group()

setup_blocks()
width = len(shape[0])
height = len(shape)
surf = pygame.Surface((width*BLOCKSIZE, height*BLOCKSIZE),SRCALPHA)
surf_rect = surf.get_rect()
#surf.fill('green')

while True:
    screen.fill('white')
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
        if event.type == MOUSEBUTTONDOWN:
            missiles_sprite.add(Missile(pygame.mouse.get_pos()))

    keys = pygame.key.get_pressed()
    if keys[pygame.K_RIGHT]:
        surf_rect.x += 5
    
    missiles_sprite.update()
    missiles_sprite.draw(screen)

    for block in blocks_sprite.sprites():
        surf.blit(block.image,block.rect)
        #screen.blit(block.image,block.rect)
    screen.blit(surf,surf_rect)
    collisions()
    pygame.display.update()
    fpsClock.tick(fps)

实际上块被删除了。但是,您必须清除绘制方块的 Surface:

while True:
   # [...]

    screen.fill('white')
    missiles_sprite.draw(screen)

    surf.fill((0, 0, 0, 0))                # <-- clear "surf"
    for block in blocks_sprite.sprites():
        surf.blit(block.image,block.rect)
    screen.blit(surf,surf_rect)
    
    pygame.display.update()
    fpsClock.tick(fps)

无论如何,在单独的 Surface 上绘制块是个坏主意,因为这会破坏碰撞检测。在这种情况下,块的 rect 属性是相对于 surf 的。但是,pygame.sprite.groupcollide 仅在 rect 属性与屏幕相关时才有效。我建议改为移动积木。这是实现您自己的碰撞检测所需的更少代码:

while True:
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
        if event.type == MOUSEBUTTONDOWN:
            missiles_sprite.add(Missile(pygame.mouse.get_pos()))

    keys = pygame.key.get_pressed()
    if keys[pygame.K_RIGHT]:
        for block in blocks_sprite.sprites():
            block.rect.x += 5
    
    missiles_sprite.update()
    collisions()

    screen.fill('white')
    blocks_sprite.draw(screen)
    missiles_sprite.draw(screen)
    pygame.display.update()
    fpsClock.tick(fps)

或者你自己的碰撞控制可以是这样的:

def collisions(surf_rect):
    for block in blocks_sprite:
        blokc_rect = block.rect.move(surf_rect.x, surf_rect.y)
        for missel in missiles_sprite:
            if blokc_rect.colliderect(missel.rect):
                block.kill()
                missel.kill()

blocks_sprite = pygame.sprite.Group()
missiles_sprite = pygame.sprite.Group()

setup_blocks()
width = len(shape[0])
height = len(shape)
surf = pygame.Surface((width*BLOCKSIZE, height*BLOCKSIZE),SRCALPHA)
surf_rect = surf.get_rect()

while True:
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
        if event.type == MOUSEBUTTONDOWN:
            missiles_sprite.add(Missile(pygame.mouse.get_pos()))

    keys = pygame.key.get_pressed()
    if keys[pygame.K_RIGHT]:
        surf_rect.x += 5
    
    missiles_sprite.update()
    collisions(surf_rect)     

    screen.fill('white')
    missiles_sprite.draw(screen)

    print(len(blocks_sprite.sprites()))
    surf.fill((0, 0, 0, 0))   
    for block in blocks_sprite.sprites():
        surf.blit(block.image,block.rect)
    screen.blit(surf,surf_rect)
    
    pygame.display.update()
    fpsClock.tick(fps)