Make a plane war game with python

Posted by asdf on Wed, 17 Nov 2021 15:06:20 +0100


There are many kinds of aircraft wars, but those are not familiar enough. Today, let's have a wechat aircraft war of the same type.

1, Software and modules used

1. Software
Python
pycharm

2. Module
pygame
sys
os
random

Import module

import pygame
import sys
import os
import random

Path address of material file

source_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'material_images')

Initialize pygame

pygame.init()
pygame.display.init()
pygame.font.init()

Simple encapsulation of upper, lower, left and right variables

class Direction():
    UP = 1
    DOWN = 2
    LEFT = 3
    RIGHT = 4

Mixin class that displays aircraft self destruction animation, which can be used for self destruction animation display of aircraft and enemy aircraft

class DestroyAnimationMixin():

    def show_destroy_animation(self, time_passed, destroy_time=200):
        '''
        Show self destruct animation
        Animation is actually a few pictures that switch quickly. Our eyes can't recognize it, so we think it is dynamic, that is, animation

        :param time_passed: Time since the last image was drawn, in ms
        :param destroy_time: Total display time of self destruction animation, unit ms
        '''

Because we have four self destruct pictures, which need to be displayed in turn. First, the effect of animation
self.destroy_image_position refers to the self destruction picture in the chapter, starting from scratch
If it is greater than or equal to 4, the self destruction animation display is completed. Set the self.destroyed variable to True for easy calling elsewhere

if self.destroy_image_position >= 4:
    self.destroyed = True
    return

Load self destruct pictures in turn

if self.time_passed >= destroy_time / 4:
    self.image = pygame.image.load(os.path.join(source_dir, self.destroy_images[self.destroy_image_position])).convert_alpha()
    self.destroy_image_position += 1
    self.time_passed = 0
else:
    self.time_passed += time_passed

The enemy aircraft class inherits DestroyAnimationMixin to facilitate the use of functions that display self destruction animation

class Enemy(DestroyAnimationMixin):
    def __init__(self, image_path=os.path.join(source_dir, 'enemy1.png'), speed=2000, background_size=(480, 700)):
        '''
        :param image_path: Enemy picture address
        :param speed: Time required for enemy aircraft to move the whole window, unit ms,That is, speed
        :param background_size: Size of the game window
        '''
        self.image = pygame.image.load(image_path).convert_alpha()
        self.speed = background_size[1] / speed
        self.background_size = background_size
        self.position = [random.randint(0, background_size[0]-self.image.get_size()[0]), -self.image.get_size()[1]]
        # Start self destruction
        self.start_destroy = False
        # Self destruction complete
        self.destroyed = False
        # Self destruct picture path
        self.destroy_images = ['enemy1_down1.png', 'enemy1_down2.png', 'enemy1_down3.png', 'enemy1_down3.png']
        # Time since the last image was drawn
        self.time_passed = 0
        # Self destruction picture in self.destroy_ Location of images
        self.destroy_image_position = 0

    def update(self, time_passed):
        '''
        Update the enemy's position
        :param time_passed: Time since the last image was drawn
        :return:
        '''
        # If the enemy plane runs out of the game window, set self.position[1] = -100 to facilitate use in other locations
        if self.position[1] >= self.background_size[1]:
            self.position[1] = -100
            return

        # If you start self destruction, call the self destruction function to display the animation. If not, change the position
        if self.start_destroy:
            self.show_destroy_animation(time_passed)
        else:
            self.position[1] += self.speed * time_passed

Aircraft bullets

