问题
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