Python课程设计 飞机大战小游戏

飞机大战小游戏

Python课程设计 飞机大战小游戏_第1张图片

目录

飞机大战小游戏

1.概述

1.1 开发环境及技术

1.2 实现功能描述

1.2.1主要的工作内容

1.2.2 实现功能

1.2.3 功能完善

1.2.4 参考项目

2.项目介绍

2.1 项目内容介绍

2.2 项目介绍

2.2.1 项目代码逻辑

2.2.2 涉及到的知识

2.2.3 遇到的问题

2.3 最终运行效果​编辑

3.附件

3.1 Bullet。py

3.2 enemy.py

3.3 hero.py

3.4 settings.py

3.5 supply.py

3.6 main.py


摘要

在数字娱乐时代,小游戏因其简单、直观且易于上手的特点,深受广大玩家的喜爱。其中,飞机大战游戏作为一种经典的游戏类型,凭借其紧张刺激的战斗场景和多样的游戏元素,一直深受玩家们的喜爱。为了满足这一市场需求,决定设计一款基于pygame的飞机大战小游戏。

Python作为一种高级编程语言,具有简洁、易读、可扩展性强等特点,被广泛应用于各个领域。近年来,随着游戏开发技术的不断发展,Python在游戏开发领域的应用也日益广泛。pygame作为Python的一个开源游戏开发库,为开发者提供了丰富的游戏开发工具和资源,使得开发者能够更加便捷地开发出高质量的游戏。

本研究的目的是通过开发一款基于pygame的飞机大战小游戏,进一步探索Python在游戏开发领域的应用。同时,本研究的意义在于为其他开发者提供一种简单、易行的游戏开发方法,推动Python在游戏开发领域的发展。

本研究的主要内容是开发一款基于pygame的飞机大战小游戏。具体研究内容包括:游戏设计、游戏实现、游戏测试与优化等。本研究采用的方法包括文献综述、案例分析、实践操作等。首先,通过文献综述了解游戏开发的基本原理和pygame库的使用方法;其次,通过案例分析学习经典的游戏设计模式;最后,通过实践操作完成游戏的开发。

本研究的目标是开发出一款简单、易玩、具有挑战性的飞机大战小游戏。具体目标包括:实现基本的游戏场景、飞机和敌机的基本移动和射击功能、分数统计和生命值管理功能等。预期成果包括:实现上述功能的完整游戏代码、一份详细的开发文档、以及针对游戏的测试报告和优化建议。

1.概述

1.1 开发环境及技术

本研究使用的是Windows11操作系统,Python版本为3.9。开发工具选择的是PyCharm,版本为2021.2.1。

  1. Pygame是一个开源的游戏开发库,提供了丰富的游戏开发工具和资源,可以帮助开发者更加便捷地开发出高质量的游戏。
  2. random是Python的内置库,用于生成随机数。在飞机大战游戏中,随机数可以用于生成敌机、子弹等元素。
  3. sys模块是Python的标准库之一,它提供了与解释器相关的功能。
  4. time模块是Python中用于处理时间的内建库。它自带了很多方法,可以将不同的时间类型进行相互转换。
  5. settings库通常用于配置应用程序的各种设置。它是一个Python模块,可以用于存储各种配置信息,如数据库连接、应用程序的路径、密码等等。settings库通常包含一系列的配置选项,每个选项都是一个变量,可以设置不同的值。这些选项可以根据需要进行设置,也可以在程序运行时动态地更改。

1.2 实现功能描述

1.2.1主要的工作内容

(1)游戏设计:确定游戏的基本规则、界面设计、音效等。这包括定义游戏的玩法、目标、难度设置,以及创建游戏的视觉和音频效果。

(2)编程:编写游戏的核心逻辑,包括飞机移动、射击、敌机生成、碰撞检测等。这是实现游戏功能的关键步骤,需要处理游戏中的各种逻辑和物理计算。

(3)图形和音效:制作游戏所需的图像和音效,如飞机、子弹、敌机、爆炸声等。这需要设计和制作游戏的视觉效果以及音效,使游戏更具吸引力。

(4)测试:确保游戏的稳定性和可玩性。这包括检查游戏是否存在漏洞、错误或性能问题,以及测试游戏的平衡性和难度设置。

(5)优化:调整游戏性能,提高游戏的流畅度。这可能包括优化代码、减少加载时间、提高渲染效率等。

(6)用户界面设计:设计易于使用的界面,使玩家能够轻松控制游戏。这包括按钮的位置、大小和颜色,以及任何其他用户界面元素的设计。

1.2.2 实现功能

(1)飞机移动:玩家可以通过键盘或触摸屏控制飞机在屏幕上左右移动,躲避敌机的攻击。

(2)射击功能:玩家可以发射子弹,射击敌机,击中敌机后可以获得分数。

敌机生成和移动:敌机在屏幕上随机生成,并向玩家的飞机移动,增加了游戏的挑战性和趣味性。

(3)碰撞检测:当飞机与敌机或子弹碰撞时,游戏结束或得分增加,确保了游戏的公平性和可玩性。

(4)得分和生命值显示:游戏实时显示玩家的得分和生命值,使玩家能够了解自己的游戏进度和当前状态。

(5)音效和视觉效果:游戏具有音效和视觉效果,如射击音效、爆炸效果等,增加了游戏的吸引力和沉浸感。

1.2.3 功能完善

  (1)爆炸效果:当子弹接触到敌机时,增加爆炸效果,使游戏更加生动和刺激。

  (2)Boss 敌机:增加 Boss 敌机,这些敌机具有更高的生命值和攻击力,使游戏更具挑战性。同时,显示 Boss 敌机的血量,让玩家更好地了解战斗状况。

  (3)敌机发射子弹:对于一部分敌机,增加敌机发射子弹的功能,增加游戏的复杂性和策略性。

  (4)道具敌机:增加道具敌机,这些敌机在被击杀后会掉落道具,玩家可以收集这些道具来获得特殊能力或增益效果。

  (5)背景音乐和击杀音乐:增加游戏的背景音乐和击杀敌机时的音效,提升游戏的沉浸感和娱乐性。

  (6)游戏难度:根据玩家的技能水平,调整敌机的数量、速度和攻击频率,以提供更具挑战性的游戏体验。

  (7)游戏模式:添加新的游戏模式,例如限时模式、生存模式或合作模式等,提供多样化的玩法。

  (8)社交功能:集成社交功能,允许玩家与好友对战或合作完成任务,增加游戏的互动性和竞技性。

  (9)游戏成就:设立游戏成就系统,激励玩家完成特定任务或挑战,增加游戏的目标和挑战性。

1.2.4 参考项目

Pygame库:Pygame是一个用于制作视频游戏的Python库。它可以用来创建飞机大战小游戏的图形和音效,并且具有广泛的使用和社区支持。.

Unity引擎:Unity是一个流行的游戏开发引擎,可以用来创建飞机大战小游戏的3D效果和复杂的游戏机制。Unity提供了丰富的资源、工具和文档,可以帮助开发者快速构建游戏。

游戏设计教程和文档:可以参考在线游戏设计教程和文档,以获取关于飞机大战小游戏游戏机制、界面设计、音效等方面的指导。这些资源可以帮助开发者了解游戏设计的最佳实践和理论。

2.项目介绍

2.1 项目内容介绍

基于pygame的飞机大战小游戏是一个经典的游戏,玩家需要控制一架飞机在屏幕上移动,并射击敌机。下面是一个简单的基于pygame的飞机大战小游戏的概述:

(1)游戏初始化:

