初学pygame之坦克大战

"""坦克大战游戏(加深对面向对象编程的理解,此程序为作者初学pygame的练习作业,请多多指教。)
简介:《坦克大战》是由日本南梦宫Namco游戏公司开发的一款平面射击游戏,于1985年发售。游戏以坦克战斗及保卫基地为主题,属于策略型联机类。
同时也是FC平台上少有的内建关卡编辑器的几个游戏之一,玩家可自己创建独特的关卡,并通过获取一些道具使坦克和基地得到强化。"""
import pygame
import time
from datetime import datetime
from icecream import ic  # 使用Python的第三方库icecream进行打印调试
import random


def time_format():  # 定义一个返回时间的函数
    return f'{datetime.now()}|> '


screen_width = 800  # 定义窗口尺寸、颜色
screen_height = 600
bg_color = pygame.Color(0, 0, 0)  # 定义黑色背景
color_red = pygame.Color(255, 0, 0)  # 定义红色,用于左上角的文本框
key_dict = {
     pygame.K_UP: 'U', pygame.K_DOWN: 'D', pygame.K_LEFT: 'L', pygame.K_RIGHT: 'R'}  # 定义pygame的按键字典(K_UP:up arrow;K_DOWN:down arrow)
enemy_tank_count = 6  # 定义敌方坦克数量
enemy_tank_list = []  # 定义列表存储敌方坦克
my_bullet_list = []  # 定义列表存储我方坦克子弹
enemy_bullet_list = []  # 定义列表存储敌方坦克子弹
my_bullet_image = pygame.image.load('坦克大战素材/my_bullet.png')  # 加载我方坦克子弹图片 pygame.image.load:load new image from a file (or file-like object)
ic.configureOutput(prefix=time_format, includeContext=True)  # 先定义函数time_format,此处才能调用显示
# prefix自定义输出前缀,此处在打印语句中插入代码执行时间。
# includecontext(显示文件名、代码行、函数信息)值为True,即可输出代码执行的行和代码文件
ic(my_bullet_image)  # my_bullet_image: ,生成一个surface对象:宽度x高度x像素位
enemy_bullet_image = pygame.image.load('坦克大战素材/enemy_bullet.png')  # 加载敌方坦克子弹图片
my_tank_images = {
     
    'U': pygame.image.load('坦克大战素材/wu.gif'),
    'D': pygame.image.load('坦克大战素材/wd.gif'),
    'L': pygame.image.load('坦克大战素材/wl.gif'),
    'R': pygame.image.load('坦克大战素材/wr.gif')
    }  # 用字典(键值对)加载我方坦克图片素材。
enemy_tank_images = {
     
    'U': pygame.image.load('坦克大战素材/eu.gif'),
    'D': pygame.image.load('坦克大战素材/ed.gif'),
    'L': pygame.image.load('坦克大战素材/el.gif'),
    'R': pygame.image.load('坦克大战素材/er.gif')
    }  # 用字典(键值对)加载敌方坦克图片素材
background_image = pygame.image.load('坦克大战素材/grass.png')
ic(background_image.get_rect())  # 查看图片尺寸    与窗口大小一致
explode_list = []  # 定义列表存储爆炸效果
wall_list = []  # 定义列表存储墙壁
wall_image = pygame.image.load('坦克大战素材/wall.gif')  # 加载墙壁图片
my_tank_bullet_count = 5  # 设置我方坦克发射的子弹数量。限制子弹数量,节省内存,运行流畅。
wall_count = 6  # 定义墙壁的数量

