Pygame:子弹粘在屏幕上

时间:2018-06-24 17:25:19

标签: python pygame sprite

我的问题很简单。如果我快速射击,我发射的子弹会粘在屏幕上。如果我拍摄缓慢,它们不会粘住。有人知道这种现象如何发生吗?

子弹贴在屏幕上的屏幕截图

screenshot of the bullets sticking to the screen

下面,我输入了密码。我遵循以下默认游戏流程图:

Game flowchart

我对问题的根源感到好奇。是代码还是硬件?

import sys
import pygame
from pygame.sprite import Sprite
from pygame.sprite import Group

# pygame initializing
pygame.init()

#create the screen surface
screen =  pygame.display.set_mode((800, 700))

class Color():
    def __init__(self):
        self.black = (0, 0, 0)
        self.white = (255, 255, 255)
        self.red = (255, 0, 0)
        self.green = (0, 255, 0)
        self.green_lambda = (10, 255, 150)
        self.blue = (0, 0, 255)

# set up the colors
color = Color() # make an instance of this class - this makes some colors available

class Spaceship(Sprite):
    """
    This class represents the Spaceship.
    It derives from the "Sprite" class in Pygame.
    """
    def __init__(self):
        """ Constructor"""
        # Call the parent class (Sprite) constructor
        super().__init__()

        width = 22
        height = 32
        self.screen = screen
        self.image = pygame.Surface((width, height))
        self.image.fill(color.black)
        self.image.set_colorkey(color.black)

        pygame.draw.polygon(self.image, color.green_lambda, [[10,0],[15,22],[20,30],[10,27],[0,30],[5,22]],2)

        self.rect = self.image.get_rect()

        self.screen_rect = self.screen.get_rect()
        self.rect.centerx = self.screen_rect.centerx
        self.rect.bottom = self.screen_rect.bottom

        # As the rect method only take integers we store a
        # This value is only used at the beginning, i.e. before the game loop starts
        self.center_x = self.rect.centerx
        self.center_y = self.rect.centery


class Bullet(Sprite):
    """
        This class represents the bullets.
        It derives from the "Sprite" class in Pygame.
    """
    def __init__(self):
        # Call the parent class (Sprite) constructor
        super().__init__()

        self.image = pygame.Surface((8,10))
        self.image.fill(color.red)
        self.image.set_colorkey((color.red))
        pygame.draw.ellipse(self.image, color.green, [1, 0, 5, 8], 2)

        self.rect = self.image.get_rect()

        self.rect.centerx = defender.rect.centerx
        self.rect.bottom = defender.rect.top

    # def function to move the bullets
    def update_pos(self):
        self.rect.y -= bullet_speed


# create spaceship instance
defender = Spaceship()

# create group to store sprites in
all_sprites_list = Group()
all_sprites_list.add(defender)

ship_speed = 0.5
bullet_speed = 3


def run_game():
    m_right = False
    m_left = False
    m_up = False
    m_down = False
    new_bullet = False

    while True:
        """This is the user interaction section"""
        for event in pygame.event.get():
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    sys.exit()
                elif event.key == pygame.K_RIGHT:
                    m_right = True
                elif event.key == pygame.K_LEFT:
                    m_left = True
                elif event.key == pygame.K_UP:
                    m_up = True
                elif event.key == pygame.K_DOWN:
                    m_down = True
                elif event.key == pygame.K_SPACE:
                    new_bullet = Bullet()
                    #print(dir(new_bullet))
                    all_sprites_list.add(new_bullet)
            if event.type == pygame.KEYUP:
                if event.key == pygame.K_RIGHT:
                    m_right = False
                elif event.key == pygame.K_LEFT:
                    m_left = False
                elif event.key == pygame.K_UP:
                    m_up = False
                elif event.key == pygame.K_DOWN:
                    m_down = False

        """Below is the game logic, which gets input from the user interaction 
        section and more"""
        # Movement of spaceship depending on the flag boolean value and on screen width and height
        if m_right and defender.rect.right < defender.screen_rect.right:
            defender.center_x += ship_speed
        if m_left and defender.rect.left > defender.screen_rect.left:
            defender.center_x -= ship_speed
        if m_up and defender.rect.top > defender.screen_rect.top:
            defender.center_y -= ship_speed
        if m_down and defender.rect.bottom < defender.screen_rect.bottom:
            defender.center_y += ship_speed

        # The cumulative value (which is a float number) for the spaceships movement
        # is given to the spaceship rect variable (which can only be integer) now.
        # This enables fine adjusting of the speed
        defender.rect.centerx = defender.center_x
        defender.rect.centery = defender.center_y
        all_sprites_list.update()
        screen.fill(color.black)
        if new_bullet:
            new_bullet.update_pos()
            # Below the bullets which leaves the screen display are deleted
            if new_bullet.rect.bottom < defender.screen_rect.top:
                all_sprites_list.remove(new_bullet)
        all_sprites_list.draw(screen)
        print(all_sprites_list)
        pygame.display.flip()