class Bullet():
    def __init__(self, image_path=os.path.join(source_dir,'bullet.png'), background_size=(480, 700), plan=None, speed=1000):
        '''
        :param image_path: Picture address of bullet
        :param background_size: Game window size
        :param plan: Aircraft object
        :param speed: Missle Velocity 
        '''
        self.image = pygame.image.load(image_path).convert_alpha()
        self.background_size = background_size
        self.speed = background_size[1] / speed
        # Did the bullet hit the enemy plane
        self.destroyed = False
        self.position = self._get_position(plan)

    def _get_position(self, plan):
        '''
        according to plan Get the location of the bullet
        :param plan: Aircraft object
        '''
        bullet_size = self.image.get_size()
        plan_width = plan.image_size[0]
        x = (plan_width-bullet_size[0]) / 2
        return [plan.position[0] + x, plan.position[1]]

    def update(self, time_passed):
        '''
        Change bullet position
        :param time_passed: Time since the last image was drawn
        '''
        # If the bullet exceeds the screen or hits the enemy plane, set self.position[1] to - 100 and remove it when planning.draw
        if self.position[1] + self.image.get_size()[1] <= 0 or self.destroyed:
            self.position[1] = -100
            return

        # Distance changed = time * rate
        self.position[1] -= time_passed * self.speed

The aircraft class inherits DestroyAnimationMixin, which is convenient to use the function of displaying self destruction animation

Aircraft picture address

class Plan(DestroyAnimationMixin):
    def __init__(self, image_path=os.path.join(source_dir,'plan.png'), background_size=(480, 700)):
        '''
        :param image_path: 
        :param background_size: Game window size
        '''
        self.background_size = background_size
        self.image = pygame.image.load(image_path).convert_alpha()
        self.image_size = self.image.get_size()
        self.position = [(background_size[0]-self.image_size[0]) / 2, 500]
        # Distance per movement of the aircraft
        self.every_time_move_distance = 0.5
        # Aircraft bullets
        self.bullets = []

        # destroy association attributes
        # Start self destruction
        self.start_destroy = False
        # End of self destruction
        self.destroyed = False
        # Self destruct picture
        self.destroy_images = ['me_destroy_1.png', 'me_destroy_2.png', 'me_destroy_3.png', 'me_destroy_4.png']
        # Self destruct picture location
        self.destroy_image_position = 0
        # Time since the last image was drawn
        self.time_passed = 0

Update aircraft position

def update(self, direction):
        '''
        :param direction: Aircraft movement direction
        '''
        if direction == 1:
            if self.position[1] <= 0:
                return
            self.position[1] -= self.every_time_move_distance
        elif direction == 2:
            if self.position[1] + self.image_size[1] >= self.background_size[1]:
                return
            self.position[1] += self.every_time_move_distance
        elif direction == 3:
            if self.position[0] <= 0:
                return
            self.position[0] -= self.every_time_move_distance
        else:
            if self.position[0] + self.image_size[0] >= self.background_size[0]:
                return
            self.position[0] += self.every_time_move_distance

The plane fired bullets

 def shut(self, image_path=os.path.join(source_dir,'bullet.png')):
        '''
        :param image_path: Bullet picture
        '''
        bullet = Bullet(image_path, plan=self)
        self.bullets.append(bullet)

Draw all the bullets in the plane

    def draw_bullets(self, time_passed, screen):
        '''
        :param time_passed: Time since the last image was drawn
        :param screen: To which window
        '''
        # Clean up the missing bullets
        for bullet in self.bullets:
            if bullet.position[1] == -100:
                self.bullets.remove(bullet)

        # Update the position of each bullet
        for bullet in self.bullets:
            bullet.update(time_passed)

        # Draw each bullet
        for bullet in self.bullets:
            screen.blit(bullet.image, bullet.position)

param background_image_path: the path address of the background picture
param size: the size of the game window
param title: the title of the game window
param font_name: specify font
param font_size: Specifies the font size
param speed: the time taken by the background image to scroll the whole window once, in ms

class Game():
    def __init__(self, background_image_path, size=(480, 700), title='Aircraft war', font_name='Fangzheng shutI', font_size=30, speed=2000):

        self.size = size
        self.screen = pygame.display.set_mode(size)
        self.title = title
        self.background_image_path = background_image_path
        self.background = pygame.image.load(self.background_image_path).convert()
        # Set the font object to get the font in the system
        self.font = pygame.font.SysFont(font_name, font_size)
        # Get the Clock object, which we can use to get the time since the last image was drawn
        self.clock = pygame.time.Clock()
        # Initial position of background image
        self.height = 0
        # Using the height of the window in the scrolling time, you can get the scrolling distance per ms
        self.every_ms_move_distance = self.size[1] / speed   # 2 seconds
        # fraction
        self.score = 0
        # Store all enemy aircraft
        self.enemies = []