"""主类:开始游戏、结束游戏"""
class MainGame:  # 类命名用驼峰原则
    window = None  # 初始化类属性窗口
    my_tank = None

    def __init__(self):  # 初始化构造函数
        pass

    # 开始游戏
    def start_game(self):
        pygame.display.init()  # 加载主窗口,初始化窗口。初始函数,使用pygame的第一步。返回窗口对象
        MainGame.window = pygame.display.set_mode((screen_width, screen_height), 0, 32)  # 设置背景对话框
        # 生成主屏幕,调用类属性MainGame.window接收
        # set_mode 函数:会返回一个 Surface 对象,代表了在桌面上出现的那个窗口。
        # 第1个参数为元组,表示屏幕的大小;第2个标志位,如果不用什么特性,就指定 0;第3个为色深
        # pygame.display.set_mode:Initialize a window or screen for display
        pygame.display.set_caption('Battle City(坦克大战)version1.0')  # 设置当前窗口标题pygame.display.set_caption:Set the current window caption
        # 首先生成游戏元素图片,再进入游戏的主循环
        self.create_my_tank()  # 调用方法生成我方坦克
        self.create_enemy_tank()  # 调用方法生成敌方坦克列表
        self.create_wall()  # 调用方法生成墙壁列表
        while True:  # 游戏循环,意味着游戏的正式开始!游戏的主循环是一个无限循环,直到用户退出。在这个主循环里面做的事情就是不停的刷新新画面
            MainGame.window.blit(background_image, (0, 0))  # 给窗口设置背景图片。必须放在循环体内才能实现画面的刷新,达到动画效果。
            # MainGame.window.fill(bg_color)  # 若不设置背景图片,可简单的设置填充色。
            MainGame.window.blit(self.get_text_surface(f'敌方坦克数量:{len(enemy_tank_list)}台'), (10, 10))
            # 在主窗口MainGame.window的(10,10)坐标处绘制get_text_surface返回的surface对象,用于显示文字信息。
            self.get_event()  # 获取事件(按下方向键、关闭键)
            self.show_enemy_tank()  # 调用显示敌方坦克,展现敌方坦克的各种操作
            if MainGame.my_tank and MainGame.my_tank.live:  # 判断我方坦克是否存活。存在且状态为活着,则显示我方坦克,否则不存在即删除。
                MainGame.my_tank.display_tank()  # 调用Tank类的display_tank方法进行显示
                if MainGame.my_tank.switch:  # 定义坦克显示之后的初始状态为静止,若开关为开,则坦克移动。
                    MainGame.my_tank.tank_move()  # 根据按键方向控制坦克的移动
                    MainGame.my_tank.tank_hit_wall()  # 调用Tank类的tank_hit_wall()方法,判断我方坦克是否与墙壁碰撞
                    MainGame.my_tank.my_tank_hit_enemy_tank()  # 调用MyTank类的my_tank_hit_enemy_tank()方法,判断我方坦克与敌方坦克的碰撞
            else:
                del MainGame.my_tank
                MainGame.my_tank = None  # 我方坦克消亡后把赋值为空。特殊常量None表示没有值,也就是空值。False表示0,也表示空字符串。
            self.show_wall()  # 显示墙壁
            self.show_my_bullet()  # 调用方法显示我方坦克子弹
            self.show_enemy_bullet()  # 调用方法显示敌方坦克子弹
            self.show_explode()  # 显示我方坦克子弹击中敌方坦克的爆炸效果
            time.sleep(0.02)  # 设置睡眠时间,使主程序减速运行,以便坦克的行进速度适宜
            pygame.display.update()  # 刷新窗口并显示

    # 初始化我方坦克
    def create_my_tank(self):  # 初始化我方坦克,传入坐标参数,和速度参数、坦克图片参数,调用类属性MainGame.my_tank接收
        MainGame.my_tank = MyTank(screen_width / 2, screen_height / 2, 5, my_tank_images)
        music = Music('坦克大战素材/start.wav')  # 调用Music类创建音乐对象
        music.play_music()  # 调用Music类的play_music()方法,播放音乐

    # 初始化敌方坦克,并添加到列表中
    def create_enemy_tank(self):
        top = 100  # 初始化敌方坦克高度坐标
        enemy_image = pygame.image.load('坦克大战素材/ed.gif')  # 加载敌方坦克图片
        enemy_image_width = enemy_image.get_rect().width  # 获取敌方坦克图片的宽度值
        ic(enemy_image.get_rect())  # 。pygame.Surface.get_rect():获取Surface对象的矩形区域
        ic(enemy_image.get_rect().width)  # 60
        ic(list(enemy_image.get_rect())[3])  # 60    ctrl+f可打开搜索框
        ic(enemy_image.get_width())  # 60。pygame.Surface.get_width():获取Surface对象的宽度
        for i in range(enemy_tank_count):
            left = random.randint(0, screen_width-enemy_image_width)  # 随机生成敌方坦克的宽度坐标值。最大值:屏幕和图片的宽度差
            tolerance = random.randint(1, 3)  # 随机生成敌方坦克的移动距离,表现为速度
            enemy_tank = EnemyTank(left, top, tolerance, enemy_tank_images)  # 传参生成敌方坦克对象
            enemy_tank_list.append(enemy_tank)  # 将敌方坦克对象加入敌方坦克列表

    # 初始化墙壁
    def create_wall(self):
        for i in range(wall_count):
            wall_left = random.randint(0, screen_width - wall_image.get_width())  # 随机生成指定宽度范围内墙体的left值
            wall_top = random.randint(0, screen_height - wall_image.get_height())  # 随机生成指定高度范围内墙体的top值
            wall = Wall(wall_left, wall_top, wall_image)  # 生成墙体对象
            wall_list.append(wall)  # 添加墙体列表的墙体对象

    # 绘制左上角文字,用于显示敌方坦克的实时数量
    def get_text_surface(self, text):  # 定义变量名为text
        pygame.font.init()  # 初始化字体模块。pygame.font.init:initialize the font module
        # ic(pygame.font.get_fonts())  # 查看系统所有字体。pygame.font.get_fonts:get all available fonts
        font = pygame.font.SysFont('kaiti', 18)
        # 从系统字体创建字体对象。pygame.font.SysFont:create a Font object from the system fonts
        text_surface = font.render(text, True, color_red)  # 将需要显示的文字转换成surface对象
        # Pygame.font.Font.render创建一个新的Surface,并在其上呈现指定的文本。 render(绘制)
        # 因为游戏无法在现有的Surface上直接绘制文本,故需新建一个Surface对象。
        return text_surface

    # 获取事件(按下方向键、关闭键、坦克重生键等)
    def get_event(self):
        for event in pygame.event.get():  # 获取所有事件,返回一个事件列表并进行遍历列表。
            # pygame.event.get():get events from the queue将获取并从队列中删除事件。如果指定一个或多个 type 参数,那么只获取并删除指定类型的事件。
            if event.type == pygame.QUIT:  # 事件类型如果是退出,则结束游戏。
                # Event对象有一个名为type的成员变量(member variable,也叫作属性,attributes或properties),它告诉我们对象表示何种事件。
                print('welcome back next time!')
                exit()  # 退出窗口。exit()是结束当前程序并且刷新缓冲区到标准输出
            elif event.type == pygame.KEYDOWN:  # 判断事件类型为按下键盘
                if not MainGame.my_tank and event.key == pygame.K_z:  # 如果我方坦克不存在,则按下字母键z生成我方坦克。
                    self.create_my_tank()
                if MainGame.my_tank and MainGame.my_tank.live:  # 判断我方坦克是否存活。活着是前提!
                    for pygamekey, direction in key_dict.items():  # 遍历自定义的pygame的按键字典key_dict
                        if event.key == pygamekey:  # 判断按键的值
                            ic(event)  # eg:
                            ic(event.key)  # eg:1073741906
                            MainGame.my_tank.direction = direction  # 切换方向
                            MainGame.my_tank.switch = True  # 按键后,将坦克的开关打开,使坦克可以移动。
                            ic(f'{MainGame.my_tank.direction},move up')
                    if event.key == pygame.K_SPACE:  # 定义按下空格键为发射子弹
                        ic('we fire bullets')
                        if len(my_bullet_list) < my_tank_bullet_count:  # 若子弹列表的长度(即子弹数量)小于限制的子弹数量,则生成子弹
                            my_bullet = Bullet(MainGame.my_tank, my_bullet_image)  # 传入我方坦克对象和子弹图片参数,生成我方坦克的子弹
                            my_bullet_list.append(my_bullet)
                            music = Music('坦克大战素材/fire.wav')  # 生成子弹发射的音乐对象
                            music.play_music()  # 播放音乐
                        else:
                            ic('The bullet is insufficient, no more bullets')  # 子弹不足,没有更多的子弹了
            elif event.type == pygame.KEYUP and event.key in key_dict.keys():  # 判断事件类型为松开按键(且是方向键),坦克停止移动
                if MainGame.my_tank and MainGame.my_tank.live:  # 判断我方坦克是否存活。活着是前提!
                    MainGame.my_tank.switch = False  # (over-indented过度缩进,检查缩进)

    # 敌方坦克的表现内容(在窗口中显示敌方坦克、敌方坦克的移动方法、敌方坦克与墙壁的碰撞处理、敌方坦克与我方坦克的碰撞处理、敌方坦克的消亡)
    def show_enemy_tank(self):  # 需在主循环中调用
        for enemy_tank in enemy_tank_list:  # 遍历敌方坦克列表
            if enemy_tank.live:  # 判断坦克是否存在
                enemy_tank.display_tank()  # 调用Tank类的display_tank()方法显示敌方坦克
                enemy_tank.rand_move()  # 自定义EnemyTank类的坦克移动方法rand_move(),随机移动坦克
                enemy_tank.tank_hit_wall()  # 调用Tank类的tank_hit_wall()方法,判断我方坦克是否与墙壁碰撞
                enemy_tank.enemy_tank_hit_my_tank()  # 调用EnemyTank类的enemy_tank_hit_my_tank()方法,判断敌方坦克与我方坦克的碰撞
                num = random.randint(1, 1000)  # 设定敌方坦克发射子弹的频率
                if num <= 20:
                    enemy_bullet = Bullet(enemy_tank, enemy_bullet_image)  # 传入敌方坦克对象和子弹图片参数,生成敌方坦克的子弹
                    enemy_bullet_list.append(enemy_bullet)
            else:
                enemy_tank_list.remove(enemy_tank)  # 敌方坦克不存在时,将从敌方坦克列表中删除

    # 我方坦克子弹的表现内容(在窗口中显示我方坦克子弹、子弹的移动方法、子弹与敌方坦克的碰撞处理、子弹与墙壁的碰撞处理、子弹的消亡)
    def show_my_bullet(self):  # 需在主循环中调用
        for my_bullet in my_bullet_list:
            if my_bullet.live:  # 若我方子弹状态为存在,则显示并发射子弹
                my_bullet.display_bullet()  # 调用子弹类Bullet的子弹显示方法display_bullet()
                my_bullet.bullet_move()  # 调用Bullet类的子弹移动方法bullet_move()
                Bullet.my_bullet_hit_enemy_tank(my_bullet)  # 检测我方子弹是否与敌方坦克发生碰撞
                Bullet.bullet_hit_wall(my_bullet)  # 检测我方子弹是否与墙壁碰撞
            else:
                my_bullet_list.remove(my_bullet)  # 若我方子弹状态为不存在,则调用列表的remove()方法,将其从子弹列表中删除。

    # 敌方坦克子弹的表现内容(在窗口中显示敌方坦克子弹、子弹的移动方法、子弹与我方坦克的碰撞处理、子弹与墙壁的碰撞处理、子弹的消亡)
    def show_enemy_bullet(self):  # 需在主循环中调用
        for enemy_bullet in enemy_bullet_list:
            if enemy_bullet.live:  # 若子弹状态为存在,则显示并发射子弹
                enemy_bullet.display_bullet()  # 调用子弹类Bullet的子弹显示方法display_bullet()
                enemy_bullet.bullet_move()  # 调用Bullet类的子弹移动方法bullet_move()
                Bullet.bullet_hit_wall(enemy_bullet)  # 检测敌方子弹是否与墙壁碰撞
                if MainGame.my_tank and MainGame.my_tank.live:  # 敌方坦克子弹击中我方坦克的前提是我方坦克存在
                    Bullet.enemy_bullet_hit_my_tank(enemy_bullet)  # 检测敌方子弹是否与我方坦克发生碰撞
            else:
                enemy_bullet_list.remove(enemy_bullet)  # 若子弹状态为不存在,则调用列表的remove()方法,将其从子弹列表中删除。

    # 循环展示爆炸效果
    def show_explode(self):
        for explode in explode_list:  # 遍历爆炸对象列表
            if explode.live:  # 判断爆炸是否存在,存在即显示,否则删除
                explode.display_explode()  # 调用Explode类的display_explode()方法,显示爆炸效果
            else:
                explode_list.remove(explode)  # 从爆炸列表中删除爆炸

    # 显示墙壁
    def show_wall(self):
        for wall in wall_list:
            if wall.live:  # 墙壁存在则展示
                wall.display_wall()
            else:
                wall_list.remove(wall)  # 墙壁不存在,则从列表中删除


