Image does not remain on screen after executing for loop in pygame

北战南征 提交于 2021-01-28 05:26:49

问题


I have been trying to modify the code from this Tutorial so that after a bullet strikes an enemy the player.png image is shown at position x = 60 and y = 48. But the image does not remain fixed, it just appears and disappears. I don't know exactly where the wrong or missing element is in the code but I suspect that one of the causes is some misuse of my part of the for loop within the draw_reaction function.

My player.png image

My MWE code:

import pygame
import random

# Define some colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
BLUE = (0, 0, 255)

# --- Classes


class Block(pygame.sprite.Sprite):
    """ This class represents the block. """
    def __init__(self, color):
        # Call the parent class (Sprite) constructor
        super().__init__()

        self.image = pygame.Surface([20, 15])
        self.image.fill(color)

        self.rect = self.image.get_rect()


class Player(pygame.sprite.Sprite):
    """ This class represents the Player. """

    def __init__(self):
        """ Set up the player on creation. """
        # Call the parent class (Sprite) constructor
        super().__init__()

        self.image = pygame.Surface([20, 20])
        self.image.fill(RED)

        self.rect = self.image.get_rect()

    def update(self):
        """ Update the player's position. """
        # Get the current mouse position. This returns the position
        # as a list of two numbers.
        pos = pygame.mouse.get_pos()

        # Set the player x position to the mouse x position
        self.rect.x = pos[0]


class Bullet(pygame.sprite.Sprite):
    """ This class represents the bullet . """
    def __init__(self):
        # Call the parent class (Sprite) constructor
        super().__init__()

        self.image = pygame.Surface([4, 10])
        self.image.fill(BLACK)

        self.rect = self.image.get_rect()

    def update(self):
        """ Move the bullet. """
        self.rect.y -= 3


# --- Create the window

# Initialize Pygame
pygame.init()

# Set the height and width of the screen
screen_width = 700
screen_height = 400
screen = pygame.display.set_mode([screen_width, screen_height])

# --- Sprite lists

# This is a list of every sprite. All blocks and the player block as well.
all_sprites_list = pygame.sprite.Group()

# List of each block in the game
block_list = pygame.sprite.Group()

# List of each bullet
bullet_list = pygame.sprite.Group()

# --- Create the sprites

for i in range(0,1,2):
    # This represents a block
    block = Block(BLUE)

    # Set a random location for the block
    block.rect.x = random.randrange(screen_width)
    block.rect.y = random.randrange(350)

    # Add the block to the list of objects
    block_list.add(block)
    all_sprites_list.add(block)

# Create a red player block
player = Player()
all_sprites_list.add(player)
player_image = pygame.image.load("player.png").convert() 
# Loop until the user clicks the close button.
done = False

# Used to manage how fast the screen updates
clock = pygame.time.Clock()

score = 0
player.rect.y = 370

# -------- Main Program Loop -----------
while not done:
    # --- Event Processing
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True

        elif event.type == pygame.MOUSEBUTTONDOWN:
            # Fire a bullet if the user clicks the mouse button
            bullet = Bullet()
            # Set the bullet so it is where the player is
            bullet.rect.x = player.rect.x
            bullet.rect.y = player.rect.y
            # Add the bullet to the lists
            all_sprites_list.add(bullet)
            bullet_list.add(bullet)

    # --- Game logic

    # Call the update() method on all the sprites


    # Calculate mechanics for each bullet
    for bullet in bullet_list:

        # See if it hit a block
        block_hit_list = pygame.sprite.spritecollide(bullet, block_list, True)

        # For each block hit, remove the bullet and add to the score
        for block in block_hit_list:
            bullet_list.remove(bullet)
            all_sprites_list.remove(bullet)
            score += 1
            print(score)

        # Remove the bullet if it flies up off the screen
        if bullet.rect.y < -10:
            bullet_list.remove(bullet)
            all_sprites_list.remove(bullet)
    def draw_reaction():
        for bullet in bullet_list:
            block_hit_list = pygame.sprite.spritecollide(bullet, block_list, True)
            for block in block_hit_list:
                screen.blit(player_image, [60, 48]) 
    all_sprites_list.update() 
    # --- Draw a frame

    # Clear the screen
    screen.fill(WHITE)
    draw_reaction()
    # Draw all the spites
    all_sprites_list.draw(screen)

    # Go ahead and update the screen with what we've drawn.
    pygame.display.flip()

    # --- Limit to 20 frames per second
    clock.tick(60)

pygame.quit()

EDIT UPDATE: The game has only one enemy that appears in a random place each time the game starts again.

EDIT UPDATE 2: I know that in most games after a collision the enemy is removed from the screen. This also happens in my code. But for a particular need of mine I need that, as an consequence, an image (player.png) remains fixed constantly after this bullet collision with the enemy.


回答1:


The problem is caused by the draw_reaction() function only drawing the player_image during the absolute-time the bullet is colliding with a block. So it shows the image for a single frame (One 60th of a second), but then on the next loop the collision is no longer occurring (the bullet is removed), so it is never drawn.

There are a number of ways around this, but I'm not sure of the purpose of showing this bitmap, so maybe they're not as helpful as they should be.

The easiest fix is to probably turn the player image into a sprite, and simply add it to the existing all_sprites_list when the bullet-hit triggers, and then take it away some time later (or how ever it should work).

class PlayerShip( pygame.sprite.Sprite ):
    def __init__(self):
        super().__init__()
        self.image = pygame.image.load( "player.png" ) #.convert() 
        self.rect  = self.image.get_rect()
        self.rect.topleft = ( 60, 48 ) 

Then later on:

player_sprite = PlayerShip()

and

def draw_reaction():
    for bullet in bullet_list:
        block_hit_list = pygame.sprite.spritecollide(bullet, block_list, True)
        for block in block_hit_list:
            # TODO: ensure it's not showing already
            all_sprites_list.add( player_sprite )      # add the player sprite 
            break                                      # only add once

EDIT: Code snippets updated based on comment



来源:https://stackoverflow.com/questions/60067478/image-does-not-remain-on-screen-after-executing-for-loop-in-pygame

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!