Pygame mask collision 仅在第一次碰撞时造成伤害

Pygame mask collision only putting damage on base on first collision

我正在写一个简单的入侵者游戏。为了增加底座的损坏,我想我可以在子弹撞击时在底座上涂上一个小的黑色表面,然后使用面罩检查子弹是否在损坏处或底座上,但它不起作用,我觉得我我误解了面具。检测到第一次碰撞,但之后它也检测到碰撞但不会对底座造成更多损坏。我想因为表面是黑色的所以基础面具不会包含它,但它不起作用。这是一个简短的测试来演示这个。按 space(或任意键)向基地发射子弹。我想也许我应该为基地生成一个新的掩码,但这不起作用。遮罩碰撞来自 github.

上的 pygame 精灵代码
import sys, pygame, random
from pygame.locals import *

screenwidth = 600
screenheight = 400

pygame.init()
screen = pygame.display.set_mode((screenwidth, screenheight))
pygame.display.set_caption("shoot 'em up")

screenrect = screen.get_rect()

black = (0, 0, 0)

blue = (10, 10, 255)
yellow = (238, 238, 0)

base_width = 80
base_height = 40

bullet_width = 3
bullet_height = 10

class Bullet(pygame.Surface):
    
    def __init__(self, point):
        super().__init__((bullet_width, bullet_height), pygame.SRCALPHA)
        self.rect = self.get_rect()
        self.rect.midbottom = point
        self.fill(yellow)
        self.velocity = -5
        self.alive = True
        self.mask = pygame.mask.from_surface(self)
            
    def update(self):
        
        self.rect.top += self.velocity
        
    def draw(self, surf):
        surf.blit(self, self.rect)

class Base(pygame.Surface):

    def __init__(self, x, y, colour):
        super().__init__((base_width, base_height), pygame.SRCALPHA)
        self.rect = self.get_rect()
        self.rect.x = x
        self.rect.y = y 
        self.fill(colour)
        self.alive = True
        
    def add_damage(self, bullet):
        width = random.randint(3, 6)
        height = random.randint(8, 12)
        damage = pygame.Surface((width, height), pygame.SRCALPHA)
        damage.fill(black)
        rect = damage.get_rect()
        rect.x = bullet.rect.x - self.rect.x
        rect.y = bullet.rect.top - self.rect.top 
        self.blit(damage, rect)
        #self.mask = pygame.mask.from_surface(self) 
    
    def draw(self, surf):
        surf.blit(self, self.rect)
        
class Test(pygame.Surface):

    def __init__(self):
        super().__init__((600, 400))

        self. base = Base(50, 300, blue)
        self.bullets = []
        
    def run(self):
    
        while 1:
            self.get_events()
            self.update()
            self.draw()
                
    def get_events(self):

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            
            if event.type == pygame.KEYDOWN:
                bullet = Bullet((60, 380))
                self.bullets.append(bullet)
                    
    def update(self):
        if self.bullets:
            for bullet in self.bullets:
                bullet.update()
                self.collision_check(bullet)
            
            for bullet in self.bullets:
                if not bullet.alive:
                    self.bullets.remove(bullet)
                    
    def collision_check(self, bullet):
        if bullet.rect.colliderect(self.base):
            if self.collide_mask(bullet, self.base):
                print("collide")
                self.base.add_damage(bullet)
                bullet.alive = False
                
    def collide_mask(self, left, right):

        xoffset = right.rect[0] - left.rect[0]
        yoffset = right.rect[1] - left.rect[1]

        try:
            leftmask = left.mask
        except AttributeError:
            leftmask = pygame.mask.from_surface(left)
        try:
            rightmask = right.mask
        except AttributeError:
            rightmask = pygame.mask.from_surface(right)
    
        return leftmask.overlap(rightmask, (xoffset, yoffset))

    def draw(self):
        self.fill(black)
        self.base.draw(self)
        
        for bullet in self.bullets:
            bullet.draw(self)
            
        screen.blit(self, (0,0))
        
        pygame.display.flip()
        
if __name__=="__main__":
    t = Test()
    t.run()

如您所见,这没有使用 pygame 精灵。

如果 pygame.Surface object is changed you need to recreate the mask with pygame.mask.from_surface. However, the mask is generated form the Surface's alpha channel. Therefore, you need to make the damaged area transparent. Create a completely transparent rectangle (RGBA = 0, 0, 0, 0) and blit 矩形使用特殊标志 BLEND_RGBA_MULT(或 BLEND_RGBA_MIN)。最后重新创建蒙版:

damage = pygame.Surface((width, height), pygame.SRCALPHA)
self.blit(damage, rect, special_flags=pygame.BLEND_RGBA_MULT)
self.mask = pygame.mask.from_surface(self) 

add_damage方法:

class Base(pygame.Surface):
    # [...]

    def add_damage(self, bullet):
        width = random.randint(3, 6)
        height = random.randint(8, 12)
        damage = pygame.Surface((width, height), pygame.SRCALPHA)
        rect = damage.get_rect()
        rect.x = bullet.rect.x - self.rect.x
        rect.y = bullet.rect.top - self.rect.top 
        self.blit(damage, rect, special_flags=pygame.BLEND_RGBA_MULT)
        self.mask = pygame.mask.from_surface(self)