class BaseItem(pygame.sprite.Sprite):  # 定义一个继承pygame.sprite.Sprite类的BaseItem类作为中间类,使Tank类和Bullet类共用其属性。
    # pygame.sprite.collide_rect:Collision detection between two sprites, using rects。两个精灵之间的碰撞检测,使用rect。
    # 矩形边框有交集即碰撞。collide_rect(left, right) -> bool,返回一个布尔值
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)


"""坦克类:显示坦克、移动。(坦克射击的效果由子弹类生成)"""
class Tank(BaseItem):  # 定义Tank类继承BaseItem类,从而也继承BaseItem类的父类pygame.sprite.Sprite类,即可调用相应的碰撞方法
    def __init__(self, left, top, tolerance, images):  # 定义坐标参数:left, top;公差参数:tolerance;图片参数images
        self.images = images
        self.direction = random.choice(list(key_dict.values()))  # 随机初始化坦克方向
        self.image = self.images[self.direction]  # 根据图片方向获取对应的图片,返回一个surface对象
        ic(self.image.get_rect())  # 
        self.rect = self.image.get_rect()
        # pygame.Surface.get_rect:get the rectangular area of the Surface 生成rec对象
        # Returns a new rectangle covering the entire surface.
        # This rectangle will always start at (0, 0) with a width and height the same size as the image.
        # pygame模块中rect方法自带的四个参数,分别有left,top,height,width。可列表化list(rect)调用各个参数。
        self.rect.left = left  # 设置区域的坐标
        self.rect.top = top
        self.tolerance = tolerance  # 定义按键后使rect.left递增/减的公差,实现坦克速度的控制。tolerance 宽容,容忍;耐力;公差
        self.switch = False   # 初始定义坦克移动状态为关
        self.live = True  # 定义变量live,记录坦克是否活着,即是否存在
        self.preleft = self.rect.left  # 定义preleft变量,保存当前矩形区域的坐标值
        self.pretop = self.rect.top

    # 显示坦克
    def display_tank(self):
        self.image = self.images[self.direction]  # 获取展示的对象
        MainGame.window.blit(self.image, self.rect)  # 将图片image按照矩形区域rect的坐标显示在窗口window上。
        # pygame.Surface.blit:draw one image onto another。将一个图像(Surface 对象)绘制到另一个图像上方。
        # blit:位块传送,位块传输,拷贝大量的位   blit surface 块移图面

    # 移动坦克
    def tank_move(self):
        self.preleft = self.rect.left  # 记录坦克移动之前的坐标
        self.pretop = self.rect.top
        if self.direction == 'L':
            if self.rect.left > 0:
                self.rect.left -= self.tolerance
        elif self.direction == 'R':
            if self.rect.left+self.rect.height < screen_width:
                self.rect.left += self.tolerance
        elif self.direction == 'U':
            if self.rect.top > 0:
                self.rect.top -= self.tolerance
        elif self.direction == 'D':
            if self.rect.top+self.rect.height < screen_height:
                self.rect.top += self.tolerance

    # 坦克还原,使坦克停在原地
    def restore(self):  # 使复原;使复位;使复职;修复;整修
        self.rect.left = self.preleft
        self.rect.top = self.pretop

    # 坦克与墙壁碰撞
    def tank_hit_wall(self):
        for wall in wall_list:  # 遍历墙壁列表
            if pygame.sprite.collide_rect(self, wall):  # 若坦克与墙壁碰撞
                self.restore()  # 修复坦克,还原坐标


