Catalog
brief introduction
Using python to realize pygame version of the aircraft war game;
Environment: Windows system + python3.8.0
Rules of the game:
1. Click "PLAY" or press "P" to start the game;
2. The enemy aircraft is generated from the top random position according to the set frequency, and then moves downward;
3. The airship is generated in the middle of the bottom. Players use the up and down keys to control the movement of the airship and hit the space bar to launch bullets;
4. When the bullet hits the enemy aircraft, it will produce explosive effect and accumulate scores to the upper right corner;
5. After killing 10 aircrafts, the level increases, the generation frequency of enemy aircrafts becomes faster and the falling speed becomes faster;
6. When all three lives are gone, the game is over.
The effect of the game is as follows:
Implementation process
- Create a new file "file.py" to store information to the file and read the information of the file. This example is used to store and read the highest score;
import pickle # filename = 'file/stats.pkl' # Store information to file def save_file(obj, filename): statsObj = load_file(filename) if statsObj == 0: # Save dictionary directly when no file exists with open(filename, 'wb') as f: pickle.dump(obj, f, pickle.HIGHEST_PROTOCOL) else: # When a file exists, only the highest score in the file is modified for key, val in statsObj.items(): # Get the highest score value of the file (used when there is more than one file field) if key == 'highScore': statsObj[key] = obj['highScore'] obj = statsObj with open(filename, 'wb') as f: pickle.dump(obj, f, pickle.HIGHEST_PROTOCOL) # Read information def load_file(filename): try: with open(filename, 'rb') as f: return pickle.load(f) except FileNotFoundError: # Enter error message if no file exists msg = "Sorry, the file " + filename + " does not exist." print(msg) return 0 # obj = {'highScore': 20, 'points': 5} # obj = {'highScore': 50} # save_file(obj, filename) # filedata = load_file(filename) # print(filedata)
- Create a new file, settings.py, to define some necessary basic properties and initial values;
import file as f class Settings(): def __init__(self): self.screen_width = 480 self.screen_height = 660 self.bg_color = (230, 230, 230) # Bullet settings (width, height, color, maximum number) self.bullet_width = 5 self.bullet_height = 15 self.bullet_color = 255, 255, 255 # Enemy aircraft moving frequency self.enemy_frequency = 0 # Speed up the game self.speedup_scale = 1.1 # Speed of score increase self.score_scale = 1.5 self.initialize_settings() # Initialize statistics self.reset_stats() # Statistics file path self.filename = 'file/stats.pkl' # The game was inactive when it was first started self.game_active = False # Read the highest score of the file, in no case should the highest score be reset statsObj = f.load_file(self.filename) if statsObj == 0: # If no file exists, the maximum score is 0 highScore = 0 else: for key, val in statsObj.items(): # Get the highest score value of the file (used when there is more than one file field) if key == 'highScore': highScore = val self.high_score = highScore def initialize_settings(self): """Initialize settings that change as the game progresses""" self.player_move_speed = 2.5 self.bullet_speed = 3 self.enemy_move_speed = 1 # Score self.one_points = 50 def increase_speed(self): """Increase speed settings""" # self.player_move_speed *= self.speedup_scale self.bullet_speed *= self.speedup_scale self.enemy_move_speed *= self.speedup_scale self.one_points = int(self.one_points * self.score_scale) def reset_stats(self): """Initialize statistics that may change during the game run""" # Number of shooting loss self.player_limit = 3 # Firing fraction self.score = 0 # Grade self.level = 1 # Hit how many rectangles to upgrade self.level_number = 10 # Generate enemy frequency interval self.enemy_frequency_space = 50
- Create a new file, enemy.py, to define the enemy aircraft class (randomly generated by position topleft) and the declaration method move;
import pygame import random from pygame.sprite import Sprite class Enemy(Sprite): def __init__(self, enemy_down_imgs, settings): super(Enemy, self).__init__() self.image = pygame.image.load('images/enemy1.png') self.rect = self.image.get_rect() self.rect.topleft = [random.randint(0, settings.screen_width - self.rect.width), 0] self.down_imgs = enemy_down_imgs self.speed = settings.enemy_move_speed self.down_index = 0 # Enemy aircraft movement, boundary judgment and deletion are handled in the main cycle of the game def move(self): self.rect.top += self.speed
- Create a new file, player.py, to define the player class (which can be moved up, down, left, right) and corresponding methods;
import pygame from pygame.sprite import Sprite class Player(Sprite): def __init__(self, settings, screen): super(Player, self).__init__() self.settings = settings self.screen = screen self.screen_rect = self.screen.get_rect() # Bring in a picture of the ship and locate it self.image = pygame.image.load('images/player.png') self.rect = self.image.get_rect() self.rect.centerx = self.screen_rect.centerx self.rect.bottom = self.screen_rect.bottom # Mobile logo self.move_left = False self.move_right = False self.move_down = False self.move_up = False def rotate(self, angle): # pictures rotating self.image = pygame.transform.rotate(self.image, angle) def scale(self, multiple): # Image scaling self.image = pygame.transform.smoothscale(self.image, (multiple, multiple)) def update(self): if self.move_left and self.rect.left > self.screen_rect.left: self.rect.centerx -= self.settings.player_move_speed if self.move_right and self.rect.right < self.screen_rect.right: self.rect.centerx += self.settings.player_move_speed if self.move_down and self.rect.bottom < self.screen_rect.bottom: self.rect.centery += self.settings.player_move_speed if self.move_up and self.rect.top > 0: self.rect.centery -= self.settings.player_move_speed def draw_player(self): """Draw spacecraft to screen""" self.screen.blit(self.image, self.rect)
- A new file "bullet.py" is created to define the bullet class (located at the top of the spacecraft and moved upward) and corresponding methods;
import pygame from pygame.sprite import Sprite class Bullet(Sprite): """ A class that manages the bullets fired by a spaceship """ def __init__(self, settings, screen, player): """ Create a bullet object where the ship is located """ super(Bullet, self).__init__() self.screen = screen # Create a rectangle representing the bullet at (0,0) and set the correct position self.rect = pygame.Rect(0, 0, settings.bullet_width, settings.bullet_height) self.rect.centerx = player.rect.centerx # Top of spacecraft self.rect.bottom = player.rect.top # Store bullet position in decimal self.y = float(self.rect.y) self.color = settings.bullet_color self.speed = settings.bullet_speed def update(self): """Move the bullet up""" # Update the small value indicating the bullet position (bullet to the right) self.y -= self.speed # Update the location of the rect that represents the bullet self.rect.y = self.y def draw_bullet(self): """Draw bullets on the screen""" pygame.draw.rect(self.screen, self.color, self.rect)
- Create a new file "button.py" to define button classes and corresponding methods. This example is used to draw "PLAY" buttons;
import pygame.font class Button(): def __init__(self, screen, msg): """Initialize button properties""" self.screen = screen self.screen_rect = screen.get_rect() # Set button size and other properties self.width, self.height = 100, 30 self.button_color = (216, 30, 6) self.text_color = (255, 255, 255) self.font = pygame.font.SysFont(None, 36) # Create and center the button's rect object 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, and then draw the text self.screen.fill(self.button_color, self.rect) self.screen.blit(self.msg_image, self.msg_image_rect)
- Create a new file "scoreboard.py" to define the scoreboard. This example is used to draw the spaceship (life number) in the upper left corner, the "highest score" in the middle of the top, the "integral" and "level" in the upper right corner;
import pygame.font from pygame.sprite import Group from player import Player class Scoreboard(): """Category displaying score information""" def __init__(self, settings, screen): """Attribute involved in initializing display score""" self.screen = screen self.screen_rect = screen.get_rect() self.settings = settings # Font settings used to display score information self.text_color = (255, 255, 255) self.font = pygame.font.SysFont(None, 30) # Spacecraft scale value self.scaleValue = 20 # Prepare initial score image \ maximum score \ level self.prep_score() self.prep_high_score() self.prep_level() self.prep_players() def prep_score(self): """Convert score to rendered image""" rounded_score = int(round(self.settings.score, -1)) score_str = '{:,}'.format(rounded_score) self.score_image = self.font.render(score_str, True, self.text_color) # Put the score in the top right corner of the screen self.score_rect = self.score_image.get_rect() self.score_rect.right = self.screen_rect.right -20 self.score_rect.top = 10 def prep_high_score(self): """ Convert highest score to rendered image """ high_score = int(round(self.settings.high_score, -1)) high_score_str = "{:,}".format(high_score) self.high_score_image = self.font.render(high_score_str, True, self.text_color) # Center the highest score on the top of the screen self.high_score_rect = self.high_score_image.get_rect() self.high_score_rect.centerx = self.screen_rect.centerx self.high_score_rect.top = self.score_rect.top def prep_level(self): """Convert levels to rendered images""" self.level_image = self.font.render(str(self.settings.level), True, self.text_color) # Put the grade below the score self.level_rect = self.level_image.get_rect() self.level_rect.right = self.score_rect.right self.level_rect.top = self.score_rect.bottom + 10 def prep_players(self): """ Show how many ships are left """ self.players = Group() for player_number in range(self.settings.player_limit): player = Player(self.settings, self.screen) # Scale ball size and assign position player.scale(self.scaleValue) player.rect.x = 10 + player.rect.width * player_number * 0.5 player.rect.y = self.score_rect.top self.players.add(player) def show_score(self): """Show score on screen""" self.screen.blit(self.score_image, self.score_rect) self.screen.blit(self.high_score_image, self.high_score_rect) self.screen.blit(self.level_image, self.level_rect) # Mapping spacecraft self.players.draw(self.screen)
- Create a new file "game_functions.py" to store all business logic functions related to the game (the code has detailed annotation information);
import sys import pygame from bullet import Bullet from enemy import Enemy import file as f # Event def check_events(settings, screen, player, play_button, scoreboard, bullets, fireSound): """ Respond to key and mouse events """ for event in pygame.event.get(): if event.type == pygame.QUIT: save_file(settings) sys.exit() elif event.type == pygame.KEYDOWN: check_keydown_events(event, settings, screen, player, scoreboard, bullets, fireSound) elif event.type == pygame.KEYUP: check_keyup_events(event, player) elif event.type == pygame.MOUSEBUTTONDOWN: mouse_x, mouse_y = pygame.mouse.get_pos() check_play_button(settings, play_button, scoreboard, mouse_x, mouse_y) def check_keydown_events(event, settings, screen, player, scoreboard, bullets, fireSound): """ Response button """ if event.key == pygame.K_DOWN: player.move_down = True elif event.key == pygame.K_UP: player.move_up = True elif event.key == pygame.K_LEFT: player.move_left = True elif event.key == pygame.K_RIGHT: player.move_right = True elif event.key == pygame.K_SPACE: fireSound.play() # Click the space bar to create a bullet fire_bullet(settings, screen, player, bullets) elif event.key == pygame.K_p: start_game(settings, scoreboard) elif event.key == pygame.K_q: save_file(settings) sys.exit() def check_keyup_events(event, player): """ Response loosening """ if event.key == pygame.K_DOWN: player.move_down = False elif event.key == pygame.K_UP: player.move_up = False elif event.key == pygame.K_LEFT: player.move_left = False elif event.key == pygame.K_RIGHT: player.move_right = False def check_play_button(settings, play_button, scoreboard, mouse_x, mouse_y): """Click on the player Play Button to start a new game""" button_clicked = play_button.rect.collidepoint(mouse_x, mouse_y) if button_clicked and not settings.game_active: start_game(settings, scoreboard) def start_game(settings, scoreboard): """Start the game""" # Reset game settings settings.initialize_settings() # hide cursor pygame.mouse.set_visible(False) # Reset game statistics settings.reset_stats() settings.game_active = True # Reset scoreboard image scoreboard.prep_score() scoreboard.prep_high_score() scoreboard.prep_level() scoreboard.prep_players() def save_file(settings): # Keep files obj = {'highScore': settings.high_score} f.save_file(obj, settings.filename) # Enemy airplane def update_enemies(settings, screen, scoreboard, enemies, enemies_down, enemy_down_imgs, player, bullets, explosiveSound): # To generate enemy aircraft, you need to control the generation frequency if settings.enemy_frequency % settings.enemy_frequency_space == 0: enemy1 = Enemy(enemy_down_imgs, settings) enemies.add(enemy1) settings.enemy_frequency += 1 if settings.enemy_frequency >= 100: settings.enemy_frequency = 0 for enemy in enemies: # Mobile enemy aircraft enemy.move() # Collision effect processing between enemy aircraft and player aircraft circle detection between two elves if pygame.sprite.collide_circle(enemy, player): enemies_down.add(enemy) enemies.remove(enemy) settings.player_limit -= 1 scoreboard.prep_players() break # Delete the aircraft after moving out of the screen if enemy.rect.top < 0: enemies.remove(enemy) # How to deal with the effect of enemy aircraft being hit by bullets # Add the hit enemy object to the destroy enemy Group to render the destroy animation # The method groupcollide() is used to detect the rectangular conflicts between two sprites in the sprite group enemies1_down = pygame.sprite.groupcollide(enemies, bullets, True, True) if enemies1_down: explosiveSound.play() # Calculate fractions and render for enemys in enemies1_down.values(): settings.score += settings.one_points * len(enemys) scoreboard.prep_score() # Render top score check_high_score(settings, scoreboard) # Wait until you reach the level number to upgrade and render the new level settings.level_number -= 1 if settings.level_number == 0: settings.increase_speed() settings.level += 1 scoreboard.prep_level() # Revert to 4 (same as settings) settings.level_number = 10 # Speed up the generation of enemy aircraft if settings.enemy_frequency_space > 10: settings.enemy_frequency_space -= 10 # Collision enemy aircraft returned by traversing key value for enemy_down in enemies1_down: # Click the destroyed enemy aircraft to the list enemies_down.add(enemy_down) # Display of the effect of enemy aircraft being hit by bullets for enemy_down in enemies_down: if enemy_down.down_index == 0: pass if enemy_down.down_index > 7: enemies_down.remove(enemy_down) continue #Show clash picture screen.blit(enemy_down.down_imgs[enemy_down.down_index // 2], enemy_down.rect) enemy_down.down_index += 1 # Display Wizard enemies.draw(screen) # Bullet def fire_bullet(settings, screen, player, bullets): """Create a bullet""" new_bullet = Bullet(settings, screen, player) bullets.add(new_bullet) def update_bullets(screen, bullets): """Update the location of bullets and delete the disappeared bullets""" # Update bullet position bullets.update() # Delete the lost bullets and update the ship's life at the same time for bullet in bullets.copy(): if bullet.rect.top < screen.get_rect().top: bullets.remove(bullet) # Fraction def check_high_score(settings, scoreboard): """Check to see if a new top score has been born""" if settings.score > settings.high_score: settings.high_score = settings.score scoreboard.prep_high_score() # screen def update_screen(settings, screen, player, play_button, scoreboard, enemies, bullets): """ Update the image on the screen and switch to the new screen """ # Draw spacecraft to screen player.draw_player() # Draw bullets to screen for bullet in bullets.sprites(): bullet.draw_bullet() # Render scoreboard information scoreboard.show_score() # if settings.player_limit == 0: settings.game_active = False settings.reset_stats() # Empty rectangle list and bullet list enemies.empty() bullets.empty() screen_rect = screen.get_rect() player.rect.centerx = screen_rect.centerx player.rect.bottom = screen_rect.bottom # If the game is inactive, draw the Play button if not settings.game_active: play_button.draw_button() # Make the most recently drawn screen visible pygame.display.flip()
- The main function is used to initialize the program and update the information of the program synchronously;
import pygame from pygame.sprite import Group from settings import Settings from button import Button from player import Player import game_functions as gf from scoreboard import Scoreboard def run_game(): pygame.init() # Initialize all audio and load blast music pygame.mixer.init() # Waiting for 1s pygame.time.delay(1000) pygame.mixer.music.load('file/bgsound.mp3') # -1 for infinite loop (background music) pygame.mixer.music.play(-1) # Explosions explosiveSound = pygame.mixer.Sound('file/explosiveSound.wav') # Shot fireSound = pygame.mixer.Sound('file/fireSound.wav') # Game cycle frame rate setting clock = pygame.time.Clock() settings = Settings() screen = pygame.display.set_mode((settings.screen_width, settings.screen_height)) # Full screen display # screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN) pygame.display.set_caption('Aircraft Wars') # Top left Icon ic_launcher = pygame.image.load('images/ic_launcher.png').convert_alpha() pygame.display.set_icon(ic_launcher) # Background map background = pygame.image.load('images/background.png').convert_alpha() # Enemy aircraft pictures enemy_img1= pygame.image.load('images/enemy1.png') enemy_img2= pygame.image.load('images/enemy2.png') enemy_img3= pygame.image.load('images/enemy3.png') enemy_img4= pygame.image.load('images/enemy4.png') # A list of pictures of enemy aircraft in different states. Multiple pictures are displayed as animation effects enemy_down_imgs = [] enemy_down_imgs.append(enemy_img1) enemy_down_imgs.append(enemy_img2) enemy_down_imgs.append(enemy_img3) enemy_down_imgs.append(enemy_img4) # Enemy aircraft storage enemies = Group() # Store destroyed aircraft for rendering destroyed animation enemies_down = Group() # Create Play button play_button = Button(screen, 'Play') # Create spaceship player = Player(settings, screen) # Create bullet groups bullets = Group() # Create scoreboard scoreboard = Scoreboard(settings, screen) while True: # Drawing background screen.blit(background, (0, 0)) # Control the maximum frequency of the game clock.tick(60) # Check player input (if not added, it will be loaded all the time) gf.check_events(settings, screen, player, play_button, scoreboard, bullets, fireSound) if settings.game_active: # Update ship position player.update() # Update enemy planes gf.update_enemies(settings, screen, scoreboard, enemies, enemies_down, enemy_down_imgs, player, bullets, explosiveSound) # Update bullet position gf.update_bullets(screen, bullets) # Update screen information gf.update_screen(settings, screen, player, play_button, scoreboard, enemies, bullets) run_game(),
- In the directory path of the file shootingenemy.py, execute the command "python shootingenemy.py" pop-up window to operate and play.
epilogue
The game includes background music, shooting sound, explosion sound and explosion effect of bullets hitting enemy aircraft, life number, score, level, maximum score, start button and other elements. You can also add other fun elements by yourself.