导入pygame库和其他必要的模块。设置游戏窗口大小、标题和其他选项。加载所需的图像、音效和字体文件。

(2)游戏主循环:

初始化游戏状态、时钟和精灵组等。进入游戏主循环,处理游戏事件(如键盘按键、鼠标点击等)。

(3)游戏逻辑:

玩家控制:根据玩家的输入,控制飞机的移动。

射击:检测玩家的射击按键,发射子弹并击中敌机。

敌机生成和移动:在屏幕上随机生成敌机,并向玩家的飞机移动。

碰撞检测:检测飞机与敌机或子弹的碰撞,处理游戏结束或得分增加。

(4)游戏渲染:

绘制游戏背景、飞机、子弹、敌机等精灵,更新游戏界面,包括得分、生命值等显示元素。

(5)游戏音效和视觉效果:

加载并播放背景音乐和音效,使用适当的颜色和动画效果,增加游戏的吸引力和沉浸感

(6)游戏优化和改进:

根据玩家反馈和测试结果,优化游戏性能、提高渲染效率、调整难度,添加新的游戏功能和元素,如道具、特殊效果等,以增加游戏的可玩性和挑战性。

2.2 项目介绍

2.2.1 项目代码逻辑

1.对项目结构进行截图并描述。

Python课程设计 飞机大战小游戏_第2张图片

图1 项目结构

描述:

  1. bullet.py、enemy.py、hero.py、main.py是游戏文件,这是游戏的主要代码文件,通常包含游戏的主循环、逻辑、渲染等核心功能。
  2. sound是音效和音乐,存放游戏的音效和背景音乐文件。
  3. image是图像资源,存放游戏所需的图像文件,如飞机、子弹、敌机等。
  4. record.txt是文档和说明,包含游戏的文档、说明和帮助文件。

2.每个代码文件代码内容进行概述 

脚本名称

实现逻辑概述

备注

hero.py

控制玩家英雄的移动、射击、跳跃等动作

飞机可能有不同的动作

bullet.py

处理子弹的生成、移动和销毁

子弹移动并可能击中敌人

setting.py

设置游戏的基本参数和配置

游戏窗口的大小、音效的开关

enemy.py

控制敌人的生成、移动和销毁

敌人从某个位置生成,向玩家移动

supply.py

处理游戏中的补给品或其他增益效果

玩家收集补给以增强能力

main.py

游戏的主入口,整合上述所有模块的功能

从上述模块中调用必要的功能

3.对项目的输入输出进行描述。

启动项:启动游戏窗口,展示游戏的主界面和初始状态

键盘输入:玩家通过键盘上的按键进行操作,如W、A、S、D控制英雄的移动

输出:

游戏界面:显示游戏的主界面,包括玩家的英雄、敌人和子弹等元素的位置和状态。

音效和背景音乐:根据游戏事件播放音效和背景音乐。

分数和生命值显示:实时显示玩家的分数和生命值,以便玩家了解游戏状态。

4.对项目里关键的属性(变量)通过表格展示出来,并标注其含义:

属性

含义

备注

hero

玩家英雄对象

控制英雄的移动、射击等动作

bullet

子弹对象

表示玩家的射击行为

setting

游戏设置对象

包含游戏的基本参数和配置

enemy

敌人对象

控制敌人的生成、移动和销毁

supply

游戏补给品对象

表示游戏中可收集的增益效果或资源

main

游戏主程序对象

整合所有模块的功能,驱动游戏的进行

2.2.2 涉及到的知识

1)书本知识涉及有:

  1. 基础语法:包括变量、数据类型(如整数、浮点数、字符串、列表、元组、字典和集合)、控制流语句(如if-else, for, while)等。
  2. 函数:如何定义函数、函数的参数传递、局部变量与全局变量、函数返回值等。
  3. 文件操作:如何打开、读取、写入和关闭文件,以及文件路径的处理等。
  4. 异常处理:使用try-except语句来处理程序中可能出现的错误或异常。
  5. 迭代器和生成器:使用迭代器遍历数据集合,以及使用生成器创建自己的迭代器对象。
  6. 装饰器:用于修改或增强函数或方法的行为的特殊类型的函数。
  7. 模块和包:除了模块的导入,还有如何创建自己的模块和包,以及模块间的相互引用等。
  8. 多线程与多进程:如何处理并发任务,包括线程和进程的基本概念、同步机制等。

(2)新知识涉及有: 

  pygame库:首先,你需要熟悉pygame库的基本使用。pygame是一个用于制作2D游戏的Python库,它提供了丰富的图像、声音、事件处理等功能,可以帮助你快速构建游戏。

2.2.3 遇到的问题

1.问题:pygame库安装失败。

解决过程:首先,确保Python和pip已经正确安装。然后,使用pip install pygame命令尝试安装。如果仍然失败,可以考虑使用虚拟环境,或者检查网络连接和代理设置。

2. 问题:加载的图像显示不正常。

解决过程:首先,检查图像文件路径是否正确。其次,确保图像文件的格式是pygame支持的。如果图像仍然显示不正常,可以尝试使用图像编辑软件打开并重新保存图像文件,或者调整pygame的图像加载参数。

3. 问题:音效和背景音乐无法播放。

解决过程:首先,检查音效和音乐文件的路径是否正确。其次,确保文件的格式是pygame支持的。如果仍然无法播放,可以尝试使用不同的音频文件,或者调整pygame的音频设置。

2.3 最终运行效果Python课程设计 飞机大战小游戏_第3张图片

图2 运行结果

描述:

1)游戏开始时,玩家将看到一个简单的菜单界面

2)玩家需要控制飞机移动并射击敌机,同时躲避敌机的攻击。

3)游戏界面下方将显示玩家生命值。当玩家击中敌机时,分数将增加;当玩家的生命值减少到零时,游戏将结束。

4)在游戏过程中,玩家可以获得补给品,以提高战斗力。

5)随着关卡的深入,敌机会变得更加强大,移动速度更快。玩家需要不断提升自己的技能和策略来应对挑战。

3.附件

3.1 Bullet。py

# import pygame
from settings import *


class Bullet1(pygame.sprite.Sprite):
    # Bullet1继承Sprite的所有属性和方法
    def __init__(self, position):
        # 构造函数,初始化对象的属性
        super().__init__()
        # 使用super().__init__()调用父类的构造函数
        self.image = pygame.image.load(FILE_PATH + "image/bullet1.png").convert_alpha()
        self.rect = self.image.get_rect()
        self.rect.left, self.rect.top = position
        self.speed = 12
        self.active = True
        # 将子弹的状态设置为活动状态
        self.mask = pygame.mask.from_surface(self.image)
        # 创建一个与图像关联的掩码,用于更精确的碰撞检测,然后存储在self.mask。

    def move(self):
        self.rect.top -= self.speed
        if self.rect.top < 0:
            self.active = False

    def reset(self, position):
        self.rect.left, self.rect.top = position
        self.active = True


class Bullet2(pygame.sprite.Sprite):
    def __init__(self, position):
        super().__init__()
        self.image = pygame.image.load(FILE_PATH + "image/bullet2.png").convert_alpha()
        self.rect = self.image.get_rect()
        self.rect.left, self.rect.top = position
        self.speed = 14
        self.active = False
        self.mask = pygame.mask.from_surface(self.image)

    def move(self):
        self.rect.top -= self.speed
        if self.rect.top < 0:
            self.active = False

    def reset(self, position):
        self.rect.left, self.rect.top = position
        self.active = True

3.2 enemy.py

from random import *
from settings import *