The score is displayed. It is 10px above the top of the window, centered left and right.

    def show_score(self):

        #                                            Enable zigzag mode font color background color
        score = self.font.render(f'score : {self.score} ', True, (0,0,0), (255,255,255))
        score_position_x = (self.size[0]-score.get_size()[0]) / 2
        score_position_y = 10
        self.screen.blit(score, (score_position_x, score_position_y))

Controls the frame of the drawing

    def set_time_passed(self):
        # Control the frame drawn, the larger the faster
        # self.clock.tick(1000)

        # Get the time from the last drawn image to the present, ms
        self.time_passed = self.clock.tick()

Draw the background picture and scroll down to create the feeling of the plane flying up all the time

   def draw_background(self):

        # Distance per move = distance per ms * time from last time to present (ms)
        move_distance = self.every_ms_move_distance * self.time_passed

        self.height += move_distance

        # If the height of the window is exceeded, reset the height to zero
        if self.height >= self.size[1]:
            self.height = 0

        # The two background images are displayed together to create an uninterrupted rolling effect of the background image
        self.screen.blit(self.background, (0, -self.size[1] + self.height))
        self.screen.blit(self.background, (0, self.height))

Create enemy aircraft

    def create_enemy(self, image_path=os.path.join(source_dir,'enemy1.png'), enemy_number=5):
        '''     
        :param image_path: Picture address of enemy aircraft
        :param enemy_number: How many enemy planes are on the screen at most
        '''
        if len(self.enemies) >= enemy_number:
            return
        enemy = Enemy(image_path=image_path)
        self.enemies.append(enemy)

Draw the enemy plane on the screen and clean up the enemy plane running out of the window.

  def draw_enemies(self, time_passed, screen):
        '''
        :param time_passed: The time elapsed since the last guide was drawn
        :param screen: Drawn window object
        '''

        # Clear enemy planes out of range
        for enemy in self.enemies:
            # When the enemy plane runs out of range, it sets enemy.position[1] to - 100, so we judge here that enemy.position[1]==-100 is the enemy plane running out of range
            # enemy.destroyed is True, which means that the enemy plane is hit by bullets and needs to clean up this enemy
            if enemy.position[1] == -100 or enemy.destroyed:
                self.enemies.remove(enemy)

        # Update enemy position
        for enemy in self.enemies:
            # Call the update method of each enemy aircraft to change the position of the enemy aircraft
            enemy.update(time_passed)

        # Draw enemy aircraft
        for enemy in self.enemies:
            # Draw the enemy plane according to its position
            screen.blit(enemy.image, enemy.position)

Detect whether the bullet hit the enemy plane

    def bullet_and_enemy_crash_detection(self, bullets):
        '''
        :param bullets: All the bullets in the plane
        '''
        for bullet in bullets:
            # Traverse every bullet
            for enemy in self.enemies:
                # Traverse each enemy aircraft to determine whether it has been hit
                if bullet.position[0] >= enemy.position[0] and bullet.position[0] <= enemy.position[0]+enemy.image.get_size()[0]:
                    if bullet.position[1] >= enemy.position[1] and bullet.position[1] <= enemy.position[1] + enemy.image.get_size()[1]:
                        # If hit, the enemy plane begins to destroy itself
                        enemy.start_destroy = True
                        # The bullet destroyed itself and disappeared
                        bullet.destroyed = True
                        # Score plus one
                        self.score += 1

Detect whether the enemy aircraft collided with the aircraft

    def plan_and_enemy_crash_detection(self, plan, allow_crash_size=None):
        '''
        :param plan: Aircraft object
        :param allow_crash_size: The allowable size of aircraft collision is only valid on the left and right
        '''
        # If this parameter is not passed in, it is assigned as 10% of the aircraft width
        if allow_crash_size is None:
            allow_crash_size = 0.1 * plan.image_size[0]

        for enemy in self.enemies:
            # Traverse each enemy aircraft to detect whether it collides
            if enemy.position[0]+enemy.image.get_size()[0] - allow_crash_size >= plan.position[0] and enemy.position[0] <= plan.position[0]+plan.image.get_size()[0] - allow_crash_size:
                if enemy.position[1] + enemy.image.get_size()[1] >= plan.position[1] and enemy.position[1] <= plan.position[1] + plan.image.get_size()[1]:
                    # A collision was detected and the plane began to self destruct
                    plan.start_destroy = True