"""敌方坦克类"""
class EnemyTank(Tank):  # 继承Tank类
    def __init__(self, left, top, tolerance, images):  # 定义坐标参数:left, top;速度参数:speed;坦克图片参数:images
        super(EnemyTank, self).__init__(left, top, tolerance, images)  # 子类调用父类的初始化方法
        self.step = 60  # 定义步数变量
        # 敌方坦克增加一个变量step步数,因为主循环while为无限循环,故可实现敌方坦克移动时步数递减。当步数 <= 0,修改敌方坦克的方向,并将步数复位。

    # 敌方坦克随机移动的方法
    def rand_move(self):
        if self.step <= 0:  # 当步数<=0时候修改敌方坦克的方向
            self.direction = random.choice(list(key_dict.values()))  # random.choice(list)从列表中随机抽取元素。
            self.step = 60  # 复位步数变量
        else:
            self.tank_move()
            self.step -= 1

    # 敌方坦克碰撞我方坦克
    def enemy_tank_hit_my_tank(self):
        if MainGame.my_tank and MainGame.my_tank.live:  # 判断我方坦克是否存活。活着是前提!
            if pygame.sprite.collide_rect(self, MainGame.my_tank):
                self.restore()  # 敌方坦克还原


"""我方坦克类"""
class MyTank(Tank):  # 继承Tank类
    def __init__(self, left, top, tolerance, images):  # 定义坐标参数:left, top;速度参数:speed;坦克图片参数:images
        super(MyTank, self).__init__(left, top, tolerance, images)  # 子类调用父类的初始化方法

    # 我方坦克碰撞敌方坦克
    def my_tank_hit_enemy_tank(self):
        for enemy_tank in enemy_tank_list:  # 遍历所有的敌方坦克,判别是否碰撞。
            if pygame.sprite.collide_rect(self, enemy_tank):
                self.restore()  # 我方坦克位置还原


