首页 > 解决方案 > Python pygame sprites 碰撞检测。如何定义哪个精灵与组碰撞并通过减少一个点来影响它的属性

问题描述

我想做的是创建一个打砖块游戏,其中每个砖块有 3 点强度,然后它们就会死亡。问题在于,不仅仅是被击中的特定砖块失去积分,整个brick_sprite 组正在失去积分。并且当一个人假设死亡时,列表中的所有之前的人都会死亡。我认为问题在于考虑到第 240 行的更新,它会循环。def collision(self):请在BrickClass下方查看第 65 行。问题出在某个地方。

"""This is a simple version of arkanoid game"""

import sys
import pygame
import random


# Set colors R G B
white = (255, 255, 255)
black = (0, 0, 0)
orange = (255, 100, 10)
light_blue = (0, 144, 255)
shadow = (192, 192, 192)
purple = (152, 0, 152)

# Display
display_height = 999
display_width = 444
pygame.display.set_caption = ("Arkanoid 1.0")
FPS = 60

# Movement speed
speed = display_width // 60

# Movements
left = (-speed, 0)
right = (speed, 0)
up = (0, speed)
diagonal_left = [-speed, -speed]
diagonal_right = [speed, -speed]

# Game objects dimentions
base_dimentions = (display_width // 5, display_height // 100)
[brick_width, brick_height] = [display_width // 20 * 2, display_height // 100]
brick_dimentions = [brick_width, brick_height] 
ball_dimentions = (display_height // 100, display_height // 100)

# Initializing text font
pygame.font.init()
txt_font = pygame.font.SysFont("Score: ", display_height//44)

# Initializing sprite lists
all_sprites = pygame.sprite.Group()
brick_sprites = pygame.sprite.Group()


class Brick(pygame.sprite.Sprite):

    def __init__(self, point_value, center):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface(brick_dimentions)
        self.image.fill(purple)
        self.rect = self.image.get_rect()
        self.rect.center = center
        self.point_value = point_value

    def update(self):
        self.collision()

    def collision1(self): #This works no issue.
        # If brick is hit, loses a point
        collision = pygame.sprite.spritecollide(ball, brick_sprites, True)
        return collision

    def collision(self): #Here is the issue.
        # If brick is hit, loses a point
        collision = pygame.sprite.spritecollide(ball, brick_sprites, False)
        if collision:
            self.point_value -= 1
            if self.point_value == 0:
                self.kill() ## BUGGISH ##"""

class Ball(pygame.sprite.Sprite):
    """Initiates a moving ball and its' attributes"""

    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface(ball_dimentions)
        self.image.fill(light_blue)
        self.rect = self.image.get_rect()
        self.rect.center = self.init_position()
        self.direction = random.choice([diagonal_left, diagonal_right])
        self.score = 0

    def update(self):
        self.movement()

    def init_position(self):
        # Initialize position of the ball
        init_position = (board.rect.center[0],
                         (board.rect.center[1] - (base_dimentions[1] / 2)
                          - (ball_dimentions[1] / 2)))
        return init_position

    def collision(self):
        # If hit bricks
        collision = pygame.sprite.spritecollideany(ball, brick_sprites)
        if collision:
            self.direction[1] *= -1
            self.score += 1
    enter code here
    def movement(self):
        self.containment()
        self.rect[1] += self.direction[1]
        self.rect[0] += self.direction[0]
        self.deflect()
        self.ball_loss()
        self.collision()

    def containment(self):
        if self.rect.right >= display_width or self.rect.left <= 0:
            self.direction[0] *= -1
        if self.rect.top <= 0:
            self.direction[1] *= -1

    def ball_loss(self):
        if self.rect.bottom >= display_height:
            self.reset()
            bricks_reset()

    def reset(self):
        self.rect.center = self.init_position()
        self.direction[1] *= -1
        self.score = 0

    def deflect(self):
        # If hit base_board, deflect
        if (self.rect.bottom == board.rect.top and
            (board.rect.left <= self.rect.left <= board.rect.right or
             board.rect.left <= self.rect.right <= board.rect.right)):
            self.direction[1] *= -1
            self.board_ball_interaction()

    def board_ball_interaction(self):
        # When board is moving, effects balls direction/speed
        keystate = pygame.key.get_pressed()
        if keystate[pygame.K_LEFT] and board.rect.left > 0:
            self.direction[0] -= speed // 2
        elif keystate[pygame.K_RIGHT] and board.rect.right < display_width:
            self.direction[0] += speed // 2


class Base_board(pygame.sprite.Sprite):
    """Initiates base_board class and it's attributes"""

    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface(base_dimentions)
        self.image.fill(orange)
        self.rect = self.image.get_rect()
        self.rect.center = (display_width // 2,
                            display_height - 2 * base_dimentions[1])
        self.x_direction = 0

    def update(self):
        # Up-dates classes' position according to user's imput
        self.x_direction = 0
        self.movement()
        self.rect.x += self.x_direction

    def movement(self):
        # Creates movement and constrains object within screen dimentions
        keystate = pygame.key.get_pressed()
        if keystate[pygame.K_LEFT]:
            if not self.rect.left <= 0:
                self.x_direction = -speed
        elif keystate[pygame.K_RIGHT]:
            if not self.rect.right >= display_width:
                self.x_direction = speed

    def shoot(self):
        pass

    def enlogate(self):
        pass


def control():

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()


# and adding all sprites on lists
board = Base_board()
ball = Ball()

all_sprites.add(board)
all_sprites.add(ball)


def bricks_list_creator():
    # Creates and adds bricks into a list
    i = 9
    point_value = 2 ####
    coordinates = [display_width // 20 + brick_width / 6, display_height // 20]
    while i > 0:
        brick = Brick(point_value, (coordinates)) ####
        coordinates[0] += brick_width * 1.1
        brick_sprites.add(brick)
        i -= 1
    return brick_sprites


def bricks_reset():
    # Reset brick list
    brick_sprites.empty()
    bricks_list_creator()
    return brick_sprites


def render_text(screen):
    text = txt_font.render("Score: {0}".format(ball.score), 1, (0, 0, 0))
    return screen.blit(text, (5, 10))


def render_main(screen):
    all_sprites.draw(screen)
    brick_sprites.draw(screen)
    render_text(screen)


# Game main
def main():
    pygame.init()

    clock = pygame.time.Clock()
    screen = pygame.display.set_mode((display_width, display_height))
    bricks_list_creator()

    while True:

        # Events
        clock.tick(FPS)
        control()

        # Update
        brick_sprites.update()
        all_sprites.update()

        # Render
        screen.fill(shadow)
        render_main(screen)
        pygame.display.flip()
        pygame.display.update()


main()

标签: pythonpygamesprite

解决方案


我认为问题出在update()您的Brick班级中,称碰撞。

sprite update 函数通常用于更改 sprite 的位置或外观,每帧调用一次。所以这不是检查碰撞的好地方。

ABrick只需要知道它point_value,它不会移动(AFAIK)。

class Brick(pygame.sprite.Sprite):

    def __init__(self, point_value, center):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface(brick_dimentions)
        self.image.fill(purple)
        self.rect = self.image.get_rect()
        self.rect.center = center
        self.point_value = point_value

    def takeHit( self, ball_sprite ): 
        # the ball has collided with *this* brick
        self.point_value -= 1
        if self.point_value == 0:
            self.kill() 

然后Ball.collision()使用pygame.sprite.spritecollide()来获取球碰撞的砖块列表,并降低它们的生命值:

class Ball:
    # [...]

    def collision(self):
        # calculate the list of bricks hit
        hit_list = pygame.sprite.spritecollide( self, brick_sprites, False )
        for brick in hit_list:
            brick.takeHit()     # may (or may not) kill the brick

大多数情况下,这hit_list将是一块砖,但根据球的大小,有时可能是两块砖。


推荐阅读