• Python飞机大战项目终篇(一步一步实现---最全笔记)


    前一篇博客为游戏实现前所用的基础知识介绍
    Python飞机大战项目前篇
    此篇为飞机大战游戏项目的整个实现过程。从游戏框架的搭建、游戏背景的设置、英雄飞机和敌机的设定,再到飞机发生碰撞时的检测(子弹摧毁敌机,敌机撞毁英雄)等详细的笔记描述

    游戏框架搭建

    在这里插入图片描述游戏初始化 —— __init__() 会调用以下方法:

    方法职责
    __create_sprites(self)创建所有精灵和精灵组

    游戏循环 —— start_game() 会调用以下方法:

    方法职责
    __event_handler(self)事件监听
    __check_collide(self)碰撞检测 —— 子弹销毁敌机、敌机撞毁英雄
    __update_sprites(self)精灵组更新和绘制
    __game_over()游戏结束

    新建plane_main.py文件和plane_sprites.py文件记得导入图片素材(如图)
    在这里插入图片描述
    plane_main

    1. 封装 主游戏类
    2. 创建 游戏对象
    3. 启动游戏
      plane_sprites
    • 封装游戏中 所有 需要使用的 精灵子类
    • 提供游戏的 相关工具

    游戏背景滚动实现

    背景图像的显示效果:

    • 游戏启动后,背景图像连续不断地 向下方 移动
    • 视觉上 产生英雄的飞机不断向上方飞行的 错觉 —— 在很多跑酷类游戏中常用的套路
      • 游戏的背景 不断变化
      • 游戏的主角 位置保持不变

    实现办法:

    1. 创建两张背景图像精灵
      • 1完全和屏幕重合
      • 2 张在 屏幕的正上方
    2. 两张图像 一起向下方运动
      • self.rect.y += self.speed
    3. 任意背景精灵rect.y >= 屏幕的高度 说明已经 移动到屏幕下方
    4. 移动到屏幕下方的这张图像 设置到 屏幕的正上方
      • rect.y = -rect.height

    设计背景类

    在这里插入图片描述* 初始化方法

    • 直接指定 背景图片
    • is_alt 判断是否是另一张图像
      • False 表示 第一张图像,需要与屏幕重合
      • True 表示 另一张图像,在屏幕的正上方
    • update() 方法
      • 判断 是否移动出屏幕,如果是,将图像设置到 屏幕的正上方,从而实现 交替滚动

    继承 如果父类提供的方法,不能满足子类的需求:

    • 派生一个子类
    • 在子类中针对特有的需求,重写父类方法,并且进行扩展

    plane_sprites 新建 Background 继承自 GameSprite

    class Background(GameSprite):
        """游戏背景精灵"""
    
        def update(self):
    
            # 1. 调用父类的方法实现
            super().update()
    
            # 2. 判断是否移出屏幕,如果移出屏幕,将图像设置到屏幕的上方
            if self.rect.y >= SCREEN_RECT.height:
                self.rect.y = -self.rect.height
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    plane_main.py 中显示背景精灵

    1. __create_sprites 方法中创建 精灵精灵组
    2. __update_sprites 方法中,让 精灵组 调用 update()draw() 方法

    __create_sprites 方法

    def __create_sprites(self):
    
        # 创建背景精灵和精灵组
        bg1 = Background("./images/background.png")
        bg2 = Background("./images/background.png")
        bg2.rect.y = -bg2.rect.height
        
        self.back_group = pygame.sprite.Group(bg1, bg2)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    __update_sprites 方法

    def __update_sprites(self):
    
        self.back_group.update()
        self.back_group.draw(self.screen)
    
    • 1
    • 2
    • 3
    • 4

    完整实现代码

    plane_main.py部分

    import pygame
    from plane_sprites import *
    
    
    class PlaneGame(object):
        """飞机大战主游戏"""
    
        def __init__(self):
            print('游戏初始化')
    
            # 1 创建游戏的窗口
            self.screen = pygame.display.set_mode(SCREEN_RECT.size)
            # 2 创建游戏的时钟
            self.clock = pygame.time.Clock()
            # 3 调用私有方法,精灵和精灵组的创建
            self.__create_spritea()
    
        def __create_spritea(self):
            # 创建背景精灵和精灵组
            bg1 = Background()
            bg2 = Background(True)
            # bg2.rect.y = -bg2.rect.height   # 定义初始位置
    
            self.back_group = pygame.sprite.Group(bg1, bg2)
    
        def start_game(self):
            print('游戏开始...')
    
            while True:
                # 1 设置刷新帧率
                self.clock.tick(FRAME_PER_SEC)
                # 2 事件监听
                self.__event_handler()
                # 3 碰撞检测
                self.__check_collide()
                # 4 更新/绘制精灵组
                self.__update_sprites()
                # 5 更新显示
                pygame.display.update()
    
        def __event_handler(self):
            for event in pygame.event.get():
                # 判断是否退出游戏
                if event.type == pygame.QUIT:
                    PlaneGame.__game_over()
    
        def __check_collide(self):
            pass
    
        def __update_sprites(self):
    
            self.back_group.update()
            self.back_group.draw(self.screen)
    
    
    if __name__ == '__main__':
        # 创建游戏对象
        game = PlaneGame()
        # 启动游戏
        game.start_game()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    plane_sprites.py部分

    import pygame
    
    # 屏幕大小的常量
    SCREEN_RECT = pygame.Rect(0, 0, 480, 700)
    # 刷新的帧率
    FRAME_PER_SEC = 60
    # 创建敌机的定时器常量(USEREVENT  是pygame提供的用户事件)
    CREATE_ENEMY_EVENT = pygame.USEREVENT
    
    
    class GameSprite(pygame.sprite.Sprite):  # 第一个是模块的名称 第二个是类的名称
        """ 飞机大战游戏精灵"""
    
        def __init__(self, image_name, speed=1):
    
            # 调用父类的初始化方法
            super().__init__()
    
            # 定义对象的属性
            self.image = pygame.image.load(image_name)
            self.rect = self.image.get_rect()
            self.speed = speed
    
        def update(self):
    
            # 在屏幕的垂直方向上移动
            self.rect.y += self.speed
    
    
    class Background(GameSprite):
        """ 游戏背景精灵"""
    
        def __init__(self, is_alt=False):
    
            # 1 调用父类方法实现精灵组的创建(image/rect/speed)
            super().__init__('./images/background.png')
    
            # 2 判断是否是交替图像,如果是,需要设置初始位置
            if is_alt:
                self.rect.y = -self.rect.height
    
        def update(self):
    
            # 1 调用父类的方法实现
            super().update()
    
            # 2 判断是否移出屏幕,如果移出屏幕, 将图像设置到屏幕上方
            if self.rect.y >= SCREEN_RECT.height:
                self.rect.y = -self.rect.height
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    实现效果图

    在这里插入图片描述

    敌机部分实现

    敌机的 出现规律

    1. 游戏启动后,每隔 1 秒 会出现一架敌机
    2. 每架敌机 向屏幕下方飞行,飞行速度各不相同
    3. 每架敌机出现的 水平位置 也不尽相同
    4. 当敌机 从屏幕下方飞出,不会再飞回到屏幕中

    定义并监听创建敌机的定时器事件

    • pygame 中可以使用 pygame.time.set_timer() 来添加 定时器
    • 所谓 定时器,就是 每隔一段时间,去 执行一些动作
    set_timer(eventid, milliseconds) -> None
    
    • 1
    • set_timer 可以创建一个事件
    • 可以在游戏循环的事件监听方法中捕获到该事件
    • 第 1 个参数事件代号需要基于常量 pygame.USEREVENT 来指定
      • USEREVENT 是一个整数,再增加的事件可以使用 USEREVENT + 1 指定,依次类推…
    • 第 2 个参数是事件触发间隔的毫秒值
      pygame 的定时器使用套路非常固定:
    1. 定义定时器常量 —— eventid
    2. 在 初始化方法中,调用 set_timer 方法设置定时器事件
    3. 在 游戏循环 中,监听定时器事件

    设计 Enemy

    1. 游戏启动后,每隔 1 秒会 出现一架敌机
    2. 每架敌机 向屏幕下方飞行,飞行速度各不相同
    3. 每架敌机出现的水平位置也不尽相同
    4. 当敌机从屏幕下方飞出,不会再飞回到屏幕中

    敌机类的准备

    • plane_sprites 新建 Enemy 继承自 GameSprite
    • 重写 初始化方法,直接指定 图片名称
    • 暂时 不实现 随机速度 和 随机位置 的指定
    • 重写 update 方法,判断是否飞出屏幕

    创建敌机

    1. __create_sprites,添加 敌机精灵组
      • 敌机是 定时被创建的,因此在初始化方法中,不需要创建敌机
    2. __event_handler,创建敌机,并且 添加到精灵组
      • 调用 精灵组 的 add 方法可以 向精灵组添加精灵
    3. __update_sprites,让 敌机精灵组 调用 updatedraw 方法

    随机敌机位置和速度
    修改 plane_sprites.py 增加 random 的导入

    import random
    
    • 1

    随机位置:使用 pygame.Rect 提供的 bottom 属性,在指定敌机初始位置时,会比较方便

    • bottom = y + height
    • y = bottom - height

    移出屏幕销毁敌机

    • 敌机移出屏幕之后,如果 没有撞到英雄,敌机的历史使命已经终结

    • 需要从 敌机组 删除,否则会造成 内存浪费

    • __del__ 内置方法会在对象被销毁前调用,在开发中,可以用于 判断对象是否被销毁

    • 判断敌机是否飞出屏幕,如果是,调用 kill() 方法从所有组中删除

    完整实现代码

    plane_main.py部分

    import pygame
    from plane_sprites import *
    
    
    class PlaneGame(object):
        """飞机大战主游戏"""
    
        def __init__(self):
            print("游戏初始化")
    
            # 1. 创建游戏的窗口
            self.screen = pygame.display.set_mode(SCREEN_RECT.size)
            # 2. 创建游戏的时钟
            self.clock = pygame.time.Clock()
            # 3. 调用私有方法,精灵和精灵组的创建
            self.__create_sprites()
    
            # 4. 设置定时器事件 - 创建敌机 1s
            pygame.time.set_timer(CREATE_ENEMY_EVENT, 1000)
    
        def __create_sprites(self):
    
            # 创建背景精灵和精灵组
            bg1 = Background()
            bg2 = Background(True)
    
            self.back_group = pygame.sprite.Group(bg1, bg2)
    
            # 创建敌机的精灵组
            self.enemy_group = pygame.sprite.Group()
    
        def start_game(self):
            print("游戏开始...")
    
            while True:
                # 1. 设置刷新帧率
                self.clock.tick(FRAME_PER_SEC)
                # 2. 事件监听
                self.__event_handler()
                # 3. 碰撞检测
                self.__check_collide()
                # 4. 更新/绘制精灵组
                self.__update_sprites()
                # 5. 更新显示
                pygame.display.update()
    
        def __event_handler(self):
    
            for event in pygame.event.get():
    
                # 判断是否退出游戏
                if event.type == pygame.QUIT:
                    PlaneGame.__game_over()
                elif event.type == CREATE_ENEMY_EVENT:
                    print("敌机出场...")
                    # 创建敌机精灵
                    enemy = Enemy()
    
                    # 将敌机精灵添加到敌机精灵组
                    self.enemy_group.add(enemy)
    
        def __check_collide(self):
            pass
    
        def __update_sprites(self):
    
            self.back_group.update()
            self.back_group.draw(self.screen)
    
            self.enemy_group.update()
            self.enemy_group.draw(self.screen)
    
        @staticmethod
        def __game_over():
            print("游戏结束")
    
            pygame.quit()
            exit()
    
    if __name__ == '__main__':
    
        # 创建游戏对象
        game = PlaneGame()
    
        # 启动游戏
        game.start_game()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87

    plane_sprites.py部分

    import random
    import pygame
    
    # 屏幕大小的常量
    SCREEN_RECT = pygame.Rect(0, 0, 480, 700)
    # 刷新的帧率
    FRAME_PER_SEC = 60
    # 创建敌机的定时器常量
    CREATE_ENEMY_EVENT = pygame.USEREVENT
    
    
    class GameSprite(pygame.sprite.Sprite):
        """飞机大战游戏精灵"""
    
        def __init__(self, image_name, speed=1):
    
            # 调用父类的初始化方法
            super().__init__()
    
            # 定义对象的属性
            self.image = pygame.image.load(image_name)
            self.rect = self.image.get_rect()
            self.speed = speed
    
        def update(self):
    
            # 在屏幕的垂直方向上移动
            self.rect.y += self.speed
    
    
    class Background(GameSprite):
        """游戏背景精灵"""
    
        def __init__(self, is_alt=False):
    
            # 1. 调用父类方法实现精灵的创建(image/rect/speed)
            super().__init__("./images/background.png")
    
            # 2. 判断是否是交替图像,如果是,需要设置初始位置
            if is_alt:
                self.rect.y = -self.rect.height
    
        def update(self):
    
            # 1. 调用父类的方法实现
            super().update()
    
            # 2. 判断是否移出屏幕,如果移出屏幕,将图像设置到屏幕的上方
            if self.rect.y >= SCREEN_RECT.height:
                self.rect.y = -self.rect.height
    
    
    class Enemy(GameSprite):
        """敌机精灵"""
    
        def __init__(self):
    
            # 1. 调用父类方法,创建敌机精灵,同时指定敌机图片
            super().__init__("./images/enemy1.png")
    
            # 2. 指定敌机的初始随机速度 1 ~ 3
            self.speed = random.randint(1, 3)
    
            # 3. 指定敌机的初始随机位置
            self.rect.bottom = 0
    
            max_x = SCREEN_RECT.width - self.rect.width
            self.rect.x = random.randint(0, max_x)
    
        def update(self):
    
            # 1. 调用父类方法,保持垂直方向的飞行
            super().update()
    
            # 2. 判断是否飞出屏幕,如果是,需要从精灵组删除敌机
            if self.rect.y >= SCREEN_RECT.height:
                print("飞出屏幕,需要从精灵组删除...")
                # kill方法可以将精灵从所有精灵组中移出,精灵就会被自动销毁
                self.kill()
    
        def __del__(self):
            print("敌机挂了 %s" % self.rect)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83

    实现效果图

    在这里插入图片描述

    英雄部分实现

    英雄需求

    1. 游戏启动后,英雄 出现在屏幕的 水平中间 位置,距离 屏幕底部120 像素
    2. 英雄 每隔 0.5 秒发射一次子弹,每次 连发三枚子弹
    3. 英雄 默认不会移动,需要通过 左/右 方向键,控制 英雄 在水平方向移动

    子弹需求

    1. 子弹 从 英雄 的正上方发射沿直线向 上方飞行
    2. 飞出屏幕后,需要从 精灵组中删除

    Hero —— 英雄

    • 初始化方法
      • 指定 英雄图片
      • 初始速度 = 0 —— 英雄默认静止不动
      • 定义 bullets 子弹精灵组保存子弹精灵
    • 重写 update()方法
      • 英雄需要 水平移动
      • 并且需要保证不能移出屏幕
    • 增加 bullets 属性,记录所有子弹精灵
    • 增加 fire 方法,用于发射子弹

    创建英雄

    • plane_sprites 新建 Hero
    • 重写 初始化方法,直接指定图片名称,并且将初始速度设置为 0
    • 设置 英雄的初始位置
    • centerx = x + 0.5 * width
    • centery = y + 0.5 * height
    • bottom = y + height
      绘制英雄
    1. __create_sprites,添加 英雄精灵 和 英雄精灵组
      • 后续要针对 英雄 做 碰撞检测 以及 发射子弹
      • 所以 英雄 需要 单独定义成属性
    2. __update_sprites,让 英雄精灵组 调用 updatedraw 方法
      移动英雄位置
    3. Hero 类中重写 update 方法
      • 用 速度 speed 和 英雄 rect.x 进行叠加
      • 不需要调用父类方法 —— 父类方法只是实现了单纯的垂直运动
    4. __event_handler 方法中根据 左右方向键 设置英雄的 速度
      • 向右 => speed = 2
      • 向左 => speed = -2
      • 其他 => speed = 0

    Bullet —— 子弹

    • 初始化方法
      • 指定子弹图片
      • 初始速度 = -2 —— 子弹需要向上方飞行
    • 重写update()方法
      • 判断 是否飞出屏幕,如果是,从精灵组删除

    发射子弹
    pygame定时器 使用套路非常固定:

    1. 定义 定时器常量 —— eventid
    2. 在 初始化方法 中,调用 set_timer 方法 设置定时器事件
    3. 在 游戏循环 中,监听定时器事件
    • Hero 中定义 fire 方法
    def fire(self):
        print("发射子弹...")
    
    • 1
    • 2
    • plane_main.py 的顶部定义 发射子弹 事件常量
    # 英雄发射子弹事件
    HERO_FIRE_EVENT = pygame.USEREVENT + 1
    
    • 1
    • 2
    • __init__ 方法末尾中添加 发射子弹 事件
    # 每隔 0.5 秒发射一次子弹
    pygame.time.set_timer(HERO_FIRE_EVENT, 500)
    
    • 1
    • 2
    • __event_handler 方法中让英雄发射子弹
    elif event.type == HERO_FIRE_EVENT:
        self.hero.fire()
    
    • 1
    • 2

    定义子弹类

    • plane_sprites 新建 Bullet 继承自 GameSprite
    • 重写 初始化方法,直接指定 图片名称,并且设置 初始速度
    • 重写 update() 方法,判断子弹 飞出屏幕从精灵组删除

    发射子弹

    1. Hero 的 初始化方法 中创建 子弹精灵组 属性
    2. 修改 plane_main.py__update_sprites 方法,让 子弹精灵组 调用 updatedraw 方法
    3. 实现 fire() 方法
      • 创建子弹精灵
      • 设置初始位置 —— 在 英雄的正上方
      • 将 子弹 添加到精灵组

    完整实现代码

    plane_main.py部分

    import pygame
    from plane_sprites import *
    
    
    class PlaneGame(object):
        """飞机大战主游戏"""
    
        def __init__(self):
            print("游戏初始化")
    
            # 1. 创建游戏的窗口
            self.screen = pygame.display.set_mode(SCREEN_RECT.size)
            # 2. 创建游戏的时钟
            self.clock = pygame.time.Clock()
            # 3. 调用私有方法,精灵和精灵组的创建
            self.__create_sprites()
    
            # 4. 设置定时器事件 - 创建敌机 1s
            pygame.time.set_timer(CREATE_ENEMY_EVENT, 1000)
            pygame.time.set_timer(HERO_FIRE_EVENT, 500)
    
        def __create_sprites(self):
    
            # 创建背景精灵和精灵组
            bg1 = Background()
            bg2 = Background(True)
    
            self.back_group = pygame.sprite.Group(bg1, bg2)
    
            # 创建敌机的精灵组
            self.enemy_group = pygame.sprite.Group()
    
            # 创建英雄的精灵和精灵组
            self.hero = Hero()
            self.hero_group = pygame.sprite.Group(self.hero)
    
        def start_game(self):
            print("游戏开始...")
    
            while True:
                # 1. 设置刷新帧率
                self.clock.tick(FRAME_PER_SEC)
                # 2. 事件监听
                self.__event_handler()
                # 3. 碰撞检测
                self.__check_collide()
                # 4. 更新/绘制精灵组
                self.__update_sprites()
                # 5. 更新显示
                pygame.display.update()
    
        def __event_handler(self):
    
            for event in pygame.event.get():
    
                # 判断是否退出游戏
                if event.type == pygame.QUIT:
                    PlaneGame.__game_over()
                elif event.type == CREATE_ENEMY_EVENT:
                    # print("敌机出场...")
                    # 创建敌机精灵
                    enemy = Enemy()
    
                    # 将敌机精灵添加到敌机精灵组
                    self.enemy_group.add(enemy)
                elif event.type == HERO_FIRE_EVENT:
                    self.hero.fire()
                # elif event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT:
                #     print("向右移动...")
    
            # 使用键盘提供的方法获取键盘按键 - 按键元组
            keys_pressed = pygame.key.get_pressed()
            # 判断元组中对应的按键索引值 1
            if keys_pressed[pygame.K_RIGHT]:
                self.hero.speed = 2
            elif keys_pressed[pygame.K_LEFT]:
                self.hero.speed = -2
            else:
                self.hero.speed = 0
    
        def __check_collide(self):
            pass
    
        def __update_sprites(self):
    
            self.back_group.update()
            self.back_group.draw(self.screen)
    
            self.enemy_group.update()
            self.enemy_group.draw(self.screen)
    
            self.hero_group.update()
            self.hero_group.draw(self.screen)
    
            self.hero.bullets.update()
            self.hero.bullets.draw(self.screen)
    
        @staticmethod
        def __game_over():
            print("游戏结束")
    
            pygame.quit()
            exit()
    
    if __name__ == '__main__':
    
        # 创建游戏对象
        game = PlaneGame()
    
        # 启动游戏
        game.start_game()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112

    plane_sprites.py部分

    import random
    import pygame
    
    # 屏幕大小的常量
    SCREEN_RECT = pygame.Rect(0, 0, 480, 700)
    # 刷新的帧率
    FRAME_PER_SEC = 60
    # 创建敌机的定时器常量
    CREATE_ENEMY_EVENT = pygame.USEREVENT
    # 英雄发射子弹事件
    HERO_FIRE_EVENT = pygame.USEREVENT + 1
    
    
    class GameSprite(pygame.sprite.Sprite):
        """飞机大战游戏精灵"""
    
        def __init__(self, image_name, speed=1):
    
            # 调用父类的初始化方法
            super().__init__()
    
            # 定义对象的属性
            self.image = pygame.image.load(image_name)
            self.rect = self.image.get_rect()
            self.speed = speed
    
        def update(self):
    
            # 在屏幕的垂直方向上移动
            self.rect.y += self.speed
    
    
    class Background(GameSprite):
        """游戏背景精灵"""
    
        def __init__(self, is_alt=False):
    
            # 1. 调用父类方法实现精灵的创建(image/rect/speed)
            super().__init__("./images/background.png")
    
            # 2. 判断是否是交替图像,如果是,需要设置初始位置
            if is_alt:
                self.rect.y = -self.rect.height
    
        def update(self):
    
            # 1. 调用父类的方法实现
            super().update()
    
            # 2. 判断是否移出屏幕,如果移出屏幕,将图像设置到屏幕的上方
            if self.rect.y >= SCREEN_RECT.height:
                self.rect.y = -self.rect.height
    
    
    class Enemy(GameSprite):
        """敌机精灵"""
    
        def __init__(self):
    
            # 1. 调用父类方法,创建敌机精灵,同时指定敌机图片
            super().__init__("./images/enemy1.png")
    
            # 2. 指定敌机的初始随机速度 1 ~ 3
            self.speed = random.randint(1, 3)
    
            # 3. 指定敌机的初始随机位置
            self.rect.bottom = 0
    
            max_x = SCREEN_RECT.width - self.rect.width
            self.rect.x = random.randint(0, max_x)
    
        def update(self):
    
            # 1. 调用父类方法,保持垂直方向的飞行
            super().update()
    
            # 2. 判断是否飞出屏幕,如果是,需要从精灵组删除敌机
            if self.rect.y >= SCREEN_RECT.height:
                # print("飞出屏幕,需要从精灵组删除...")
                # kill方法可以将精灵从所有精灵组中移出,精灵就会被自动销毁
                self.kill()
    
        def __del__(self):
            # print("敌机挂了 %s" % self.rect)
            pass
    
    
    class Hero(GameSprite):
        """英雄精灵"""
    
        def __init__(self):
    
            # 1. 调用父类方法,设置image&speed
            super().__init__("./images/me1.png", 0)
    
            # 2. 设置英雄的初始位置
            self.rect.centerx = SCREEN_RECT.centerx
            self.rect.bottom = SCREEN_RECT.bottom - 120
    
            # 3. 创建子弹的精灵组
            self.bullets = pygame.sprite.Group()
    
        def update(self):
    
            # 英雄在水平方向移动
            self.rect.x += self.speed
    
            # 控制英雄不能离开屏幕
            if self.rect.x < 0:
                self.rect.x = 0
            elif self.rect.right > SCREEN_RECT.right:
                self.rect.right = SCREEN_RECT.right
    
        def fire(self):
            print("发射子弹...")
    
            for i in (0, 1, 2):
                # 1. 创建子弹精灵
                bullet = Bullet()
    
                # 2. 设置精灵的位置
                bullet.rect.bottom = self.rect.y - i * 20
                bullet.rect.centerx = self.rect.centerx
    
                # 3. 将精灵添加到精灵组
                self.bullets.add(bullet)
    
    
    class Bullet(GameSprite):
        """子弹精灵"""
    
        def __init__(self):
    
            # 调用父类方法,设置子弹图片,设置初始速度
            super().__init__("./images/bullet1.png", -2)
    
        def update(self):
    
            # 调用父类方法,让子弹沿垂直方向飞行
            super().update()
    
            # 判断子弹是否飞出屏幕
            if self.rect.bottom < 0:
                self.kill()
    
        def __del__(self):
            print("子弹被销毁...")
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148

    实现效果图

    在这里插入图片描述

    碰撞检测

    pygame 提供了 两个非常方便 的方法可以实现碰撞检测:

    pygame.sprite.groupcollide()

    • 两个精灵组 中 所有的精灵 的碰撞检测
    groupcollide(group1, group2, dokill1, dokill2, collided = None) -> Sprite_dict
    
    • 1
    • 如果将 dokill 设置为 True,则 发生碰撞的精灵将被自动移除
    • collided 参数是用于 计算碰撞的回调函数
      • 如果没有指定,则每个精灵必须有一个 rect 属性

    pygame.sprite.spritecollide()

    • 判断 某个精灵 和 指定精灵组 中的精灵的碰撞
    spritecollide(sprite, group, dokill, collided = None) -> Sprite_list
    
    • 1
    • 如果将 dokill 设置为 True,则 指定精灵组 中 发生碰撞的精灵将被自动移除
    • collided 参数是用于 计算碰撞的回调函数
      • 如果没有指定,则每个精灵必须有一个 rect 属性
    • 返回 精灵组 中跟 精灵 发生碰撞的 精灵列表

    完整实现代码

    plane_main.py部分

    import pygame
    from plane_sprites import *
    
    # 敌机出现事件
    CREATE_ENEMY_EVENT = pygame.USEREVENT
    # 发射子弹事件
    HERO_FIRE_EVENT = pygame.USEREVENT + 1
    
    
    class PlaneGame(object):
        """飞机大战游戏类"""
    
        def __init__(self):
    
            # 1. pygame 初始化
            pygame.init()
    
            # 2. 创建游戏屏幕
            self.screen = pygame.display.set_mode(SCREEN_RECT.size)
    
            # 3. 创建游戏时钟
            self.clock = pygame.time.Clock()
    
            # 4. 创建精灵组
            self.__create_sprites()
    
            # 5. 创建用户事件
            PlaneGame.__create_user_events()
    
        def __create_sprites(self):
            """创建精灵组"""
    
            # 背景组
            bg1 = Background()
            bg2 = Background(True)
            self.back_group = pygame.sprite.Group(bg1, bg2)
    
            # 敌机组
            enemy = Enemy()
            self.enemy_group = pygame.sprite.Group(enemy)
    
            # 英雄组
            self.hero = Hero()
            self.hero_group = pygame.sprite.Group(self.hero)
    
        @staticmethod
        def __create_user_events():
            """创建用户事件"""
    
            # 每秒添加一架敌机
            pygame.time.set_timer(CREATE_ENEMY_EVENT, 1 * 1000)
    
            # 每秒发射两次子弹
            pygame.time.set_timer(HERO_FIRE_EVENT, 500)
    
        def start_game(self):
            """开始游戏"""
            while True:
                # 1. 设置刷新帧率
                self.clock.tick(60)
                # 2. 事件监听
                self.__event_handler()
                # 3. 更新精灵组
                self.__update_sprites()
                # 碰撞检测
                self.__check_collide()
                # 4. 更新屏幕显示
                pygame.display.update()
    
        def __event_handler(self):
            """事件监听"""
    
            for event in pygame.event.get():
    
                if event.type == pygame.QUIT:
                    print("退出游戏...")
                    pygame.quit()
                    exit()
                elif event.type == CREATE_ENEMY_EVENT:
                    # 创建敌机,并且添加到敌机组
                    self.enemy_group.add(Enemy())
    
                    # 测试敌机精灵数量
                    # enemy_count = len(self.enemy_group.sprites())
                    # print("敌机精灵数量 %d" % enemy_count)
                elif event.type == HERO_FIRE_EVENT:
                    # 英雄发射子弹
                    self.hero.fire()
    
            # 通过 pygame.key 获取用户按键
            keys_pressed = pygame.key.get_pressed()
            dir = keys_pressed[pygame.K_RIGHT] - keys_pressed[pygame.K_LEFT]
    
            # 根据移动方向设置英雄的速度
            self.hero.speed = dir * 2
    
        def __update_sprites(self):
            """更新精灵组"""
    
            for group in [self.back_group, self.enemy_group,
                          self.hero_group, self.hero.bullets]:
    
                group.update()
                group.draw(self.screen)
    
        def __check_collide(self):
            """碰撞检测"""
    
            # 1. 子弹摧毁敌机
            pygame.sprite.groupcollide(self.hero.bullets, self.enemy_group, True, True)
    
            # 2. 英雄被撞毁
            collide_list = pygame.sprite.spritecollide(self.hero, self.enemy_group, False)
    
            if len(collide_list) > 0:
                self.hero.is_alive = False
    
                print("英雄牺牲...")
    
                pygame.quit()
                exit()
    
    if __name__ == '__main__':
        # 1. 创建游戏对象
        game = PlaneGame()
    
        # 2. 开始游戏
        game.start_game()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129

    plane_sprites.py部分

    import random
    import pygame
    
    
    # 游戏屏幕大小
    SCREEN_RECT = pygame.Rect(0, 0, 480, 700)
    
    
    class GameSprite(pygame.sprite.Sprite):
        """游戏精灵基类"""
    
        def __init__(self, image_name, speed=1):
    
            # 调用父类的初始化方法
            super().__init__()
    
            # 加载图像
            self.image = pygame.image.load(image_name)
            # 设置尺寸
            self.rect = self.image.get_rect()
            # 记录速度
            self.speed = speed
    
        def update(self, *args):
    
            # 默认在垂直方向移动
            self.rect.top += self.speed
    
    
    class Background(GameSprite):
        """背景精灵"""
    
        def __init__(self, is_alt=False):
    
            image_name = "./images/background.png"
            super().__init__(image_name)
    
            # 判断是否交替图片,如果是,将图片设置到屏幕顶部
            if is_alt:
                self.rect.bottom = 0
    
        def update(self, *args):
    
            # 调用父类方法
            super().update(args)
    
            # 判断是否超出屏幕
            if self.rect.top >= SCREEN_RECT.height:
                self.rect.bottom = 0
    
    
    class Enemy(GameSprite):
        """敌机精灵"""
    
        def __init__(self):
    
            image_name = "./images/enemy1.png"
            super().__init__(image_name)
    
            # 随机敌机出现位置
            width = SCREEN_RECT.width - self.rect.width
            self.rect.left = random.randint(0, width)
            self.rect.bottom = 0
    
            # 随机速度
            self.speed = random.randint(1, 3)
    
        def update(self, *args):
            super().update(args)
    
            # 判断敌机是否移出屏幕
            if self.rect.top >= SCREEN_RECT.height:
                # 将精灵从所有组中删除
                self.kill()
    
    
    class Hero(GameSprite):
        """英雄精灵"""
    
        def __init__(self):
    
            image_name = "./images/me1.png"
            super().__init__(image_name, 0)
    
            # 设置初始位置
            self.rect.centerx = SCREEN_RECT.centerx
            self.rect.bottom = SCREEN_RECT.bottom - 120
    
            # 创建子弹组
            self.bullets = pygame.sprite.Group()
    
        def update(self, *args):
    
            # 飞机水平移动
            self.rect.left += self.speed
    
            # 超出屏幕检测
            if self.rect.left < 0:
                self.rect.left = 0
            if self.rect.right > SCREEN_RECT.right:
                self.rect.right = SCREEN_RECT.right
    
        def fire(self):
    
            # bullet_count = len(self.bullets.sprites())
            # print("子弹数量 %d" % bullet_count)
    
            for i in range(0, 3):
                # 创建子弹精灵
                bullet = Bullet()
    
                # 设置子弹位置
                bullet.rect.bottom = self.rect.top - i * 20
                bullet.rect.centerx = self.rect.centerx
    
                # 将子弹添加到精灵组
                self.bullets.add(bullet)
    
    
    class Bullet(GameSprite):
        """子弹精灵"""
    
        def __init__(self):
    
            image_name = "./images/bullet1.png"
            super().__init__(image_name, -2)
    
        def update(self, *args):
    
            super().update(args)
    
            # 判断是否超出屏幕
            if self.rect.bottom < 0:
                self.kill()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135

    实现效果图

    在这里插入图片描述

    **简单的的一个飞机大战小游戏就实现了!
    但是这太简略了
    比如:游戏前没有设置开始页面,代码一运行就直接开始游戏
    再比如:游戏中没有吸引人的爆炸效果,没有分数的统计,没有实现飞机的升级以及随着英雄的升级还有敌机的升级,直至游戏的难度升级。
    再再比如:游戏后的结束页面,最高分的排名记录等功能。
    有兴趣的同学可以尝试再整整!

    又添加了一个爆炸效果的功能

    更新2.0版

    完整实现代码

    plane_main.py部分

    #! /usr/bin/python3
    
    import pygame
    from plane_sprites import *
    
    # 敌机出现事件
    CREATE_ENEMY_EVENT = pygame.USEREVENT
    # 发射子弹事件
    HERO_FIRE_EVENT = pygame.USEREVENT + 1
    
    
    class PlaneGame:
        """飞机大战游戏类"""
    
        def __init__(self):
            print("游戏初始化...")
    
            pygame.init()
    
            self.screen = pygame.display.set_mode(SCREEN_RECT.size)
            self.clock = pygame.time.Clock()
    
            self.__create_sprites()
            PlaneGame.__create_user_events()
    
        def __create_sprites(self):
            """创建精灵组"""
    
            self.back_group = pygame.sprite.Group(Background(), Background(True))
    
            self.hero = Hero()
            self.hero_group = pygame.sprite.Group(self.hero)
    
            self.enemy_group = pygame.sprite.Group()
            self.destroy_group = pygame.sprite.Group()
    
        @staticmethod
        def __create_user_events():
            """创建用户事件"""
    
            # 每秒添加一架敌机
            pygame.time.set_timer(CREATE_ENEMY_EVENT, 1 * 1000)
    
            # 每秒发射两次子弹
            pygame.time.set_timer(HERO_FIRE_EVENT, 500)
    
        def start_game(self):
            """开启游戏循环"""
    
            while True:
                self.clock.tick(60)
                self.__event_handler()
                self.__update_sprites()
                self.__check_collide()
                pygame.display.update()
    
        def __check_collide(self):
            """碰撞检测"""
    
            # 子弹摧毁敌机
            enemies = pygame.sprite.groupcollide(self.enemy_group,
                                                 self.hero.bullets,
                                                 False,
                                                 True).keys()
            for enemy in enemies:
                enemy.life -= 1
    
                if enemy.life <= 0:
                    enemy.add(self.destroy_group)
                    enemy.remove(self.enemy_group)
    
                    enemy.destroied()
    
            # 敌机撞毁英雄
            for hero in pygame.sprite.spritecollide(self.hero,
                                                    self.enemy_group,
                                                    True):
                print("英雄牺牲了...")
                self.hero.destroied()
    
        def __event_handler(self):
            """事件处理"""
    
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    PlaneGame.__finished_game()
                elif event.type == HERO_FIRE_EVENT:
                    self.hero.fire()
                elif event.type == CREATE_ENEMY_EVENT:
                    self.enemy_group.add(Enemy())
                # 按下 b 英雄自爆
                elif event.type == pygame.KEYDOWN and event.key == pygame.K_b:
                    # self.hero.destroied()
    
                    # 集体自爆
                    for enemy in self.enemy_group.sprites():
                        enemy.destroied()
    
            # 判断英雄是否已经被销毁,如果是,游戏结束!
            if self.hero.can_destroied:
                PlaneGame.__finished_game()
    
            # 通过 pygame.key 获取用户按键
            keys_pressed = pygame.key.get_pressed()
            dir = keys_pressed[pygame.K_RIGHT] - keys_pressed[pygame.K_LEFT]
    
            # 根据移动方向设置英雄的速度
            self.hero.speed = dir * 2
    
        def __update_sprites(self):
            """更新/绘制精灵组"""
    
            for group in [self.back_group, self.hero_group,
                          self.hero.bullets, self.enemy_group,
                          self.destroy_group]:
                group.update()
                group.draw(self.screen)
    
        @staticmethod
        def __finished_game():
            """退出游戏"""
    
            print("退出游戏")
            pygame.quit()
            exit()
    
    
    if __name__ == '__main__':
        PlaneGame().start_game()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130

    plane_sprites.py部分

    import random
    import pygame
    
    # 屏幕尺寸
    SCREEN_RECT = pygame.Rect(0, 0, 480, 700)
    
    
    class GameSprite(pygame.sprite.Sprite):
        """游戏精灵"""
    
        def __init__(self, image_name, speed=1):
            super().__init__()
    
            self.image = pygame.image.load(image_name)
            self.rect = self.image.get_rect()
            self.speed = speed
    
        def update(self, *args):
            self.rect.top += self.speed
    
        @staticmethod
        def image_names(prefix, count):
            names = []
            for i in range(1, count + 1):
                names.append("./images/" + prefix + str(i) + ".png")
    
            return names
    
    
    class Background(GameSprite):
        """背景精灵"""
    
        def __init__(self, is_alt=False):
            super().__init__("./images/background.png")
    
            if is_alt:
                self.rect.bottom = 0
    
        def update(self, *args):
            super().update(args)
    
            if self.rect.top >= SCREEN_RECT.height:
                self.rect.bottom = 0
    
    
    class PlaneSprite(GameSprite):
        """飞机精灵,包括敌机和英雄"""
    
        def __init__(self, image_names, destroy_names, life, speed):
    
            image_name = image_names[0]
            super().__init__(image_name, speed)
    
            # 生命值
            self.life = life
    
            # 正常图像列表
            self.__life_images = []
            for file_name in image_names:
                image = pygame.image.load(file_name)
                self.__life_images.append(image)
    
            # 被摧毁图像列表
            self.__destroy_images = []
            for file_name in destroy_names:
                image = pygame.image.load(file_name)
                self.__destroy_images.append(image)
    
            # 默认播放生存图片
            self.images = self.__life_images
            # 显示图像索引
            self.show_image_index = 0
            # 是否循环播放
            self.is_loop_show = True
            # 是否可以被删除
            self.can_destroied = False
    
        def update(self, *args):
            self.update_images()
    
            super().update(args)
    
        def update_images(self):
            """更新图像"""
    
            pre_index = int(self.show_image_index)
            self.show_image_index += 0.05
            count = len(self.images)
    
            # 判断是否循环播放
            if self.is_loop_show:
                self.show_image_index %= len(self.images)
            elif self.show_image_index > count - 1:
                self.show_image_index = count - 1
                self.can_destroied = True
    
            current_index = int(self.show_image_index)
    
            if pre_index != current_index:
                self.image = self.images[current_index]
    
        def destroied(self):
            """飞机被摧毁"""
    
            # 默认播放生存图片
            self.images = self.__destroy_images
            # 显示图像索引
            self.show_image_index = 0
            # 是否循环播放
            self.is_loop_show = False
    
    
    class Hero(PlaneSprite):
        """英雄精灵"""
    
        def __init__(self):
    
            image_names = GameSprite.image_names("me", 2)
            destroy_names = GameSprite.image_names("me_destroy_", 4)
    
            super().__init__(image_names, destroy_names, 0, 0)
    
            # 设置初始位置
            self.rect.centerx = SCREEN_RECT.centerx
            self.rect.bottom = SCREEN_RECT.bottom - 120
    
            # 创建子弹组
            self.bullets = pygame.sprite.Group()
    
        def update(self, *args):
            self.update_images()
    
            # 飞机水平移动
            self.rect.left += self.speed
    
            # 超出屏幕检测
            if self.rect.left < 0:
                self.rect.left = 0
            if self.rect.right > SCREEN_RECT.right:
                self.rect.right = SCREEN_RECT.right
    
        def fire(self):
            """发射子弹"""
    
            # bullet_count = len(self.bullets.sprites())
            # print("子弹数量 %d" % bullet_count)
    
            for i in range(0, 3):
                # 创建子弹精灵
                bullet = Bullet()
    
                # 设置子弹位置
                bullet.rect.bottom = self.rect.top - i * 20
                bullet.rect.centerx = self.rect.centerx
    
                # 将子弹添加到精灵组
                self.bullets.add(bullet)
    
    
    class Bullet(GameSprite):
        """子弹精灵"""
    
        def __init__(self):
            image_name = "./images/bullet1.png"
            super().__init__(image_name, -2)
    
        def update(self, *args):
            super().update(args)
    
            # 判断是否超出屏幕
            if self.rect.bottom < 0:
                self.kill()
    
    
    class Enemy(PlaneSprite):
        """敌机精灵"""
    
        def __init__(self):
            image_names = ["./images/enemy1.png"]
            destroy_names = GameSprite.image_names("enemy1_down", 4)
            super().__init__(image_names, destroy_names, 2, 1)
    
            # 随机敌机出现位置
            width = SCREEN_RECT.width - self.rect.width
            self.rect.left = random.randint(0, width)
            self.rect.bottom = 0
    
            # 随机速度
            self.speed = random.randint(1, 3)
    
        def update(self, *args):
            super().update(args)
    
            # 判断敌机是否移出屏幕
            if self.rect.top >= SCREEN_RECT.height:
                # 将精灵从所有组中删除
                self.kill()
    
            # 判断敌机是否已经被销毁
            if self.can_destroied:
                self.kill()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202

    实现效果图

    在这里插入图片描述


    图片素材:

    链接:https://pan.baidu.com/s/1LKoVQCRApscxpgg4MYznyw
    提取码:chui


    此项目为学习python时笔记
    来源于:黑马程序员python教程

  • 相关阅读:
    Running job: job_1709516801756_0003
    加载各种PostProcessor -》 生成BeanDefinition
    [Database] 脏读、幻读这些都是什么?事务隔离级别又是什么?MySQL数据库的事务隔离级别都有哪些?
    WordPress(6)网站侧边栏倒计时进度小工具
    出现 nested exception is java.sql.SQLException: 无效的列类型 的解决方法
    机器人控制——PID参数整定
    9.2 安卓逆向之—Frida持久化方案
    define宏定义和const的区别
    如何使用html、css制作一个期末作业网站【羽毛球体育运动主题html网页设计】
    开源|用 Java 实现一个生成 Markdown 文本的工具
  • 原文地址:https://blog.csdn.net/weixin_51313763/article/details/124897654