"""子弹类:显示、移动"""
class Bullet(BaseItem):  # 定义Bullet类继承BaseItem类,从而也继承BaseItem类的父类pygame.sprite.Sprite类,即可调用相应的碰撞方法
    def __init__(self, tank, image):  # 初始化参数坦克tank(子弹由坦克发射)
        self.image = image
        self.direction = tank.direction  # 子弹方向同坦克方向
        self.rect = self.image.get_rect()  # 获取子弹图片的区域
        if self.direction == 'U':
            self.rect.left = tank.rect.left+tank.rect.width/2-self.rect.width/2
            self.rect.top = tank.rect.top-self.rect.height
        if self.direction == 'D':
            self.rect.left = tank.rect.left+tank.rect.width/2-self.rect.width/2
            self.rect.top = tank.rect.top+tank.rect.height
        if self.direction == 'L':
            self.rect.left = tank.rect.left-self.rect.width
            self.rect.top = tank.rect.top+tank.rect.width/2-self.rect.width/2
        if self.direction == 'R':
            self.rect.left = tank.rect.left+tank.rect.height
            self.rect.top = tank.rect.top+tank.rect.width/2-self.rect.width/2
        self.tolerance = 6  # 子弹移动的单位距离,控制速度
        self.live = True   # 初始定义存在子弹

    # 显示子弹
    def display_bullet(self):
        MainGame.window.blit(self.image, self.rect)  # 将图片surface加载到窗口

    # 移动子弹
    def bullet_move(self):
        if self.direction == 'U':
            if self.rect.top > 0:
                self.rect.top -= self.tolerance
            else:
                self.live = False  # 修改子弹状态为不存在
        elif self.direction == 'D':
            if self.rect.top + self.rect.height < screen_height:
                self.rect.top += self.tolerance
            else:
                self.live = False
        elif self.direction == 'L':
            if self.rect.left > 0:
                self.rect.left -= self.tolerance
            else:
                self.live = False
        elif self.direction == 'R':
            if self.rect.left + self.rect.width < screen_width:
                self.rect.left += self.tolerance
            else:
                self.live = False

    # 我方坦克子弹击中敌方坦克(即两者发生碰撞)
    def my_bullet_hit_enemy_tank(self):
        for enemy_tank in enemy_tank_list:
            if pygame.sprite.collide_rect(enemy_tank, self):  # 判断敌方坦克是否和我方子弹发射碰撞, 碰撞则产生爆炸效果。
                music = Music('坦克大战素材/hit.wav')  # 生成碰撞的音乐对象(创建爆炸对象的同时播放音效)
                music.play_music()  # 播放音乐
                enemy_tank.live = False  # 修改敌方坦克的状态为消亡
                self.live = False  # 修改子弹的状态为消亡
                explode = Explode(enemy_tank)  # 生成爆炸对象
                explode_list.append(explode)  # 爆炸列表中加入爆炸对象

    # 敌方坦克子弹击中我方坦克
    def enemy_bullet_hit_my_tank(self):
        if pygame.sprite.collide_rect(MainGame.my_tank, self):  # 判断我方坦克是否和敌方子弹发射碰撞, 碰撞则产生爆炸效果。
            explode = Explode(MainGame.my_tank)  # 产生爆炸效果
            music = Music('坦克大战素材/hit.wav')  # 生成碰撞的音乐对象(创建爆炸对象的同时播放音效)
            music.play_music()  # 播放音乐
            explode_list.append(explode)  # 爆炸效果添加到爆炸列表中
            self.live = False  # 修改子弹的状态为消亡
            MainGame.my_tank.live = False  # 修改我方坦克的状态为消亡

    # 子弹与墙壁碰撞
    def bullet_hit_wall(self):
        for wall in wall_list:  # 遍历墙壁列表
            if pygame.sprite.collide_rect(wall, self):  # 若子弹与墙壁碰撞
                music = Music('坦克大战素材/hit.wav')  # 生成碰撞的音乐对象(创建爆炸对象的同时播放音效)
                music.play_music()  # 播放音乐
                self.live = False  # 子弹消亡
                wall.hp -= 1  # 墙壁的生命值减小
                if wall.hp < 0:  # 当墙壁的生命值为负,则墙壁消亡。
                    wall.live = False