run_game()

2 个答案:

答案 0 :(得分:1)

不仅仅是更新new_bullet的位置

    # if new_bullet:
    #     new_bullet.update_pos()
    #     # Below the bullets which leaves the screen display are deleted
    #     if new_bullet.rect.bottom < defender.screen_rect.top:
    #         all_sprites_list.remove(new_bullet)

更新所有项目符号的位置

    for bullet in all_sprites_list:
        if isinstance(bullet,Bullet):
            bullet.update_pos()
            if bullet.rect.bottom < defender.screen_rect.top:
                all_sprites_list.remove(bullet)
                del bullet

答案 1 :(得分:0)

Joran Beasley的答案是正确的。我只想指出,您还可以将精灵的行为放入它们的update方法中,这些方法在您调用all_sprites_list.update()时会自动被调用。实际上,您可以将while循环中的大多数代码移至更新方法。

我有一个示例,说明了这些更改以及注释中的其他一些提示(快速代码回顾):

import pygame
from pygame.sprite import Sprite
from pygame.sprite import Group


# I'd just define some global constants for the colors.
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
GREEN_LAMBDA = (10, 255, 150)


class Spaceship(Sprite):
    """This class represents the Spaceship."""
    def __init__(self, screen):
        """Constructor"""
        super().__init__()
        self.screen = screen
        # pygame.SRCALPHA makes the surface transparent.
        self.image = pygame.Surface((22, 32), pygame.SRCALPHA)
        pygame.draw.polygon(
            self.image, GREEN_LAMBDA,
            [[10,0],[15,22],[20,30],[10,27],[0,30],[5,22]], 2
        )
        self.screen_rect = self.screen.get_rect()
        # You can pass the position as the midbottom argument to `get_rect`.
        self.rect = self.image.get_rect(midbottom=self.screen_rect.midbottom)
        self.center_x = self.rect.centerx
        self.center_y = self.rect.centery
        # I've removed the `m_right`, etc. variables and just set the speed
        # of the sprite in the event loop.
        self.max_speed = 3.5
        self.speed_x = 0
        self.speed_y = 0

    def update(self):
        # Move the sprite.
        self.center_x += self.speed_x
        self.center_y += self.speed_y
        self.rect.centerx = self.center_x
        self.rect.centery = self.center_y
        # Keep the sprite on the screen.
        if not self.screen_rect.contains(self.rect):
            self.rect.clamp_ip(self.screen_rect)
            self.center_x, self.center_y = self.rect.center


class Bullet(Sprite):
    """This class represents the bullets."""
    def __init__(self, pos):
        super().__init__()
        self.image = pygame.Surface((8, 10), pygame.SRCALPHA)
        pygame.draw.ellipse(self.image, GREEN, [1, 0, 5, 8], 2)
        self.rect = self.image.get_rect(midbottom=pos)
        self.speed = 3  # The speed is now an attribute.

    def update(self):
        self.rect.y -= self.speed

        if self.rect.top < 0:
            self.kill()  # Remove the sprite from all groups.


def run_game():
    pygame.init()
    screen = pygame.display.set_mode((800, 700))
    clock = pygame.time.Clock()  # Use a clock to limit the frame rate.

    defender = Spaceship(screen)
    all_sprites = Group()  # Changed the name because groups are not lists.
    all_sprites.add(defender)

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                return
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    return
                elif event.key == pygame.K_RIGHT:
                    defender.speed_x = defender.max_speed
                elif event.key == pygame.K_LEFT:
                    defender.speed_x = -defender.max_speed
                elif event.key == pygame.K_UP:
                    defender.speed_y = -defender.max_speed
                elif event.key == pygame.K_DOWN:
                    defender.speed_y = defender.max_speed
                elif event.key == pygame.K_SPACE:
                    new_bullet = Bullet(defender.rect.midtop)  # Pass the pos.
                    all_sprites.add(new_bullet)
            elif event.type == pygame.KEYUP:
                if event.key == pygame.K_RIGHT and defender.speed_x > 0:
                    defender.speed_x = 0
                elif event.key == pygame.K_LEFT and defender.speed_x < 0:
                    defender.speed_x = 0
                elif event.key == pygame.K_UP and defender.speed_y < 0:
                    defender.speed_y = 0
                elif event.key == pygame.K_DOWN and defender.speed_y > 0:
                    defender.speed_y = 0

        all_sprites.update()  # Calls the update methods of all sprites.

        screen.fill(BLACK)
        all_sprites.draw(screen)
        pygame.display.flip()
        clock.tick(60)  # Limit the frame rate to 60 FPS.

run_game()