class SmallEnemy(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.image.load(FILE_PATH + "image/enemy1.png").convert_alpha()
        self.destroy_images = []
        self.destroy_images.extend([
            pygame.image.load(FILE_PATH + "image/enemy1_down1.png").convert_alpha(),
            pygame.image.load(FILE_PATH + "image/enemy1_down2.png").convert_alpha(),
            pygame.image.load(FILE_PATH + "image/enemy1_down3.png").convert_alpha(),
            pygame.image.load(FILE_PATH + "image/enemy1_down4.png").convert_alpha()
        ])
        self.rect = self.image.get_rect()
        # 获取敌人图像的矩形区域
        self.speed = 2
        self.active = True
        self.rect.left = randint(0, SCREEN_RECT.width - self.rect.width)
        self.rect.top = randint(-5 * SCREEN_RECT.height, 0)
        # 随机初始化敌人的位置。敌人的左边缘位置在屏幕宽度内随机选择,
        # 而顶部位置则在屏幕高度的五倍范围内随机选择
        self.mask = pygame.mask.from_surface(self.image)
        self.check()
        # 调用check方法检查敌人的位置是否需要进行调整

    def move(self):
        if self.rect.top < SCREEN_RECT.height:
            self.rect.top += self.speed
        else:
            self.reset()

    def reset(self):
        self.active = True
        self.rect.left = randint(0, SCREEN_RECT.width - self.rect.width)
        self.rect.top = randint(-5 * SCREEN_RECT.height, 0)

    def check(self):
        if self.rect.right >= SCREEN_RECT.width:
            self.rect.right = SCREEN_RECT.width


class MidEnemy(pygame.sprite.Sprite):
    hp = 8

    # 代表敌人的生命值,并且被设置为8
    def __init__(self):
        super().__init__()
        self.image = pygame.image.load(FILE_PATH + "image/enemy2.png").convert_alpha()
        self.image_hit = pygame.image.load(FILE_PATH + "image/enemy2_hit.png").convert_alpha()
        self.destroy_images = []
        self.destroy_images.extend([
            pygame.image.load(FILE_PATH + "image/enemy2_down1.png").convert_alpha(),
            pygame.image.load(FILE_PATH + "image/enemy2_down2.png").convert_alpha(),
            pygame.image.load(FILE_PATH + "image/enemy2_down3.png").convert_alpha(),
            pygame.image.load(FILE_PATH + "image/enemy2_down4.png").convert_alpha()
        ])
        self.rect = self.image.get_rect()
        self.speed = 1
        self.active = True
        self.rect.left = randint(0, SCREEN_RECT.width - self.rect.width)
        self.rect.top = randint(-10 * SCREEN_RECT.height, -SCREEN_RECT.height)
        # 随机初始化敌人的位置。敌人的左边缘位置在屏幕宽度内随机选择,
        # 而顶部位置则在屏幕高度的 - 10倍到 - 1 倍范围内随机选择(允许敌人从屏幕上方开始移动)。
        self.mask = pygame.mask.from_surface(self.image)
        self.hit = False
        # 敌人是否受到攻击

    def move(self):
        if self.rect.top < SCREEN_RECT.height:
            self.rect.top += self.speed
        else:
            self.reset()

    def reset(self):
        self.hp = MidEnemy.hp
        self.active = True
        self.rect.left = randint(0, SCREEN_RECT.width - self.rect.width)
        self.rect.top = randint(-10 * SCREEN_RECT.height, -SCREEN_RECT.height)


class BigEnemy(pygame.sprite.Sprite):
    hp = 20

    def __init__(self):
        super().__init__()
        self.image = pygame.image.load(FILE_PATH + "image/enemy3_n1.png").convert_alpha()
        self.image2 = pygame.image.load(FILE_PATH + "image/enemy3_n2.png").convert_alpha()
        self.image_hit = pygame.image.load(FILE_PATH + "image/enemy3_hit.png").convert_alpha()
        # 加载敌人受到攻击时的图像
        self.destroy_images = []
        self.destroy_images.extend([
            pygame.image.load(FILE_PATH + "image/enemy3_down1.png").convert_alpha(),
            pygame.image.load(FILE_PATH + "image/enemy3_down2.png").convert_alpha(),
            pygame.image.load(FILE_PATH + "image/enemy3_down3.png").convert_alpha(),
            pygame.image.load(FILE_PATH + "image/enemy3_down4.png").convert_alpha(),
            pygame.image.load(FILE_PATH + "image/enemy3_down5.png").convert_alpha(),
            pygame.image.load(FILE_PATH + "image/enemy3_down6.png").convert_alpha()
        ])
        self.rect = self.image.get_rect()
        self.speed = 1
        self.active = True
        self.rect.left = randint(0, SCREEN_RECT.width - self.rect.width)
        self.rect.top = randint(-15 * SCREEN_RECT.height, -5 * SCREEN_RECT.height)
        self.mask = pygame.mask.from_surface(self.image)
        self.hit = False

    def move(self):
        if self.rect.top < SCREEN_RECT.height:
            self.rect.top += self.speed
        else:
            self.reset()

    def reset(self):
        self.active = True
        self.hp = BigEnemy.hp
        self.rect.left = randint(0, SCREEN_RECT.width - self.rect.width)
        self.rect.top = randint(-15 * SCREEN_RECT.height, -5 * SCREEN_RECT.height)

3.3 hero.py

from settings import *


class Hero(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        # 在子类中调用其父类(超类)的初始化方法
        # super().__init__()来调用基类的初始化方法
        self.image = pygame.image.load(FILE_PATH + "image/me1.png").convert_alpha()
        self.image2 = pygame.image.load(FILE_PATH + "image/me2.png").convert_alpha()
        self.destroy_images = []
        self.destroy_images.extend([
            pygame.image.load(FILE_PATH + "image/me_destroy_1.png").convert_alpha(),
            pygame.image.load(FILE_PATH + "image/me_destroy_2.png").convert_alpha(),
            pygame.image.load(FILE_PATH + "image/me_destroy_3.png").convert_alpha(),
            pygame.image.load(FILE_PATH + "image/me_destroy_4.png").convert_alpha()
        ])
        self.rect = self.image.get_rect()
        self.rect.centerx = SCREEN_RECT.centerx
        self.rect.bottom = SCREEN_RECT.bottom - 60
        # 底部保持60像素的距离
        self.speed = 10
        self.active = True
        self.invincible = False
        self.mask = pygame.mask.from_surface(self.image)

    def moveUp(self):
        if self.rect.top > 0:
            self.rect.top -= self.speed
        else:
            self.rect.top = 0

    def moveDown(self):
        if self.rect.bottom < SCREEN_RECT.height - 60:
            self.rect.top += self.speed
        else:
            self.rect.bottom = SCREEN_RECT.height - 60
            # 向下移动主角。如果主角的底部位置小于屏幕的高度减去60,则增加其底部位置。
            # 否则,将其设置为屏幕高度减去60

    def moveLeft(self):
        if self.rect.left > 0:
            self.rect.left -= self.speed
        else:
            self.rect.left = 0
            # 向左移动主角。如果主角的左侧位置大于0,则减少其左侧位置。否则,将其设置为0。

    def moveRight(self):
        if self.rect.right < SCREEN_RECT.width:
            self.rect.right += self.speed
        else:
            self.rect.right = SCREEN_RECT.width

    def reset(self):
        self.active = True
        self.invincible = False
        self.rect.centerx = SCREEN_RECT.centerx
        self.rect.bottom = SCREEN_RECT.bottom - 60
        # 重置主角的状态。将主角的活动状态设置为True
        # 无敌状态设置为True,并重置其位置到屏幕底部中心

3.4 settings.py

import pygame
SCREEN_RECT = pygame.Rect(0, 0, 480, 700)
FPS = 60
FILE_PATH = ""

3.5 supply.py

import pygame
from settings import *
from random import randint


class BulletSupply(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.image.load(FILE_PATH + "image/bullet_supply.png").convert_alpha()
        self.rect = self.image.get_rect()
        self.rect.left = randint(0, SCREEN_RECT.width - self.rect.width)
        self.rect.bottom = -100
        # 设置矩形的底部位置为 - 100,意味着它最初位于屏幕底部下方。
        self.speed = 3
        self.active = False
        self.mask = pygame.mask.from_surface(self.image)

    def move(self):
        if self.rect.top < SCREEN_RECT.height:
            self.rect.top += self.speed
        else:
            self.active = False
    # 如果子弹的顶部在屏幕上(即self.rect.top < SCREEN_RECT.height),
    # 则向上移动其位置,使其沿屏幕向上移动。当其到达屏幕顶部时,
    # 将self.active设置为False,表示它不再活动

    def reset(self):
        self.active = True
        self.rect.left = randint(0, SCREEN_RECT.width - self.rect.width)
        self.rect.bottom = -100


class BombSupply(pygame.sprite.Sprite):
    # 这个类与BulletSupply类相似
    def __init__(self):
        super().__init__()
        self.image = pygame.image.load(FILE_PATH + "image/bomb_supply.png").convert_alpha()
        self.rect = self.image.get_rect()
        self.rect.left = randint(0, SCREEN_RECT.width - self.rect.width)
        self.rect.bottom = -100
        self.speed = 3
        self.active = False
        self.mask = pygame.mask.from_surface(self.image)

    def move(self):
        if self.rect.top < SCREEN_RECT.height:
            self.rect.top += self.speed
        else:
            self.active = False

    def reset(self):
        self.active = True
        self.rect.left = randint(0, SCREEN_RECT.width - self.rect.width)
        self.rect.bottom = -100


class LifeSupply(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()

        self.image = pygame.image.load(FILE_PATH + "image/life_supply.png").convert_alpha()
        self.rect = self.image.get_rect()
        self.rect.left = randint(0, SCREEN_RECT.width - self.rect.width)
        self.rect.bottom = -100
        self.speed = 3
        self.active = False
        self.mask = pygame.mask.from_surface(self.image)

    def move(self):
        if self.rect.top < SCREEN_RECT.height:
            self.rect.top += self.speed
        else:
            self.active = False

    def reset(self):
        self.active = True
        self.rect.left = randint(0, SCREEN_RECT.width - self.rect.width)
        self.rect.bottom = -100

3.6 main.py

from sys import exit
from time import sleep
from pygame.constants import USEREVENT
from bullet import *
from enemy import *
from hero import *
from supply import *

pygame.init()


class PlaneGame(object):
    def __init__(self):
        self.screen = pygame.display.set_mode(SCREEN_RECT.size)
        # set_mode()函数:这是pygame.display模块中的一个函数,用于创建一个窗口或全屏窗口来显示内容。它接受一个元组参数,表示窗口的尺寸。
        pygame.display.set_caption("飞机大战")

        self.background = pygame.image.load(FILE_PATH + "image/background.png")
        self.clock_ = pygame.time.Clock()
        # 创建一个时钟对象,用于控制游戏的速度。
        pygame.time.set_timer(self.SUPPLY_NOTICE_TIME, 30 * 1000)
        # 设置一个定时器,当定时器到期时(这里是30秒后),
        # 它会触发self.SUPPLY_NOTICE_TIME事件。这意味着当游戏运行30秒后,
        # 可能会显示一个通知或提示玩家有关补给的消息
        sleep(0.5)
        pygame.time.set_timer(self.SUPPLY_TIME, 30 * 1000)
        # 这里又设置了一个定时器,当游戏运行30秒后,它会触发另一个事件或效果,可能与补给品有关
        self.__createEvent()
        # 调用一个名为__createEvent 的方法

    def startGame(self):
        pygame.mixer.music.play(-1)
        while True:
            self.screen.blit(self.background, (0, 0))
            # 在屏幕上绘制背景图像。屏幕的左上角开始
            self.screen.blit(self.paused_image, self.paused_rect)
            # paused_rect 定义了图像的位置。
            self.__handleEvent()
            # 调用一个名为__handleEvent 的方法
            if self.life_num and not self.paused:
                # 检查玩家的生命数量是否大于0并且游戏没有暂停。
                self.clock_.tick(FPS)
                # 使用pygame的时钟对象来控制游戏的速度
                self.__drawEvents()
                # 调用一个自定义方法来绘制或显示游戏中的事件或元素。
                self.__checkCollide()
                # 调用一个自定义方法来检查游戏中的碰撞事件,如子弹击中敌人
            pygame.display.update()
            # 更新屏幕上的所有绘制操作
            if self.life_num == 0:
                pygame.mixer.music.stop()
                pygame.mixer.stop()
                pygame.time.set_timer(self.SUPPLY_TIME, 0)
                # 设置一个定时器事件,但参数设置为0意味着定时器将立即触发。
                print("Game Over!")
                if not self.recording:
                    # 检查是否已经开始记录分数或其他信息。
                    self.recording = True
                    with open("record.txt", "r") as f:
                        record_score = int(f.read())
                    if self.score > record_score:
                        with open("record.txt", "w") as f:
                            f.write(str(self.score))
                    exit()

    screen = pygame.display.set_mode(SCREEN_RECT.size)
    pygame.display.set_caption("飞机大战")
    switch_image = True
    # 控制图像的切换
    delay = 500
    # 用于定时或延迟的参数。用于控制某个动作或事件之间的时间间隔
    e1_destroy_index = 0
    e2_destroy_index = 0
    e3_destroy_index = 0
    me_destroy_index = 0
    # 跟踪敌机的销毁索引
    bullet1 = []
    # 存储玩家发射的子弹
    bullet1_index = 0
    BULLET1_NUM = 4
    bullet2 = []
    bullet2_index = 0
    BULLET2_NUM = 8
    BLACK = (0, 0, 0)
    GREEN = (0, 255, 0)
    RED = (255, 0, 0)
    WHITE = (255, 255, 255)
    # 定义四种颜色。黑色是由三个0组成,绿色由0和255的绿色组成,红色由255的红色组成,白色由三个255组成。这些常量可能在游戏的图形渲染中使用。
    score = 0
    score_font = pygame.font.SysFont("Freestyle Script", 36)
    paused = False
    pause_nor = pygame.image.load(FILE_PATH + "image/pause_nor.png").convert_alpha()
    pause_pressed = pygame.image.load(FILE_PATH + "image/pause_pressed.png").convert_alpha()
    resume_nor = pygame.image.load(FILE_PATH + "image/resume_nor.png").convert_alpha()
    resume_pressed = pygame.image.load(FILE_PATH + "image/resume_pressed.png").convert_alpha()
    # 加载暂停和恢复按钮的图像
    paused_rect = pause_nor.get_rect()
    paused_rect.left, paused_rect.top = SCREEN_RECT.width - paused_rect.width - 10, 10
    # 获取暂停按钮的矩形区域并设置其位置
    paused_image = pause_nor
    # 设置暂停图像的默认图像

    level = 1
    bomb = pygame.image.load(FILE_PATH + "image/bomb.png").convert_alpha()
    bomb_rect = bomb.get_rect()
    bomb_font = pygame.font.SysFont("Freestyle Script", 48)
    bomb_num = 3
    bomb_max = 3
    bullet_supply = BulletSupply()
    bomb_supply = BombSupply()
    life_supply = LifeSupply()
    # 用于子弹、炸弹和生命值的供应或生成
    SUPPLY_TIME = USEREVENT + 1
    # 供应的持续时间
    SUPER_BULLET = USEREVENT + 2
    # 供应子弹的持续时间
    is_super_bullet = False
    super_bullet_s = 18
    life_num = 3
    life_image = pygame.image.load(FILE_PATH + "image/life.png").convert_alpha()
    life_rect = life_image.get_rect()
    INVINCIBLE_TIME = USEREVENT + 3
    # 无敌状态的持续时间
    recording = False
    # 游戏是否处于录制模式。
    SUPPLY_NOTICE_TIME = USEREVENT + 4
    # 供应通知的持续时间
    supply_notice_font = pygame.font.SysFont("Freestyle Script", 48)
    supply_notice = False
    supply_notice_end = False
    # 显示供应通知
    pygame.mixer.init()
    pygame.mixer.music.load(FILE_PATH + "sound/game_music.mp3")
    pygame.mixer.music.set_volume(0.2)
    bullet_sound = pygame.mixer.Sound(FILE_PATH + "sound/bullet.mp3")
    bullet_sound.set_volume(0.2)
    # 子弹声音
    bomb_sound = pygame.mixer.Sound(FILE_PATH + "sound/use_bomb.mp3")
    bomb_sound.set_volume(0.2)
    # 炸弹声音
    supply_sound = pygame.mixer.Sound(FILE_PATH + "sound/out_porp.mp3")
    supply_sound.set_volume(0.2)
    # 供应声音
    get_bomb_sound = pygame.mixer.Sound(FILE_PATH + "sound/get_bomb.mp3")
    get_bomb_sound.set_volume(0.2)
    # 获得炸弹
    get_bullet_sound = pygame.mixer.Sound(FILE_PATH + "sound/bullet.mp3")
    get_bullet_sound.set_volume(0.2)
    # 获得子弹
    double_sound = pygame.mixer.Sound(FILE_PATH + "sound/get_double_laser.mp3")
    double_sound.set_volume(0.2)
    # 双子弹声音
    enemy3_fly_sound = pygame.mixer.Sound(FILE_PATH + "sound/big_spaceship_flying.mp3")
    enemy3_fly_sound.set_volume(0.2)
    # 敌人飞行的声音
    enemy1_down_sound = pygame.mixer.Sound(FILE_PATH + "sound/enemy0_down.mp3")
    enemy1_down_sound.set_volume(0.2)
    enemy2_down_sound = pygame.mixer.Sound(FILE_PATH + "sound/enemy1_down.mp3")
    enemy2_down_sound.set_volume(0.2)
    enemy3_down_sound = pygame.mixer.Sound(FILE_PATH + "sound/enemy2_down.mp3")
    enemy3_down_sound.set_volume(0.2)
    # 敌人倒下的声音
    me_down_sound = pygame.mixer.Sound(FILE_PATH + "sound/game_over.mp3")
    me_down_sound.set_volume(0.2)
    # 玩家倒下的声音
    upgrade_sound = pygame.mixer.Sound(FILE_PATH + "sound/achievement.mp3")
    upgrade_sound.set_volume(0.2)
    # 游戏升级

    def __createEvent(self):
        self.hero = Hero()
        # 在方法中创建新的Hero对象
        self.hero_group = pygame.sprite.Group(self.hero)
        # 创建一个玩家组,并将之前创建的英雄对象添加到这个组中
        for i in range(self.BULLET1_NUM):
            self.bullet1.append(Bullet1(self.hero.rect.midtop))
            # del i
            # 这是一个循环,用于创建BULLET1_NUM数量的第一种子弹。
            # 每种子弹都是通过调用Bullet1() 类并传递英雄的顶部中心位置来创建的。
            # 然后,将新创建的子弹添加到self.bullet1列表中。del i语句是多余的,
            # 因为循环结束后,变量i就不再使用了
        for i in range(self.BULLET2_NUM // 2):
            self.bullet2.append(Bullet2((self.hero.rect.centerx - 33, self.hero.rect.centery)))
            self.bullet2.append(Bullet2((self.hero.rect.centerx + 30, self.hero.rect.centery)))
        # 这个循环创建了两种位置的第二种子弹,并将它们添加到self.bullet2列表中。
        self.enemies = pygame.sprite.Group()
        self.small_enemy_group = pygame.sprite.Group()
        self.__add_small_enemy(self.small_enemy_group, self.enemies, 15)
        # 调用一个静态方法,来添加15个小型敌人到之前创建的两个精灵组中。
        self.mid_enemy_group = pygame.sprite.Group()
        self.__add_mid_enemy(self.mid_enemy_group, self.enemies, 4)
        self.big_enemy_group = pygame.sprite.Group()
        self.__add_big_enemy(self.big_enemy_group, self.enemies, 2)

    @staticmethod
    def __add_small_enemy(group1, group2, num):
        for i in range(num):
            m0 = SmallEnemy()
            group1.add(m0)
            group2.add(m0)
        del i
    # 这个静态方法接受两个精灵组和一个数字作为参数。
    # 它使用一个循环来创建指定数量的小型敌人,并将这些敌人添加到两个精灵组中。
    # del i是多余的,因为在循环结束后,变量i会自动被销毁

    @staticmethod
    def __add_mid_enemy(group1, group2, num):
        for i in range(num):
            m1 = MidEnemy()
            group1.add(m1)
            group2.add(m1)
        del i

    @staticmethod
    def __add_big_enemy(group1, group2, num):
        for i in range(num):
            m2 = BigEnemy()
            group1.add(m2)
            group2.add(m2)
        del i

    @staticmethod
    def __inc_speed(target, inc=1):
        for i in target:
            i.speed += inc
            del i
    # 遍历target中的所有对象,并将它们的speed属性值增加1。

    def __handleEvent(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                # 如果事件类型是退出事件(即用户点击了关闭按钮)
                pygame.quit()
                exit()

            if event.type == pygame.MOUSEBUTTONDOWN:
                if event.button == 1 and self.paused_rect.collidepoint(event.pos):
                    # 如果按下的是鼠标左键,并且鼠标位置与暂停按钮的矩形区域相交
                    self.paused = not self.paused
                    # 切换游戏的暂停状态
                    if self.paused:
                        # 如果游戏处于暂停状态
                        pygame.time.set_timer(self.SUPPLY_TIME, 0)
                        pygame.time.set_timer(self.SUPPLY_NOTICE_TIME, 0)
                        # 停止所有计时器
                        pygame.mixer.music.pause()
                        pygame.mixer.pause()
                        # 暂停所有音乐和声效
                    else:
                        pygame.time.set_timer(self.SUPPLY_NOTICE_TIME, 30 * 1000)
                        sleep(0.5)
                        # 在0.5秒后启动供应通知计时器
                        pygame.time.set_timer(self.SUPPLY_TIME, 30 * 1000)
                        # 启动供应计时器
                        pygame.mixer.music.unpause()
                        pygame.mixer.unpause()
                        # 恢复所有音乐和声效

            if event.type == pygame.MOUSEMOTION:
                # 如果事件类型是鼠标移动事件
                if self.paused_rect.collidepoint(event.pos):
                    # 检查鼠标的位置是否与一个特定的矩形区域(self.paused_rect)相交
                    if self.paused:
                        self.paused_image = self.resume_pressed
                        # 如果游戏处于暂停状态,则将self.paused_image设置为self.resume_pressed。
                    else:
                        self.paused_image = self.pause_pressed
                else:
                    # 如果鼠标位置不在矩形区域内
                    if self.paused:
                        self.paused_image = self.resume_nor
                        # 如果游戏处于暂停状态,则将self.paused_image设置为self.resume_nor
                    else:
                        self.paused_image = self.pause_nor

            if event.type == pygame.KEYDOWN:
                # 如果事件类型是键盘按键按下事件
                if event.key == pygame.K_SPACE:
                    # 如果按下的键是空格键
                    if self.bomb_num:
                        # 如果玩家有炸弹
                        self.bomb_num -= 1
                        # 减少玩家的炸弹数量
                        self.bomb_sound.play()
                        # 播放炸弹声效
                        for each in self.enemies:
                            if each.rect.bottom > 0:
                                each.active = False
            # 遍历所有的敌人,如果敌人的底部在屏幕上方,则将其标记为不活动状态(可能是炸弹的效果
            if event.type == self.SUPPLY_TIME:
                # 如果事件类型是供应时间事件
                self.supply_sound.play()
                self.supply_notice_end = True
                # 设置供应通知结束标志为True
                self.supply_type = choice([1, 2, 3])
                # 使用choice函数从列表[1, 2, 3]中随机选择一个值,
                # 并将该值赋给self.supply_type。这可能代表不同类型的供应。
                if self.supply_type == 1:
                    self.bomb_supply.reset()
                    # *重置`bomb_supply`,可能是恢复玩家的炸弹数量或某种与炸弹相关的供应。
                elif self.supply_type == 2:
                    self.bullet_supply.reset()
                    # *重置`bullet_supply`,可能是恢复玩家的子弹数量或某种与子弹相关的供应。
                else:
                    if self.life_num < 3:
                        self.life_supply.reset()
                    else:
                        if choice([True, False]):
                            self.bomb_supply.reset()
                        else:
                            self.bullet_supply.reset()
                            # *首先检查玩家的生命数(`self.life_num`)是否小于3。
                            # 如果是,则重置`life_supply`;
                            # 否则,根据一个随机选择决定是重置`bomb_supply` 还是 `bullet_supply`。
                self.supply_notice = False
                self.supply_notice_end = False
                # 将两个标志重置为False,可能是为了准备下一次的供应通知或过程。

            if event.type == self.SUPER_BULLET:
                self.is_super_bullet = False
                # 如果检测到的事件类型是SUPER_BULLET,代码首先将self.is_super_bullet设置为False,
                pygame.time.set_timer(self.SUPER_BULLET, 0)
            # 使用pygame.time.set_timer()函数重置或停止该事件的计时器。
            if event.type == self.INVINCIBLE_TIME:
                self.hero.invincible = False
                # 无敌状态被关闭。
                pygame.time.set_timer(self.INVINCIBLE_TIME, 0)
                # 使用pygame.time.set_timer()函数重置或停止该事件的计时器
            if event.type == self.SUPPLY_NOTICE_TIME:
                self.supply_notice = True
                # 供应通知被触发或显示。

        if self.level == 1 and self.score >= 50:
            self.level = 2
            self.upgrade_sound.play()
            # 播放升级音效
            self.bomb_max += 2
            self.__add_small_enemy(self.small_enemy_group, self.enemies, 3)
            self.__add_mid_enemy(self.mid_enemy_group, self.enemies, 2)
            self.__add_big_enemy(self.big_enemy_group, self.enemies, 1)
            # 在敌人组中添加3个小敌机、2个中敌机和1个大敌机
            self.__inc_speed(self.small_enemy_group)
            # 小敌机的速度增加1

        elif self.level == 2 and self.score >= 300:
            self.level = 3
            self.upgrade_sound.play()
            self.super_bullet_s += 2
            # 超级子弹的数量增加2
            self.bomb_max += 1
            # 炸弹的最大数量增加1
            self.__add_small_enemy(self.small_enemy_group, self.enemies, 5)
            self.__add_mid_enemy(self.mid_enemy_group, self.enemies, 3)
            self.__add_big_enemy(self.big_enemy_group, self.enemies, 2)
            # 向游戏中的敌人组中添加5个小敌机、3个中敌机和2个大敌机
            self.__inc_speed(self.small_enemy_group)
            # 小型敌机的速度 + 1
            self.__inc_speed(self.mid_enemy_group)
            # 中型敌机的速度 + 1

        elif self.level == 3 and self.score >= 600:
            self.level = 4
            self.upgrade_sound.play()
            self.super_bullet_s += 3
            self.bomb_max += 1
            self.__add_small_enemy(self.small_enemy_group, self.enemies, 5)
            self.__add_mid_enemy(self.mid_enemy_group, self.enemies, 3)
            self.__add_big_enemy(self.big_enemy_group, self.enemies, 2)
            # 小型敌机的速度 + 1
            self.__inc_speed(self.small_enemy_group)
            # 中型敌机的速度 + 1
            self.__inc_speed(self.mid_enemy_group)

        elif self.level == 4 and self.score >= 1000:
            self.level = 5
            self.upgrade_sound.play()
            self.super_bullet_s += 4
            self.bomb_max += 1
            self.__add_small_enemy(self.small_enemy_group, self.enemies, 5)
            self.__add_mid_enemy(self.mid_enemy_group, self.enemies, 3)
            self.__add_big_enemy(self.big_enemy_group, self.enemies, 2)
            # 小型敌机的速度 + 1
            self.__inc_speed(self.small_enemy_group)
            # 中型敌机的速度 + 1
            self.__inc_speed(self.mid_enemy_group)

        keys_pressed = pygame.key.get_pressed()
        if keys_pressed[pygame.K_w] or keys_pressed[pygame.K_UP]:
            self.hero.moveUp()
        if keys_pressed[pygame.K_s] or keys_pressed[pygame.K_DOWN]:
            self.hero.moveDown()
        if keys_pressed[pygame.K_a] or keys_pressed[pygame.K_LEFT]:
            self.hero.moveLeft()
        if keys_pressed[pygame.K_d] or keys_pressed[pygame.K_RIGHT]:
            self.hero.moveRight()

        if self.bomb_supply.active:
            self.bomb_supply.move()
            # 调用bomb_supply对象的move方法,更新其在屏幕上的位置。
            self.screen.blit(self.bomb_supply.image, self.bomb_supply.rect)
            if pygame.sprite.collide_mask(self.bomb_supply, self.hero):
                # 使用Pygame的collide_mask函数检查bomb_supply和hero对象是否发生碰撞。
                self.get_bomb_sound.play()
                if self.bomb_num < self.bomb_max:
                    # 检查当前拥有的炸弹数量是否小于最大炸弹数量。
                    self.bomb_num += 1
                    # 增加当前拥有的炸弹数量
                    self.bomb_supply.active = False
                    # 将bomb_supply对象的active属性设置为False,表示该供应物已被使用或消耗。

        if self.bullet_supply.active:
            self.bullet_supply.move()
            # 调用bullet_supply对象的move方法,更新其在屏幕上的位置。
            self.screen.blit(self.bullet_supply.image, self.bullet_supply.rect)
            if pygame.sprite.collide_mask(self.bullet_supply, self.hero):
                # 使用Pygame的collide_mask函数检查bullet_supply和hero对象是否发生碰撞。
                self.get_bullet_sound.play()
                # 播放一个声音效果
                pygame.time.set_timer(self.SUPER_BULLET, self.super_bullet_s * 1000)
                # 设置一个定时器,当定时器触发时,它将执行与self.SUPER_BULLET相关的动作。
                # 定时器的触发时间由self.super_bullet_s * 1000 决定,
                # 其中self.super_bullet_s表示秒数,乘以1000是为了将其转换为毫秒。
                self.is_super_bullet = True
                # 将is_super_bullet属性设置为True,表示当前子弹是super。
                self.bullet_supply.active = False
                # 将bullet_supply对象的active属性设置为False,表示该供应物已被使用或消耗

        if self.life_supply.active:
            self.life_supply.move()
            # 调用life_supply对象的move方法,更新其在屏幕上的位置。
            self.screen.blit(self.life_supply.image, self.life_supply.rect)
            if pygame.sprite.collide_mask(self.life_supply, self.hero):
                # 使用Pygame的collide_mask函数检查life_supply和hero对象是否发生碰撞。
                self.get_bomb_sound.play()
                # 播放一个声音效果
                if self.life_num < 3:
                    # 检查当前的生命值数量是否小于3。如果是,则执行以下代码块
                    self.life_num += 1
                    # 增加当前的生命值数量
                    self.life_supply.active = False
                    # 将life_supply对象的active属性设置为False,表示该供应物已被使用或消耗。

    def __checkCollide(self):
        self.enemies_down = pygame.sprite.spritecollide(self.hero, self.enemies, False, pygame.sprite.collide_mask)
        # collide_mask来进行更精确的碰撞检测。
        if self.enemies_down and not self.hero.invincible:
            self.hero.active = False
            # 将英雄的状态设置为非活动状态,英雄被击败
            self.is_super_bullet = False
            # 取消超级子弹的状态
            for e in self.enemies_down:
                e.active = False
                # 将碰撞到的敌人的状态设置为非活动状态,敌人被击败

        for b in self.bullets:
            # 遍历所有的子弹
            if b.active:
                b.move()
                # 调用子弹的move方法,更新子弹的位置
                self.screen.blit(b.image, b.rect)
                # 在屏幕上绘制子弹的图像
                enemy_hit = pygame.sprite.spritecollide(b, self.enemies, False, pygame.sprite.collide_mask)
                # 使用spritecollide函数检测子弹与敌人之间的碰撞。
                if enemy_hit:
                    b.active = False
                    # 将子弹的状态设置为非活动状态,表示已经击中目标
                    for e in enemy_hit:
                        if e not in self.small_enemy_group:
                            # 如果击中的敌人不属于小型敌人组
                            e.hit = True
                            # 设置敌人的hit属性为True,表示敌人被击中
                            e.hp -= 1
                            # 减少敌人的生命值
                            if e.hp == 0:
                                e.active = False
                                # 将敌人的状态设置为非活动状态,表示敌人被击败
                        else:
                            e.active = False
                            # 直接将小型敌人的状态设置为非活动状态,表示敌人被击败。

    def __drawEvents(self):
        if not (self.delay % 10):
            # 每10个单位的时间,这个条件只满足一次
            self.bullet_sound.play()
            # 播放一个子弹的声音效果。
            if self.is_super_bullet:
                self.bullets = self.bullet2
                # 如果处于超级子弹模式,将子弹列表设置为 self.bullet2
                self.bullets[self.bullet2_index].reset((self.hero.rect.centerx - 33, self.hero.rect.centery - 5))
                # 将一个超级子弹重置到英雄的左侧或右侧的一个特定位置。
                self.bullets[self.bullet2_index + 1].reset((self.hero.rect.centerx + 30, self.hero.rect.centery - 5))
                # 重置超级子弹的位置
                self.bullet2_index = (self.bullet2_index + 2) % self.BULLET2_NUM
                # 更新超级子弹的索引,使其在列表中循环。
            else:
                self.bullets = self.bullet1
                self.bullets[self.bullet1_index].reset(self.hero.rect.midtop)
                # 重置普通子弹的位置。
                self.bullet1_index = (self.bullet1_index + 1) % self.BULLET1_NUM
                # 更新普通子弹的索引,使其在列表中循环。

        for each in self.big_enemy_group:
            if each.active:
                # 检查每个敌人对象是否(active)。只有活跃的敌人会被处理
                each.move()
                # 调用每个活跃敌人的move方法,可能是更新敌人的位置或状态。
                if each.hit:
                    # 检查敌人是否被击中
                    self.screen.blit(each.image_hit, each.rect)
                    each.hit = False
                    # 将敌人的hit属性设置为False,表示敌人现在不再是被击中的状态。
                else:
                    if self.switch_image:
                        # 检查一个名为switch_image的属性。这个属性决定使用哪个图像来绘制敌人
                        self.screen.blit(each.image, each.rect)
                    else:
                        self.screen.blit(each.image2, each.rect)
                        # 使用敌人的另一个图像(each.image2)在其当前位置(each.rect)进行绘制

                if each.hp != 20:
                    # 检查每个敌人的生命值(each.hp)是否等于20。
                    pygame.draw.line(self.screen, self.BLACK,
                                     (each.rect.left, each.rect.top - 5),
                                     (each.rect.right, each.rect.top - 5), 2)
                    # 使用 pygame的draw.line方法在屏幕上绘制一条线。
                    # 线的颜色是黑色(self.BLACK),从每个敌人的左边(each.rect.left)到右边(each.rect.right),
                    # 位置在每个敌人的顶部偏移5个像素(each.rect.top - 5)。线的宽度是2个像素。

                    hp_remain = each.hp / BigEnemy.hp
                    if hp_remain > 0.2:
                        hp_color = self.GREEN
                    else:
                        hp_color = self.RED
                    pygame.draw.line(self.screen, hp_color,
                                     (each.rect.left, each.rect.top - 5),
                                     (each.rect.left + each.rect.width * hp_remain, each.rect.top - 5), 2)
                    # 使用pygame的draw.line方法在屏幕上绘制一条线,表示敌人的生命值。
                    # 线的颜色是hp_color,线的起点是每个敌人的左边,终点是敌人宽度的hp_remain倍的位置,线的宽度是2个像素。
                    if each.rect.bottom == -50:
                        self.enemy3_fly_sound.play(-1)
            else:
                if not (self.delay % 5):
                    # 检查 self.delay除以5的余数是否为0。
                    if not self.e3_destroy_index:
                        # 检查self.e3_destroy_index是否为0
                        self.enemy3_down_sound.play()
                        # 播放一个声音效果,可能是敌人被击败或销毁时的声音。
                    self.screen.blit(each.destroy_images[self.e3_destroy_index], each.rect)
                    self.e3_destroy_index = (self.e3_destroy_index + 1) % 6
                    # 更新动画帧的索引。这行代码将self.e3_destroy_index加1,然
                    # 后取模6,这样索引值会在 0到 5之间循环,实现动画帧的循环播放。
                    if not self.e3_destroy_index:
                        # 再次检查 self.e3_destroy_index是否为0。
                        self.enemy3_fly_sound.stop()
                        # 停止播放敌人的飞行声音效果
                        self.score += 10
                        # 将玩家的得分增加10分
                        each.reset()
                        # 重置敌人对象的状态,为了准备下一轮的游戏或让敌人重新出现在屏幕上。

        for each in self.mid_enemy_group:
            if each.active:
                # 检查当前敌人对象是否是活跃的(即是否在游戏界面上)。
                if each.hit:
                    self.screen.blit(each.image_hit, each.rect)
                    each.hit = False
                    # 将each.hit设置为False,表示该敌人对象现在没有被击
                else:
                    each.move()
                    # 调用每个活跃敌人的move方法,可能是更新敌人的位置或状态。
                    self.screen.blit(each.image, each.rect)
                    # 在屏幕上绘制正常的敌人图像
                if each.hp != 8:
                    # 如果敌人的生命值不等于8。
                    pygame.draw.line(self.screen, self.BLACK,
                                     (each.rect.left, each.rect.top - 5),
                                     (each.rect.right, each.rect.top - 5),
                                     2)
                    # 在屏幕上绘制一个黑色的生命值指示线
                    hp_remain = each.hp / MidEnemy.hp
                    # 计算敌人生命值的剩余比例
                    if hp_remain > 0.2:
                        hp_color = self.GREEN
                    else:
                        hp_color = self.RED
                    pygame.draw.line(self.screen, hp_color,
                                     (each.rect.left, each.rect.top - 5),
                                     (each.rect.left + each.rect.width * hp_remain, each.rect.top - 5),
                                     2)
            else:
                if not (self.delay % 5):
                    # 检查self.delay除以5的余数是否为0。
                    # 如果余数为0,那么条件成立,执行下面的代码块。
                    if not self.e2_destroy_index:
                        # 检查self.e2_destroy_index是否为0。
                        self.enemy2_down_sound.play()
                        # 播放一个声音效果,可能是敌人被击败或销毁时的声音
                    self.screen.blit(each.destroy_images[self.e2_destroy_index], each.rect)
                    # 在游戏屏幕上绘制敌人被销毁的动画图像。
                    self.e2_destroy_index = (self.e2_destroy_index + 1) % 4
                    # 更新索引。
                    if not self.e2_destroy_index:
                        # 再次检查self.e2_destroy_index是否为0。
                        self.score += 4
                        # 将玩家的得分增加4分。
                        each.reset()
                        # 重置敌人对象的状态
        for each in self.small_enemy_group:
            # 这是一个循环,遍历self.small_enemy_group中的每一个元素
            if each.active:
                each.move()
                # 调用每个活跃敌人的move方法,可能是更新敌人的位置或状态。
                self.screen.blit(each.image, each.rect)
            else:
                if not (self.delay % 5):
                    # 这是一个条件判断。它检查self.delay除以5的余数是否为0。
                    # 如果余数为0,那么条件成立,执行下面的代码块。
                    if not self.e1_destroy_index:
                        # 一个条件判断。检查self.e1_destroy_index是否为0。
                        # 如果是,执行下面的代码块。
                        self.enemy1_down_sound.play()
                        # 播放一个声音效果,可能是敌人被击败或销毁时的声音
                    self.screen.blit(each.destroy_images[self.e1_destroy_index], each.rect)
                    # 在游戏屏幕上绘制敌人被销毁的动画图像
                    self.e1_destroy_index = (self.e1_destroy_index + 1) % 4
                    # 更新动画帧的索引
                    if not self.e1_destroy_index:
                        # 再次检查self.e1_destroy_index是否为0。
                        self.score += 1
                        # 将玩家的得分增加1分
                        each.reset()
                        # 重置敌人对象的状态
        if self.hero.active:
            if self.switch_image:
                self.screen.blit(self.hero.image, self.hero.rect)
            else:
                self.screen.blit(self.hero.image2, self.hero.rect)
                # 如果self.switch_image为True,则在屏幕上绘制self.hero.image,
                # 否则绘制self.hero.image2。blit方法用于在屏幕上的指定位置绘制图像
        else:
            if not (self.delay % 5):
                # 如果主角不活跃,代码将进入这个else块,处理主角的“毁灭”动画或状态。
                # 它首先检查self.delay是否能被5整除。
                if not self.me_destroy_index:
                    self.me_down_sound.play()
                    # 播放一个音效(主角被摧毁的声音)
                self.screen.blit(self.hero.destroy_images[self.me_destroy_index], each.rect)
                self.me_destroy_index = (self.me_destroy_index + 1) % 4
                # 如果self.me_destroy_index为0,则播放一个音效(可能是主角被摧毁的声音)。
                # 然后,它在屏幕上绘制“毁灭”动画的当前帧,并更新动画帧的索引。
                if not self.me_destroy_index:
                    self.life_num -= 1
                    self.hero.reset()
                    pygame.time.set_timer(self.INVINCIBLE_TIME, 3 * 1000)
                    # 更新生命值和重置主角:当动画结束时(即self.me_destroy_index再次为0),减少生命值,
                    # 重置主角,并设置一个无敌时间(可能是让主角在一段时间内不受伤害)。

        self.score_text = self.score_font.render("Score : {}".format(self.score), True, self.WHITE)
        self.screen.blit(self.score_text, (10, 5))
        # 显示得分,使用字体和颜色渲染得分文本,并在屏幕的指定位置绘制它。
        self.screen.blit(self.paused_image, self.paused_rect)
        # 在屏幕上的指定位置绘制一个暂停图像
        self.bomb_text = self.bomb_font.render("{} / {}".format(self.bomb_num, self.bomb_max), True, self.WHITE)
        self.text_rect = self.bomb_text.get_rect()
        self.screen.blit(self.bomb, (10, SCREEN_RECT.height - 10 - self.bomb_rect.height))
        self.screen.blit(self.bomb_text, (20 + self.bomb_rect.width, SCREEN_RECT.height - 5 - self.text_rect.height))

        if self.life_num:
            for i in range(self.life_num):
                # 循环遍历每个生命数量
                self.screen.blit(self.life_image,
                                 (SCREEN_RECT.width - 50 - (i * 1) * self.life_rect.width,
                                  SCREEN_RECT.height - 10 - self.life_rect.height))
                # 计算图像的x坐标。它从屏幕的右边缘减去50像素,再减去每个生命图像的宽度乘以当前的循环迭代次数(即i的值)。这样,每个生命图像都会从左到右依次排列
                # 计算图像的y坐标。它从屏幕的底部减去10像素,再减去生命图像的高度。这样,生命图像就会出现在屏幕的底部附近。
        if self.supply_notice and not self.supply_notice_end:
            # 是否存在一个补给通知且该通知尚未结束
            self.supply_notice_text = self.supply_notice_font.render("supply time!", True, self.WHITE)
            self.screen.blit(self.supply_notice_text, (SCREEN_RECT.centerx - 40, 0))
            # 将渲染后的补给通知文本绘制到屏幕上,其x坐标为屏幕中心减去40像素,y坐标为0(即屏幕的顶部)。

        if not (self.delay % 5):
            # 这行代码检查self.delay是否能被5整除。如果不能,执行下面的代码块
            self.switch_image = not self.switch_image
            # 切换self.switch_image的布尔值。如果它原来是True,则变为False;
            # 如果原来是False,则变为True。这通常用于在两个图像或状态之间切换
        self.delay -= 1
        # 将self.delay的值减1。这通常用于计时或延迟某些操作
        if not self.delay:
            # 检查self.delay是否为0
            self.delay = 500
            # 重新设置self.delay的值为500,重置或开始一个新的计时周期


if __name__ == "__main__":
    game = PlaneGame()
    print("这里是游戏后台")
    try:
        game.startGame()
        # 调用了game对象的startGame方法启动游戏。
    except SystemExit:
        # 这种异常通常是由于调用了sys.exit()而引发的,用于退出程序。
        # 如果发生了SystemExit异常,
        # 程序会静默地忽略它,不会输出任何信息也不会终止程序。
        pass
    except Exception as problem:
        # 用于捕获所有其他类型的异常。
        print("未知错误:{}".format(problem))

源码放到了我的网盘:飞机大战源码

因为有一些音效、图片不想放在blog里了。

你可能感兴趣的:(python,课程设计,python,pygame,开发语言)