Drawing aircraft

    def draw_plan(self, plan, time_passed):
        '''
        :param plan: Aircraft object
        :param time_passed: Time since last drawing
        :return:
        '''
        # If the aircraft starts to self destruct, call the self destruct function to display the self destruct animation
        if plan.start_destroy:
            plan.show_destroy_animation(time_passed, destroy_time=1000)

        self.screen.blit(plan.image, plan.position)

game over

    def game_over(self):

        while True:
            # Draw background map
            self.set_time_passed()
            self.draw_background()
            text = self.font.render(f'Game over, score : {self.score} ', True, (0, 0, 0), (255, 255, 255))
            text_position_x = (self.size[0] - text.get_size()[0]) / 2
            text_position_y = (self.size[1] - text.get_size()[1]) / 2
            self.screen.blit(text, (text_position_x, text_position_y))

            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()
                elif event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE:
                        pygame.quit()
                        sys.exit()

            pygame.display.update()

Game entry function, start function

    def run(self):
        '''
        :return:
        '''

        # Set the size of the game window
        pygame.display.set_caption(self.title)
        # Initialize an aircraft object
        plan = Plan()

        while True:
            # If the plane self destructs and the game is over, call game_over function
            if plan.destroyed:
                self.game_over()
                break

            # Detect listening events
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()
                elif event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE:
                        pygame.quit()
                        sys.exit()
                # If the user's spacebar pops up, it means that the user has pressed the spacebar and fired a bullet
                elif event.type == pygame.KEYUP:
                    if event.key == pygame.K_SPACE:
                        # Call the plan.shut function
                        plan.shut()

            # Detect up, down, left and right moving cases
            # w. A, s, D and up, down, left and right buttons are OK
            # Then execute the plan.update function to change the position of the aircraft
            key_pressed = pygame.key.get_pressed()
            if key_pressed[pygame.K_w] or key_pressed[pygame.K_UP]:
                plan.update(direction=Direction.UP)
            elif key_pressed[pygame.K_s] or key_pressed[pygame.K_DOWN]:
                plan.update(direction=Direction.DOWN)
            elif key_pressed[pygame.K_a] or key_pressed[pygame.K_LEFT]:
                plan.update(direction=Direction.LEFT)
            elif key_pressed[pygame.K_d] or key_pressed[pygame.K_RIGHT]:
                plan.update(direction=Direction.RIGHT)

            # Collision detection between bullets and enemy aircraft
            self.bullet_and_enemy_crash_detection(plan.bullets)
            # Collision detection between aircraft and enemy aircraft
            self.plan_and_enemy_crash_detection(plan)
            # Set property time_ The value of passed, the time from the last time, is convenient for later use
            self.set_time_passed()
            # Draw background picture
            self.draw_background()
            # Show score
            self.show_score()
            # Generate enemy aircraft
            self.create_enemy()
            # Draw enemy aircraft
            self.draw_enemies(time_passed=self.time_passed, screen=self.screen)
            # Drawing aircraft
            self.draw_plan(plan=plan, time_passed=self.time_passed)
            # Draw bullets
            plan.draw_bullets(time_passed=self.time_passed, screen=self.screen)
            # Show our images
            pygame.display.update()

Background picture address

if __name__ == '__main__':
    # Background picture address
    background_image_path = os.path.join(source_dir, 'background.png')
    game = Game(background_image_path=background_image_path)

Add game.run() to start the game

Brother Meng, have you abandoned your studies?
python welfare tutorial (including the complete source code of this article, the materials to be used in this article, e-books, other video tutorials and answers)
Collection method:
1. Like + comment (check "forward at the same time")
2. Pay attention to Xiaobian. And reply to the keyword [aircraft war] by private letter
(be sure to send a private message ~ click my avatar to see the private message button)

Topics: Python Game Development