Pygame - 外星人入侵:让外星人还击

Pygame - Alien Invasion: Making aliens shoot back

我目前正在尝试让我的外星人在游戏过程中向玩家投掷 'bombs'。我目前的目标是让所有外星人都投下炸弹,然后最终让外星人在达到一定水平后随机投下炸弹。

Currently is seems that all bombs are coming from the starting location of my first alien.

alien_invasion.py

import sys, pygame

from settings import Settings
from ship import Ship
from bombs import Bomb
from alien import Alien

class AlienInvasion:
    """Overall class to manage game assets and behavior."""

    def __init__(self):
        """Initlize the game, and create game resources."""
        pygame.init()
        self.settings = Settings()
        self.screen = pygame.display.set_mode((self.settings.screen_width, 
                                            self.settings.screen_height))
        self.settings.screen_width = self.screen.get_rect().width
        self.settings.screen_height = self.screen.get_rect().height

        pygame.display.set_caption("Alien Invasion")

        self.ship = Ship(self)
        self.alien = Alien(self)

        self.bullets = pygame.sprite.Group()
        self.bombs = pygame.sprite.Group()

        self.aliens = pygame.sprite.Group()
        self._create_fleet()

        #Set our background color
        self.bg_color = (230, 230, 230)
        

    def run_game(self):
        """Start main loop for our game."""
        while True:
            self._check_events()


            self.ship.update() 

            self._drop_bombs()
            self._update_bombs()
            
            self._update_aliens()
                
            self._update_screen()

    def _check_events(self):
        """Respond to kepresses and mouse events."""
        #for each event in game capture that event
        for event in pygame.event.get():
            #if player preses close, quit game
            if event.type == pygame.QUIT:
                sys.exit()
            # if event is a key press
            elif event.type == pygame.KEYDOWN:
                self._check_keydown_events(event)
            elif event.type == pygame.KEYUP:
                self._check_keyup_events(event)

    def _check_keydown_events(self, event):
        """respond to keydown events"""
        if event.key == pygame.K_RIGHT:
            self.ship.moving_right = True
        elif event.key == pygame.K_LEFT:
            self.ship.moving_left = True
        elif event.key == pygame.K_ESCAPE:
            sys.exit()

    def _check_keyup_events(self, event):
        if event.key == pygame.K_RIGHT:
            self.ship.moving_right = False
        elif event.key == pygame.K_LEFT:
            self.ship.moving_left = False

    def _drop_bombs(self):
        """drop bombs from alien ships"""
        new_bomb = Bomb(self)
        for alien in self.aliens:
            self.bombs.add(new_bomb)

    def _update_bombs(self):
        """update bombs positions and gets rid of old bombs"""
        self.screen_rect = self.screen.get_rect()
        self.bombs.update()
        for bomb in self.bombs.copy():
            if bomb.rect.bottom >= self.screen_rect.bottom:
                self.bombs.remove(bomb)


    def _update_aliens(self):
        """update the position of the aliens"""
        self._check_fleet_edges()
        self.aliens.update()

        self._check_aliens_bottom()

    def _create_fleet(self):
        """create our fleet of aliens"""
        # creat an alien and fine the number that fits in a row
        # spacing between each alien is equal to one alien
        alien = Alien(self)
        alien_width, alien_height = alien.rect.size
        available_space_x = self.settings.screen_width - (2 * alien_width)
        number_aliens_x = available_space_x // (2 * alien_width)

        # determine the number of rows that fit on the screen
        ship_height = self.ship.rect.height
        available_space_y = (self.settings.screen_height - 
                                (5 * alien_height) - ship_height)
        number_rows = available_space_y // (2 * alien_height)

        # create a full fleet of aliens
        for row_number in range(number_rows):
            for alien_number in range(number_aliens_x):
                self._create_alien(alien_number, row_number)


    def _create_alien(self, alien_number, row_number):
        alien = Alien(self)
        alien_width, alien_height = alien.rect.size
        alien.x = alien_width + 2 * alien_width * alien_number
        alien.rect.x = alien.x
        alien.rect.y = 2 * alien_height + 2 * alien.rect.height * row_number
        self.aliens.add(alien)

    def _check_fleet_edges(self):
        """respond if any aliens reach the edge"""
        for alien in self.aliens.sprites():
            if alien.check_edges():
                self._change_fleet_direction()
                break

    def _check_aliens_bottom(self):
        """check if any aliens have reached the bottom of the screen"""
        screen_rect = self.screen.get_rect()
        for alien in self.aliens.sprites():
            if alien.rect.bottom >= screen_rect.bottom:
                self._ship_hit()
                break

    def _change_fleet_direction(self):
        """drop entire fleet and change direction"""
        for alien in self.aliens.sprites():
            alien.rect.y += self.settings.fleet_drop_speed
        self.settings.fleet_direction *= -1

    def _update_screen(self):
        """Update images on screen and flip to the new screen."""
        #fill our background with our bg_color
        self.screen.fill(self.settings.bg_color)

        #draw ship to screen
        self.ship.blitme()

        for bullet in self.bullets.sprites():
            bullet.draw_bullet()

        self.aliens.draw(self.screen)


        for bomb in self.bombs.sprites():
            bomb.draw_bomb()


        #Make the most recently drawn screen visible.
        #this clears our previous screen and updates it to a new one
        #this gives our programe smooth movemnt
        pygame.display.flip()

