I'm learning 《python Programming : From introduction to practice 》 This book , Complete the first project “ Alien invasion ” The problems encountered are shown in the figure below , Strangely, this error doesn't run every time alien_invasion All the time ( The previous several runs can correctly complete the corresponding logic functions ), After this error, I rerun , Sometimes it works , Sometimes continue to report this error . I don't know why , People are numb .
import pygamefrom pygame.sprite import Spriteclass Alien(Sprite): # Class representing a single alien def __init__(self, ai_settings, screen): # Initialize the alien and set its starting position super().__init__() self.screen = screen self.ai_settings = ai_settings # Loading alien images , And set its rect attribute self.image = pygame.image.load('images/alien.bmp') self.image = pygame.transform.scale(self.image, (45, 48)) self.rect = self.image.get_rect() # Each alien was initially near the top left corner of the screen # The left margin of each alien is set to the width of the alien , And set the upper margin to the height of aliens self.rect.x = self.rect.width self.rect.y = self.rect.height # Store the exact location of Aliens self.x = float(self.rect.x) def check_edges(self): # If the alien is on the edge of the screen , Just go back to True screen_rect = self.screen.get_rect() if self.rect.right >= screen_rect.right: return True elif self.rect.left <= 0: return True def update(self): # Move aliens left or right self.x += (self.ai_settings.alien_speed_factor * self.ai_settings.fleet_direction) self.rect.x =self.x def blitme(self): # Draw aliens in designated locations self.screen.blit(self.image, self.rect)
alien_invasion.py The code is as follows
import pygamefrom pygame.sprite import Groupfrom button import Buttonfrom game_stats import GameStatsfrom ship import Shipfrom settings import Settingsimport game_functions as gfdef run_game(): # initialization pygame、 Settings and screen objects pygame.init() ai_settings = Settings() # Create a display window , And specify the size of the game window px, Notice the use of two parentheses screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height)) pygame.display.set_caption("Alien Invasion") ship = Ship(ai_settings, screen) # Create a spaceship bullets = Group() # Create a group to store bullets aliens = Group() # Create an alien group stats = GameStats(ai_settings) # Create an instance to store game statistics gf.create_fleet(ai_settings, screen, ship, aliens) # Creating alien groups play_button = Button(ai_settings, screen, 'Play') # establish Play Button # Start the main cycle of the game while True: gf.check_events(ai_settings, screen, stats, play_button, ship, aliens, bullets) # Monitor keyboard and mouse events if stats.game_active: ship.update() # Update the position of the ship gf.update_bullets(ai_settings, screen, ship, aliens, bullets) # Update the location of all bullets that have not disappeared gf.update_aliens(ai_settings, stats, screen, ship, aliens, bullets) gf.update_screen(ai_settings, screen, stats, ship, aliens, bullets, play_button) # Use the updated position to redraw the new screen run_game() # This will initialize the game and start the main loop
bullet.py Code block
import pygamefrom pygame.sprite import Spriteclass Bullet(Sprite): """ A class that manages the bullets fired by a spaceship """ def __init__(self, ai_settings, screen, ship): # Create a bullet object where the ship is located super().__init__() self.screen = screen # stay (0,0) Create a rectangle representing the bullet at , Set the correct position self.rect = pygame.Rect(0, 0, ai_settings.bullet_width, ai_settings.bullet_height) self.rect.centerx = ship.rect.centerx self.rect.top = ship.rect.top # Will represent the... Of a bullet rect Of top Property is set to the ship's rect Of top attribute , Make the bullet look like it was shot from a spaceship # Store bullet position in decimal self.y = float(self.rect.y) self.color = ai_settings.bullet_color self.speed_factor = ai_settings.bullet_speed_factor def update(self): # Move the bullet up # Update the small value indicating the bullet position self.y -= self.speed_factor # After launch , The bullet moves up the screen , It means y The coordinates will decrease # Update for bullets rect The location of self.rect.y = self.y def draw_bullet(self): # Draw bullets on the screen pygame.draw.rect(self.screen, self.color, self.rect)
button.py Code block
import pygame.font # Render text to the screen class Button(): def __init__(self, ai_settings, screen, msg): # Initialize button properties self.screen = screen self.screen_rect = screen.get_rect() # Set button size and other properties self.width, self.height = 200, 50 self.button_color = (67, 205, 128) self.text_color = (255, 255, 255) self.font = pygame.font.SysFont(None, 48) # Actual parameters None Give Way Pygame Use default font ,48 Specify the font size of the text . # Create button's rect object , And center it self.rect = pygame.Rect(0, 0, self.width, self.height) self.rect.center = self.screen_rect.center # The label of the button needs to be created only once self.prep_msg(msg) def prep_msg(self, msg): # take msg Render as image , And center it on the button self.msg_image = self.font.render(msg, True, self.text_color, self.button_color) self.msg_image_rect = self.msg_image.get_rect() self.msg_image_rect.center = self.rect.center def draw_button(self): # Draw a color filled button , Redraw text self.screen.fill(self.button_color, self.rect) self.screen.blit(self.msg_image, self.msg_image_rect)
game_functions.py Code block :
import sys # When players exit , Using modules sys Quit the game from time import sleepimport pygamefrom alien import Alienfrom bullet import Bulletdef check_keydown_events(event, ai_settings, screen, ship, bullets): # Response button if event.key == pygame.K_RIGHT: ship.moving_right = True elif event.key == pygame.K_LEFT: ship.moving_left = True elif event.key == pygame.K_UP: ship.moving_up = True elif event.key == pygame.K_DOWN: ship.moving_down = True elif event.key == pygame.K_SPACE: fire_bullet(ai_settings, screen, ship, bullets)def check_keyup_events(event, ship): # Response loosening if event.key == pygame.K_RIGHT: ship.moving_right = False elif event.key == pygame.K_LEFT: ship.moving_left = False elif event.key == pygame.K_UP: ship.moving_up = False elif event.key == pygame.K_DOWN: ship.moving_down = False elif event.key == pygame.K_q: sys.exit()def check_events(ai_settings, screen, stats, play_button, ship, aliens, bullets): # Respond to key and mouse events for event in pygame.event.get(): if event.type == pygame.QUIT: # When the player clicks the close button of the game window, it is detected that sys.exit() elif event.type == pygame.KEYDOWN: check_keydown_events(event, ai_settings, screen, ship, bullets) elif event.type == pygame.KEYUP: check_keyup_events(event, ship) elif event.type == pygame.MOUSEBUTTONDOWN: mouse_x, mouse_y = pygame.mouse.get_pos() check_play_button(ai_settings, screen, stats, play_button, ship, aliens, bullets, mouse_x, mouse_y)def check_play_button(ai_settings, screen, stats, play_button, ship, aliens, bullets, mouse_x, mouse_y): # Player Click Play Button to start a new game button_clicked = play_button.rect.collidepoint(mouse_x, mouse_y) if button_clicked and not stats.game_active: # hide cursor pygame.mouse.set_visible(False) # Reset game statistics stats.reset_stats() stats.game_active = True # Empty alien list and bullet list aliens.empty() bullets.empty() # Create a new group of Aliens , And center the ship create_fleet(ai_settings, screen, ship, aliens) ship.center_ship()def update_screen(ai_settings, screen, stats, ship, aliens, bullets, play_button): # Update image on screen , And switch to the new screen # Redraw the screen every time you cycle screen.fill(ai_settings.bg_color) # Redraw all the bullets behind the spaceship and the alien for bullet in bullets.sprites(): bullet.draw_bullet() ship.blitme() aliens.draw(screen) # If the game is inactive , Just draw Play Button if not stats.game_active: play_button.draw_button() # Constantly update the screen , Make the most recently drawn screen visible # As we move game elements ,pygame.display.flip() The screen will be constantly updated , To display the new position of the element , And hide the element in its original position , So as to create the effect of smooth movement . pygame.display.flip()def update_bullets(ai_settings, screen, ship, aliens, bullets): # Update bullet position bullets.update() # Delete bullets that have disappeared for bullet in bullets.copy(): if bullet.rect.bottom <= 0: bullets.remove(bullet) check_bullet_alien_collisions(ai_settings, screen, ship, aliens, bullets)def check_bullet_alien_collisions(ai_settings, screen, ship, aliens, bullets): # In response to the collision between the bullet and the alien # Delete bullets and aliens in collision collisions = pygame.sprite.groupcollide(bullets, aliens, True, True) if len(aliens) == 0: # Delete existing bullets and create a new group of Aliens bullets.empty() create_fleet(ai_settings, screen, ship, aliens)def fire_bullet(ai_settings, screen, ship, bullets): # If the limit is not reached , Just fire a bullet # Create a bullet , And add it to the group bullets in if len(bullets) < ai_settings.bullets_allowed: new_bullet = Bullet(ai_settings, screen, ship) bullets.add(new_bullet)def get_number_aliens_x(ai_settings, alien_width): # Calculate how many aliens per row avaliable_space_x = ai_settings.screen_width - 2 * alien_width number_aliens_x = int(avaliable_space_x / (2 * alien_width)) return number_aliens_xdef get_number_rows(ai_settings, ship_height, alien_height): # How many lines of aliens can the screen hold available_space_y = (ai_settings.screen_height - (3 * alien_height) - ship_height) number_rows = int(available_space_y / (2 * alien_height)) return number_rowsdef create_alien(ai_settings, screen, aliens, alien_number, row_number): # Create an alien and put it on the current line alien = Alien(ai_settings, screen) alien_width = alien.rect.width # From alien rect Property to get the alien width alien.x = alien_width + 2 * alien_width * alien_number alien.rect.x = alien.x alien.rect.y = alien.rect.height + 2 * alien.rect.height * row_number aliens.add(alien)def create_fleet(ai_settings, screen, ship, aliens): # Creating alien groups # Create an alien , And calculate how many aliens a row can hold # Space between aliens is alien width alien = Alien(ai_settings, screen) number_aliens_x = get_number_aliens_x(ai_settings, alien.rect.width) number_rows = get_number_rows(ai_settings, ship.rect.height, alien.rect.height) # # Create the first line of Aliens # for alien_number in range(number_aliens_x): # create_alien(ai_settings, screen, aliens, alien_number) # Creating alien groups for row_number in range(number_rows): for alien_number in range(number_aliens_x): create_alien(ai_settings, screen, aliens, alien_number, row_number)def check_fleet_edges(ai_settings, aliens): # Take action when an alien reaches the edge for alien in aliens.sprites(): if alien.check_edges(): change_fleet_direction(ai_settings, aliens) breakdef change_fleet_direction(ai_settings, aliens): # Move the whole group of aliens down , And change their direction for alien in aliens.sprites(): alien.rect.y += ai_settings.fleet_drop_speed ai_settings.fleet_direction *= -1def ship_hit(ai_settings, stats, screen, ship, aliens, bullets): # Responding to a spaceship hit by an alien if stats.ships_left > 0: # take ship_left reduce 1 stats.ships_left -= 1 # Empty alien list and bullet list aliens.empty() bullets.empty() # Create a new group of aliens , And put the ship in the center of the bottom create_fleet(ai_settings, screen, ship, aliens) ship.center_ship() # Pause sleep(0.5) else: stats.game_active = False pygame.mouse.set_visible(True)def check_aliens_bottom(ai_settings, stats, screen, ship, aliens, bullets): # Check if any aliens have reached the bottom of the screen screen_rect = screen.get_rect() for alien in aliens.sprites(): if alien.rect.bottom >= screen_rect.bottom: # Deal with it as if it were hit by a spaceship ship_hit(ai_settings, stats, screen, ship, aliens, bullets) breakdef update_aliens(ai_settings, stats, screen, ship, aliens, bullets): # Check if any aliens have reached the bottom of the screen check_aliens_bottom(ai_settings, stats, screen, ship, aliens, bullets) # Check for aliens on the edge of the screen , And update the location of all aliens in the alien population check_fleet_edges(ai_settings, aliens) aliens.update() # Detect collisions between aliens and spacecraft if pygame.sprite.spritecollideany(ship, aliens): ship_hit(ai_settings, stats, screen, ship, aliens, bullets)
game_stats.py Code block
class GameStats(): # Track game statistics def __init__(self, ai_settings): # Initialize statistics self.ai_settings = ai_settings self.reset_stats() # Make the game inactive at the beginning self.game_active = False def reset_stats(self): # Initialize statistics that may change during the game run self.ships_left = self.ai_settings.ship_limit
settings Code block
class Settings(): # A class that stores all the settings for the project def __init__(self): # Initialize game settings # screen setting self.screen_width = 1000 self.screen_height = 700 self.bg_color = (230, 230, 230) # Spacecraft setup self.ship_left_right_speed_factor = 1.5 # Left and right movement speed self.ship_up_down_speed_factor = 1.5 self.ship_limit = 3 # Bullet settings self.bullet_speed_factor = 3 self.bullet_width = 3 self.bullet_height = 15 self.bullet_color = 178, 34, 34 self.bullets_allowed = 3 # Alien settings self.alien_speed_factor = 0.3 # Alien movement speed setting self.fleet_drop_speed = 10 # The speed at which the aliens move down # fleet_direction by 1 Move to the right , by -1 Move left self.fleet_direction = 1
ship.py Code block
import pygameclass Ship(): def __init__(self, ai_settings, screen): # Initialize the spacecraft and set its initial position self.screen = screen self.ai_settings = ai_settings # Load the image of the spacecraft and get its external rectangle self.image = pygame.image.load('images/ship.bmp') self.image = pygame.transform.scale(self.image, (40, 70)) # take Surface Attribute self.image Turn into 30×30 Size self.rect = self.image.get_rect() self.screen_rect = screen.get_rect() # Put each new ship in the center of the bottom of the screen self.rect.centerx = self.screen_rect.centerx self.rect.bottom = self.screen_rect.bottom # Properties in the spaceship center Small value stored in self.centerx = float(self.rect.centerx) self.centery = float(self.rect.centery) # Mobile logo self.moving_right = False self.moving_left = False self.moving_up = False self.moving_down = False def update(self): # Adjust the position of the spacecraft according to the moving signs # Update the ship's center value , instead of rect # Make the spacecraft stop moving when it reaches the edge of the screen if self.moving_right and (self.rect.right < self.screen_rect.right): # Mark is True Move the ship to the right self.centerx += self.ai_settings.ship_left_right_speed_factor if self.moving_left and (self.rect.left > 0): self.centerx -= self.ai_settings.ship_left_right_speed_factor if self.moving_down and (self.rect.bottom < self.screen_rect.bottom): self.centery += self.ai_settings.ship_up_down_speed_factor if self.moving_up and (self.rect.top > 0): self.centery -= self.ai_settings.ship_up_down_speed_factor # according to self.centerx/y to update rect object self.rect.centerx = self.centerx self.rect.centery = self.centery def blitme(self): # Draw the spacecraft at the designated location self.screen.blit(self.image, self.rect) def center_ship(self): # Center the ship on the screen # self.center = self.screen_rect.centerx self.rect.midbottom = self.screen_rect.midbottom self.centerx = float(self.rect.centerx) self.centery = float(self.rect.centery)