"""爆炸效果类:展示爆炸"""
class Explode:
    def __init__(self, tank):  # 爆炸的位置由当前子弹打中的坦克位置决定, 故将坦克对象作为参数传入。
        self.rect = tank.rect  # 爆炸的位置同坦克的位置
        self.images = [
            pygame.image.load('坦克大战素材/blast0.gif'),
            pygame.image.load('坦克大战素材/blast1.gif'),
            pygame.image.load('坦克大战素材/blast2.gif'),
            pygame.image.load('坦克大战素材/blast3.gif'),
            pygame.image.load('坦克大战素材/blast4.gif')
            ]  # 用列表加载爆炸效果的图片集
        self.step = 0  # 定义参数step,用于循环显示爆炸效果
        self.image = self.images[self.step]  # 读取爆炸图片
        self.live = True  # 初始设定爆炸效果的存在与否

    # 展示爆炸
    def display_explode(self):
        if self.step < len(self.images):  # 若step小于图片列表的长度则显示相应的爆炸图片,否则爆炸图片消亡,从新开始
            self.image = self.images[self.step]
            MainGame.window.blit(self.image, self.rect)  # 将爆炸效果在屏幕上显示
            self.step += 1  # 循环遍历爆炸效果的图片,依次进行显示。
        else:
            self.live = False
            self.step = 0


"""墙壁类:是否通过墙壁"""
class Wall:
    def __init__(self, left, top, image):
        self.image = image
        self.rect = self.image.get_rect()
        self.rect.left = left
        self.rect.top = top
        self.live = True
        self.hp = 3  # 设置生命值。血量:HP(HealthPoint)。墙壁被子弹击中四次就消亡。

    def display_wall(self):  # 展示墙壁
        MainGame.window.blit(self.image, self.rect)


"""音效类:播放音乐"""
class Music:
    def __init__(self, filename):
        self.filename = filename
        pygame.mixer.init()  # 初始化混音器模块,以进行声音的加载和播放。
        pygame.mixer.music.load(self.filename)  # 加载音乐文件,并为播放做准备。

    # 播放音乐
    def play_music(self):
        pygame.mixer.music.play(loop
[video(video-NsuU6cVA-1612278054153)(type-undefined)(url-undefined)(image-https://img-blog.csdnimg.cn/editor-video.png)(title-undefined)]
s=1)  # 启动音乐流的回放,loops表示重复播放的次数。若loops=-1,则表示无限重复播放。


if __name__ == '__main__':
    MainGame().start_game()
    #MainGame().get_text_surface()  # 调用函数查看字体

你可能感兴趣的:(python)