if __name__ == '__main__':
    #Make a game instance, and run the game
    ai = AlienInvasion()
    ai.run_game()

alien.py

import pygame
from pygame.sprite import Sprite

class Alien(Sprite):
    """A class to represent a sinlge alien in the fleet"""

    def __init__(self, ai_game):
        """initlize alien and set its starting position"""
        super().__init__()
        self.screen = ai_game.screen
        self.settings = ai_game.settings
        self.screen_rect = ai_game.screen.get_rect()

        # load alien image at set its rect
        self.image = pygame.image.load('images/alien.bmp')
        self.rect = self.image.get_rect()

        # start each new alien at the top left of the screen
        self.rect.x = self.rect.width
        self.rect.y = self.rect.height

        # store aliens exact position (decimal)
        self.x = float(self.rect.x)

    def check_edges(self):
        """return true if alien is at edge of screen"""
        screen_rect = self.screen.get_rect()
        if self.rect.right >= screen_rect.right or self.rect.left <= 0:
            return True

    def update(self):
        """move the alien to the right or left"""
        self.x += (self.settings.alien_speed *
                        self.settings.fleet_direction)
        self.rect.x = self.x

bombs.py

from pygame.sprite import Sprite

class Bomb(Sprite):
    """create a bullet that is dropped by aliens"""

    def __init__(self, ai_game):
        super().__init__()
        self.screen = ai_game.screen
        self.settings = ai_game.settings
        self.color = self.settings.bullet_color

        self.rect = pygame.Rect(0, 0, self.settings.bullet_width,
            self.settings.bullet_height)
        self.rect.midbottom = ai_game.alien.rect.midbottom

        self.y = float(self.rect.y)

    def update(self):
        """move bullet down screen"""
        self.y += self.settings.bullet_speed
        self.rect.y = self.y

    def draw_bomb(self):
        """draw bullet to screen"""
        pygame.draw.rect(self.screen, self.color, self.rect)

settings.py

class Settings:
    """A class to store our settings for Alien Invasion game."""

    def __init__(self):
        """Initlize the games settings."""
        # screen settings
        self.screen_width = 1600
        self.screen_height = 900
        self.bg_color = (230, 230, 230)

        # ship settings
        self.ship_limit = 3

        # bullet settings
        self.bullet_width = 3
        self.bullet_height = 15
        self.bullet_color = (60, 60, 60)
        self.bullets_allowed = 3

        # alien settings
        self.fleet_drop_speed = 10

        # how quickly the game speeds up
        self.speedup_scale = 1.1

        # how muct alien poits value increses
        self.score_scale = 1.5

        self.initialize_dynamic_settings()

    def initialize_dynamic_settings(self):
        """settings that change through the game"""
        self.ship_speed = 1.5
        self.bullet_speed = 1.5
        self.alien_speed = 0.5

        self.fleet_direction = 1

        # scoring
        self.alien_points = 50

    def increse_speed(self):
        """increse speed settings"""
        self.ship_speed *= self.speedup_scale
        self.bullet_speed *= self.speedup_scale
        self.alien_speed *= self.speedup_scale

        self.alien_points = int(self.alien_points * self.score_scale)

ship.py

from pygame.sprite import Sprite

class Ship(Sprite):
    """A class for managing our ship."""

    def __init__(self, ai_game):
        """Initlize the ship and set its starting position."""
        super().__init__()
        self.screen = ai_game.screen
        self.settings = ai_game.settings
        self.screen_rect = ai_game.screen.get_rect()

        # load ship image and get its rect.(rect stands for rectangle)
        self.image = pygame.image.load('images/ship.bmp')
        self.rect = self.image.get_rect()

        # Start each new ship at the bottom center of the screen.
        self.rect.midbottom = self.screen_rect.midbottom

        # store a decimal value for the ship's horizontal position
        self.x = float(self.rect.x)

        # movement flags
        # we start our flag at false so the ship doesnt move by itself
        self.moving_right = False
        self.moving_left = False

    def update(self):
        """Update ships position based on our movement flags"""
        # update ship's x value, not its rectange
        if self.moving_right and self.rect.right < self.screen_rect.right:
            self.x += self.settings.ship_speed
        if self.moving_left and self.rect.left > 0:
            self.x -= self.settings.ship_speed

        # update rect object for self.x
        self.rect.x = self.x

    def blitme(self):
        """Draw the ship at its current location."""
        self.screen.blit(self.image, self.rect)

    def center_ship(self):
        """center ship"""
        self.rect.midbottom = self.screen_rect.midbottom
        self.x = float(self.rect.x)

我将如何让每个外星人投下他们自己的炸弹?

希望这遵循对你们更好的最小可重现示例。

你需要在炸弹的 __init__ 中包含炸弹的 x y 位置:

class Bomb(sprite):
    def __init__(self, ai_game, x, y):
        # [...]

        self.rect = pygame.Rect(x, y, self.settings.bullet_width,
            self.settings.bullet_height)

        self.y = y

现在在创建炸弹时(在 _drop_bombs 中),您需要包括外星人的 x 和 y 位置:

class AlienInvasion:
    # [...]

    def _drop_bombs(self):
        """drop bombs from alien ships"""
        for alien in self.aliens:
            new_bomb = Bomb(self, alien.x, alien.y)  # top left of alien
            # do alien.x + 5 or something to drop the bomb in the middle
            self.bombs.add(new_bomb)

我还建议在 __init__ 中包含外星人的 x 和 y 位置,而不是事后修改它。