我是一个典型的80后,年老时玩过了特地多的游戏,所以这几天用Python3+pygame实现了一个另外小游戏”坦克大战“(其余的游戏,请翻阅我的博客)

本实例代码量有些多,残缺的版本在1000行左右(当然了如果再次优化的话 会缩小一部分)

分享进去,心愿能帮忙到大家,毕竟本人做教育行业做了这么多年,还是教育情怀的,哈哈哈哈哈

一、显示成果

二、代码

上面代码用到了一些素材(游戏背景音乐、图片等等),能够到https://www.itprojects.cn/detail.html?example_id=869a7cbfd9bd4d9b23e61a4d88e39b1c下载,谢谢大家的反对

残缺代码如下(留神:为了不便下载以及编写更简略,没有采纳多模块的形式,全副代码全副放到main.py文件中)

"""作者:it我的项目实例网网站:wwww.itprojects.cn"""import randomimport sysimport pygame# 屏幕的宽、高WIDTH = 630HEIGHT = 630# 边界值BORDER_LEN = 3# 字体FONTPATH = 'resources/font/font.ttf'class Iron(pygame.sprite.Sprite):    """    铁墙类    """    # 定义精灵组,将所有的砖墙实例对象增加到外面    group = pygame.sprite.Group()    def __init__(self, position):        # 调用父类的初始化办法,这样才可能实现必要的初始化操作        super().__init__()        self.image = pygame.image.load("resources/images/scene/iron.png")        # 当应用碰撞判断办法时,pygame就须要晓得以后要检测的物体的地位,所以这个rect属性肯定要设置        self.rect = self.image.get_rect()        self.rect.topleft = position        # 增加到精灵组        self.group.add(self)    @classmethod    def show(cls, screen):        for temp in cls.group:            screen.blit(temp.image, temp.rect)class Ice(pygame.sprite.Sprite):    """    冰类    """    # 定义精灵组,将所有的实例对象增加到外面    group = pygame.sprite.Group()    def __init__(self, position):        # 调用父类的初始化办法,这样才可能实现必要的初始化操作        super().__init__()        # 因为是12x12的小图片,所以须要制作一个24x24的image        image = pygame.Surface((24, 24))        for i in range(2):            for j in range(2):                image.blit(pygame.image.load("resources/images/scene/ice.png"), (12 * i, 12 * j))        self.image = image        # 当应用碰撞判断办法时,pygame就须要晓得以后要检测的物体的地位,所以这个rect属性肯定要设置        self.rect = self.image.get_rect()        self.rect.topleft = position        # 增加到精灵组        self.group.add(self)    @classmethod    def show(cls, screen):        for temp in cls.group:            screen.blit(temp.image, temp.rect)class River(pygame.sprite.Sprite):    """    河流类    """    # 定义精灵组,将所有的实例对象增加到外面    group = pygame.sprite.Group()    def __init__(self, position):        # 调用父类的初始化办法,这样才可能实现必要的初始化操作        super().__init__()        # 因为是12x12的小图片,所以须要制作一个24x24的image        image = pygame.Surface((24, 24))        for i in range(2):            for j in range(2):                image.blit(pygame.image.load("resources/images/scene/river1.png"), (12 * i, 12 * j))        self.image = image        # 当应用碰撞判断办法时,pygame就须要晓得以后要检测的物体的地位,所以这个rect属性肯定要设置        self.rect = self.image.get_rect()        self.rect.topleft = position        # 增加到精灵组        self.group.add(self)    @classmethod    def show(cls, screen):        for temp in cls.group:            screen.blit(temp.image, temp.rect)class Tree(pygame.sprite.Sprite):    """    树类    """    # 定义精灵组,将所有的实例对象增加到外面    group = pygame.sprite.Group()    def __init__(self, position):        # 调用父类的初始化办法,这样才可能实现必要的初始化操作        super().__init__()        # 因为是12x12的小图片,所以须要制作一个24x24的image        image = pygame.Surface((24, 24))        for i in range(2):            for j in range(2):                image.blit(pygame.image.load("resources/images/scene/tree.png"), (12 * i, 12 * j))        self.image = image        # 当应用碰撞判断办法时,pygame就须要晓得以后要检测的物体的地位,所以这个rect属性肯定要设置        self.rect = self.image.get_rect()        self.rect.topleft = position        # 增加到精灵组        self.group.add(self)    @classmethod    def show(cls, screen):        for temp in cls.group:            screen.blit(temp.image, temp.rect)class Brick(pygame.sprite.Sprite):    """    砖墙类    """    # 定义精灵组,将所有的砖墙实例对象增加到外面    group = pygame.sprite.Group()    def __init__(self, position):        # 调用父类的初始化办法,这样才可能实现必要的初始化操作        super().__init__()        self.image = pygame.image.load("resources/images/scene/brick.png")        # 当应用碰撞判断办法时,pygame就须要晓得以后要检测的物体的地位,所以这个rect属性肯定要设置        self.rect = self.image.get_rect()        self.rect.topleft = position        # 增加到精灵组        self.group.add(self)    @classmethod    def show(cls, screen):        for temp in cls.group:            screen.blit(temp.image, temp.rect)class Bullet(pygame.sprite.Sprite):    """    子弹类    """    # 定义精灵组,将所有的砖墙实例对象增加到外面    group = pygame.sprite.Group()    group_enemy = pygame.sprite.Group()    def __init__(self, _type, direction, position):        super().__init__()        # 子弹图片        if direction == "up":            image_path = "resources/images/bullet/bullet_up.png"        elif direction == "down":            image_path = "resources/images/bullet/bullet_down.png"        elif direction == "left":            image_path = "resources/images/bullet/bullet_left.png"        elif direction == "right":            image_path = "resources/images/bullet/bullet_right.png"        self.image = pygame.image.load(image_path)        # 子弹地位        self.rect = self.image.get_rect()        self.rect.center = position  # 设置子弹的初始地位的中心点        # 子弹方向        self.direction = direction        # 子弹挪动速度        self.speed = 8        # 将子弹增加到精灵组        if _type == "player":            self.group.add(self)        else:            self.group_enemy.add(self)    @classmethod    def auto_move(cls):        for temp in cls.group:            if temp.rect.x < BORDER_LEN or temp.rect.x > WIDTH - BORDER_LEN or temp.rect.y < BORDER_LEN or temp.rect.y > HEIGHT - BORDER_LEN:                cls.group.remove(temp)                print("子弹超出边界,移除子弹")                continue            if temp.direction == "up":                temp.rect = temp.rect.move((0, -temp.speed))            elif temp.direction == "down":                temp.rect = temp.rect.move((0, temp.speed))            elif temp.direction == "left":                temp.rect = temp.rect.move((-temp.speed, 0))            elif temp.direction == "right":                temp.rect = temp.rect.move((temp.speed, 0))        for temp in cls.group_enemy:            if temp.rect.x < BORDER_LEN or temp.rect.x > WIDTH - BORDER_LEN or temp.rect.y < BORDER_LEN or temp.rect.y > HEIGHT - BORDER_LEN:                cls.group_enemy.remove(temp)                print("子弹超出边界,移除子弹")                continue            if temp.direction == "up":                temp.rect = temp.rect.move((0, -temp.speed))            elif temp.direction == "down":                temp.rect = temp.rect.move((0, temp.speed))            elif temp.direction == "left":                temp.rect = temp.rect.move((-temp.speed, 0))            elif temp.direction == "right":                temp.rect = temp.rect.move((temp.speed, 0))        # 子弹碰砖墙(如果相碰,那么就移除以后子弹以及砖墙)        pygame.sprite.groupcollide(cls.group, Brick.group, True, True)        pygame.sprite.groupcollide(cls.group_enemy, Brick.group, True, True)        # 子弹碰铁墙(如果相碰,那么只移除子弹)        for bullet in cls.group:            if pygame.sprite.spritecollide(bullet, Iron.group, False, None):                cls.group.remove(bullet)        for bullet in cls.group_enemy:            if pygame.sprite.spritecollide(bullet, Iron.group, False, None):                cls.group_enemy.remove(bullet)    @classmethod    def show(cls, screen):        """        显示子弹        """        for temp in cls.group:            screen.blit(temp.image, temp.rect)        for temp in cls.group_enemy:            screen.blit(temp.image, temp.rect)    @classmethod    def move_and_show(cls, screen):        """        挪动、显示子弹        """        cls.auto_move()        cls.show(screen)class PlayerTank(pygame.sprite.Sprite):    """    我方坦克类    """    # 定义类属性,存储我方坦克(如果是单人模式就只有1个,如果是双人模式就有2个)    player_group = list()    # 定义精灵组,用来碰撞等判断    group = pygame.sprite.Group()    def __init__(self, player, top_left):        """        实现初始化性能        """        # 调用父类的初始化办法,这样才可能实现必要的初始化操作        super().__init__()        # 坦克的图片        image_path = "resources/images/playerTank/tank_T1_0.png" if player == "player1" else "resources/images/playerTank/tank_T2_0.png"        self.tank_all_image = pygame.image.load(image_path).convert_alpha()        self.image = self.tank_all_image.subsurface((0, 0), (48, 48))        # 当应用碰撞判断办法时,pygame就须要晓得以后要检测的物体的地位,所以这个rect属性肯定要设置        self.rect = self.image.get_rect()        self.rect.topleft = top_left        # 记录初始地位,以便在被击中后可能从新在默认地位呈现        self.origin_position = top_left        # 定义挪动的步长        self.step_length = 8        # 坦克的默认方向        self.direction = "up"  # 默认朝上        # 挪动缓冲, 用于防止坦克间断挪动过快导致不不便调整地位        self.move_cache_time = 4        self.move_cache_count = 0        # 坦克轮子转动成果        self.switch_count = 0        self.switch_time = 2        self.switch_image_index = False        self.image_postion_index = 0        # 发射子弹的距离        self.is_bullet_cooling = False  # 如果是第一次发射子弹,则不在冷却工夫内,能够失常发射        self.bullet_cooling_count = 0        self.bullet_cooling_time = 30        # 我方坦克生命次数        self.life_num = 3        # 标记此坦克是否显示        self.is_show_flag = True        # 将以后对象增加到类属性中,这样就能够通过类对象拜访到我方坦克        self.__class__.player_group.append(self)  # 或者self.player_group.append(self)也是能够的        # 增加到精灵组        self.group.add(self)    def update_direction(self):        """        更新坦克的朝向        """        if self.direction == 'up':            self.image = self.tank_all_image.subsurface((0, 0), (48, 48))            self.image_postion_index = 0        elif self.direction == 'down':            self.image = self.tank_all_image.subsurface((0, 48), (48, 48))            self.image_postion_index = 48        elif self.direction == 'left':            self.image = self.tank_all_image.subsurface((0, 96), (48, 48))            self.image_postion_index = 96        elif self.direction == 'right':            self.image = self.tank_all_image.subsurface((0, 144), (48, 48))            self.image_postion_index = 144    def move(self, direction, group_list):        """        依据键盘调整坦克方向,而后挪动        """        # 如果要挪动的方向与以后坦克的朝向不同,则先调整朝向        if self.direction != direction:            self.direction = direction            self.update_direction()            return        # 挪动缓冲        self.move_cache_count += 1        if self.move_cache_count < self.move_cache_time:            return        else:            self.move_cache_count = 0        # 挪动坦克        # 复制一份以后玩家坦克的坐标,如果碰到障碍物之后,能够进行复原        rect_ori = self.rect        if direction == "up":            self.rect = self.rect.move((0, -self.step_length))        elif direction == "down":            self.rect = self.rect.move((0, self.step_length))        elif direction == "left":            self.rect = self.rect.move((-self.step_length, 0))        elif direction == "right":            self.rect = self.rect.move((self.step_length, 0))        # 检测碰撞"砖墙"、"铁墙"、"冰"、"河流"。"树"无需查看        for group in group_list:            if pygame.sprite.spritecollide(self, group, False, None):                self.rect = rect_ori        # 判断碰撞到边界        if self.rect.top < BORDER_LEN:            self.rect.top = BORDER_LEN        elif self.rect.bottom > HEIGHT - BORDER_LEN:            self.rect.bottom = HEIGHT - BORDER_LEN        elif self.rect.left < BORDER_LEN:            self.rect.left = BORDER_LEN        elif self.rect.right > WIDTH - BORDER_LEN:            self.rect.right = WIDTH - BORDER_LEN        # 为坦克轮动特效切换图片        self.switch_count += 1        if self.switch_count > self.switch_time:            self.switch_count = 0            self.switch_image_index = not self.switch_image_index            self.image = self.tank_all_image.subsurface((48 * int(self.switch_image_index), self.image_postion_index), (48, 48))    def fire(self):        """        发射子弹        """        if not self.is_bullet_cooling:            if self.direction == "up":                position = (self.rect.centerx, self.rect.y)            elif self.direction == "down":                position = (self.rect.centerx, self.rect.y + 48)            elif self.direction == "left":                position = (self.rect.x, self.rect.centery)            elif self.direction == "right":                position = (self.rect.x + 48, self.rect.centery)            Bullet("player", self.direction, position)            print("我方坦克发射子弹")    @classmethod    def move_player_tank(cls, is_dual_mode, group_list):        """        管制我方坦克挪动        """        # 检查用户按键,从而管制坦克挪动        key_pressed = pygame.key.get_pressed()        # 定义挪动的步长        step_length = 8        # 复制一份以后玩家1的坐标,如果碰到障碍物之后,能够进行复原        # rect_ori = cls.player_group[0].rect        # 玩家一, ASWD挪动        if key_pressed[pygame.K_w]:            cls.player_group[0].move("up", group_list)        elif key_pressed[pygame.K_s]:            cls.player_group[0].move("down", group_list)        elif key_pressed[pygame.K_a]:            cls.player_group[0].move("left", group_list)        elif key_pressed[pygame.K_d]:            cls.player_group[0].move("right", group_list)        elif key_pressed[pygame.K_SPACE]:            # 如果按下了空格键,那么就发射子弹            cls.player_group[0].fire()        # 查看玩家1是否碰撞到障碍物        # # 检测碰撞"砖墙"        # if pygame.sprite.spritecollide(cls.player_group[0], brick_group, False, None):        #     print("玩家1碰到了砖墙", cls.player_group[0].rect)        #     cls.player_group[0].rect = rect_ori        # 玩家二, ↑↓←→挪动        if is_dual_mode:            # 复制一份以后玩家2的坐标,如果碰到障碍物之后,能够进行复原            # rect_ori = cls.player_group[1].rect            if key_pressed[pygame.K_UP]:                cls.player_group[1].move("up", group_list)            elif key_pressed[pygame.K_DOWN]:                cls.player_group[1].move("down", group_list)            elif key_pressed[pygame.K_LEFT]:                cls.player_group[1].move("left", group_list)            elif key_pressed[pygame.K_RIGHT]:                cls.player_group[1].move("right", group_list)            elif key_pressed[pygame.K_KP0]:                # 如果按下了数字0,那么就发射子弹                cls.player_group[1].fire()            # 查看玩家2是否碰撞到障碍物            # 检测碰撞"砖墙"            # if pygame.sprite.spritecollide(cls.player_group[1], brick_group, False, None):            #     cls.player_group[1].rect = rect_ori    def bullet_cooling(self):        """        判断发射子弹的冷却工夫是否达到        """        # 对发射子弹的冷却工夫计数        self.bullet_cooling_count += 1        if self.bullet_cooling_count > self.bullet_cooling_time:            self.is_bullet_cooling = False  # 不在冷却状态,即意味着能够发射子弹            self.bullet_cooling_count = 0            print("冷却结束...")        else:            self.is_bullet_cooling = True  # 不能发射,正在冷却    def judge_bomb(self):        """        判断是否被击中        """        # 判断碰撞到敌方坦克子弹        if pygame.sprite.spritecollide(self, Bullet.group_enemy, True, None):            self.life_num -= 1  # 如果被击中,那么就生命值-1            if self.life_num == 0:                self.is_show_flag = False  # 如果曾经没有了生命值,那么就标记为不显示            # 从新设置地位为初始地位            self.rect.topleft = self.origin_position    @classmethod    def show(cls, screen, is_dual_mode):        """        显示我方坦克        """        if cls.player_group:            if cls.player_group[0].is_show_flag:                screen.blit(cls.player_group[0].image, cls.player_group[0].rect)                # 对发射子弹的冷却工夫计数                cls.player_group[0].bullet_cooling()                # 判断是否被击中                cls.player_group[0].judge_bomb()            if is_dual_mode and cls.player_group[1].is_show_flag:                # 如果是双人模式                screen.blit(cls.player_group[1].image, cls.player_group[1].rect)                # 对发射子弹的冷却工夫计数                cls.player_group[1].bullet_cooling()                # 判断是否被击中                cls.player_group[1].judge_bomb()class PlayerHome(pygame.sprite.Sprite):    """    我方大本营    """    home = None    def __init__(self, position):        pygame.sprite.Sprite.__init__(self)        self.image = pygame.image.load("resources/images/home/home1.png")        self.rect = self.image.get_rect()        self.rect.left, self.rect.top = position        self.__class__.home = self    @classmethod    def show(cls, screen):        """        显示大本营        """        screen.blit(cls.home.image, cls.home.rect)class EnemyTank(pygame.sprite.Sprite):    """    敌人坦克类    """    # 定义精灵组,将所有的实例对象增加到外面    group = pygame.sprite.Group()    def __init__(self, position):        # 调用父类的初始化办法,这样才可能实现必要的初始化操作        super().__init__()        # 坦克默认的朝向        self.direction = random.choice(["up", "down", "left", "right"])        self.tank_all_image = pygame.image.load("resources/images/enemyTank/enemy_1_0.png")        self.image = None        self.update_direction()  # 依据随机朝向,计算出要应用的坦克图片        self.rect = self.image.get_rect()        self.rect.topleft = position        # 坦克默认的速度        self.speed = random.choice([2, 4])        # 发射子弹的距离        self.is_bullet_cooling = True        self.bullet_cooling_count = 0        self.bullet_cooling_time = 5        # 增加到精灵组        self.group.add(self)    def update_direction(self):        """        更新坦克的朝向        """        if self.direction == 'up':            self.image = self.tank_all_image.subsurface((0, 0), (48, 48))        elif self.direction == 'down':            self.image = self.tank_all_image.subsurface((0, 48), (48, 48))        elif self.direction == 'left':            self.image = self.tank_all_image.subsurface((0, 96), (48, 48))        elif self.direction == 'right':            self.image = self.tank_all_image.subsurface((0, 144), (48, 48))    @classmethod    def show(cls, screen):        for temp in cls.group:            screen.blit(temp.image, temp.rect)    def move(self, group_list):        """        敌方坦克主动挪动        :return:        """        # 记录地位,以便在碰到障碍物之后,可能复原        rect_ori = self.rect        if self.direction == "up":            self.rect = self.rect.move((0, -self.speed))        elif self.direction == "down":            self.rect = self.rect.move((0, self.speed))        elif self.direction == "left":            self.rect = self.rect.move((-self.speed, 0))        elif self.direction == "right":            self.rect = self.rect.move((self.speed, 0))        # 检测碰撞"砖墙"、"铁墙"、"冰"、"河流"。"树"无需查看        for group in group_list:            if pygame.sprite.spritecollide(self, group, False, None):                self.rect = rect_ori                # 随机失去新的朝向                self.direction = random.choice(["up", "down", "left", "right"])                self.update_direction()        # 碰撞到其余敌方坦克        # 先将本坦克从精灵组中移除        self.group.remove(self)        # 而后再判断是否碰撞到其余敌方坦克        if pygame.sprite.spritecollide(self, self.group, False, None):            self.rect = rect_ori            # 随机失去新的朝向            self.direction = random.choice(["up", "down", "left", "right"])            self.update_direction()        # 判断之后再将本坦克增加到精灵组        self.group.add(self)        # 碰撞到玩家坦克        if pygame.sprite.spritecollide(self, PlayerTank.group, False, None):            self.rect = rect_ori            # 随机失去新的朝向            self.direction = random.choice(["up", "down", "left", "right"])            self.update_direction()        # 碰撞到我方子弹        if pygame.sprite.spritecollide(self, Bullet.group, True, None):            self.group.remove(self)        # 判断碰撞到边界,而后再次随机新朝向        if self.rect.top < BORDER_LEN:            self.rect.top = BORDER_LEN            # 随机失去新的朝向            self.direction = random.choice(["up", "down", "left", "right"])            self.update_direction()        elif self.rect.bottom > HEIGHT - BORDER_LEN:            self.rect.bottom = HEIGHT - BORDER_LEN            # 随机失去新的朝向            self.direction = random.choice(["up", "down", "left", "right"])            self.update_direction()        elif self.rect.left < BORDER_LEN:            self.rect.left = BORDER_LEN            # 随机失去新的朝向            self.direction = random.choice(["up", "down", "left", "right"])            self.update_direction()        elif self.rect.right > WIDTH - BORDER_LEN:            self.rect.right = WIDTH - BORDER_LEN            # 随机失去新的朝向            self.direction = random.choice(["up", "down", "left", "right"])            self.update_direction()    @classmethod    def auto_move(cls, group_list):        for temp in cls.group:            temp.move(group_list)    @classmethod    def auto_move_and_show(cls, screen, group_list):        cls.auto_move(group_list)        cls.show(screen)    def judge_cooling_and_fire(self):        """        判断是否达到冷却工夫(即发射子弹要有距离),而后发射        """        self.bullet_cooling_count += 1        if self.bullet_cooling_count > self.bullet_cooling_time:            self.bullet_cooling_count = 0            if random.randint(1, 10) == 6:  # 如果随机失去的数字恰巧是6,那么就示意冷却工夫到                self.is_bullet_cooling = False        if not self.is_bullet_cooling:            # 创立子弹对象            if self.direction == "up":                position = (self.rect.centerx, self.rect.y)            elif self.direction == "down":                position = (self.rect.centerx, self.rect.y + 48)            elif self.direction == "left":                position = (self.rect.x, self.rect.centery)            elif self.direction == "right":                position = (self.rect.x + 48, self.rect.centery)            Bullet("enemy", self.direction, position)        # 发射结束后,立即设置为冷却状态        self.is_bullet_cooling = True    @classmethod    def fire(cls):        """        发射子弹        """        for temp in cls.group:            temp.judge_cooling_and_fire()class Game(object):    """    游戏管制类    """    def __init__(self):        """        初始化工作        """        # 游戏初始化        pygame.init()        # 创立用来显示画面的对象(了解为相框)        self.screen = pygame.display.set_mode((WIDTH, HEIGHT))    def game_start_interface(self):        """        显示游戏开始画面(让用户抉择游戏人数)        :return:False为单人模式,True为双人模式        """        # 筹备用到的图片        background_img = pygame.image.load("resources/images/others/background.png")        logo_img = pygame.image.load("resources/images/others/logo.png")        logo_img = pygame.transform.scale(logo_img, (446, 70))  # 图片放大1倍,行将892x140-->446x70        logo_rect = logo_img.get_rect()  # 失去这个图片的左上角的坐标(默认是(0,0)点),以及宽高        # 为了可能让logo图片在适合的地位显示,咱们能够设置它的中心点的坐标,它会依据本身的宽高主动计算出左上角的坐标        logo_rect.centerx, logo_rect.centery = WIDTH / 2, HEIGHT // 4        # print(logo_rect.topleft)  # 在终端中看到,此时输入的值是:(92, 122)        # 筹备要显示文本(1player、2players)        # 字体        font = pygame.font.Font(FONTPATH, 60)  # 60示意要显示的字体大小        font_color_white = (255, 255, 255)  # 要显示的字体色彩为红色        # 1player        one_player_text = font.render('1 PLAYER', True, font_color_white)        one_player_rect = one_player_text.get_rect()        one_player_rect.left, one_player_rect.top = WIDTH / 2 - 50, HEIGHT / 2 - 60        # 2players        two_players_text = font.render('2 PLAYERS', True, font_color_white)        two_players_rect = two_players_text.get_rect()        two_players_rect.left, two_players_rect.top = WIDTH / 2 - 50, HEIGHT / 2        # 游戏人数抉择时的图片        select_player_num_tank = pygame.image.load("resources/images/playerTank/tank_T1_0.png").convert_alpha().subsurface((0, 144), (48, 48))        select_player_num_tank_rect = select_player_num_tank.get_rect()        # 游戏开始提醒        game_tip = font.render('press <Enter> to start', True, font_color_white)        game_tip_rect = game_tip.get_rect()        game_tip_rect.centerx, game_tip_rect.top = WIDTH / 2, HEIGHT / 1.4        # 创立一个计时器,用来实现更加不便的延时(避免while循环过快,占用太多CPU的问题)        clock = pygame.time.Clock()        # 存储游戏人数(False单人,True双人)        is_dual_mode = False        # 主循环        while True:            # 事件检测(例如点击了键盘、鼠标点击等)            for event in pygame.event.get():                if event.type == pygame.QUIT:  # 如果用鼠标点击了❌,那么就退出程序                    pygame.quit()                    sys.exit()  # 退出程序                elif event.type == pygame.KEYDOWN:                    if event.key == pygame.K_RETURN:                        return is_dual_mode                    elif event.key == pygame.K_UP or event.key == pygame.K_DOWN or event.key == pygame.K_w or event.key == pygame.K_s:                        is_dual_mode = not is_dual_mode                        # print("以后抉择的游戏人数是(True双人、False单人)", is_dual_mode)            # 从(0,0)点(即左上角)开始贴一张图片(了解为在screen这个相框中从左上角开始贴一张照片)            self.screen.blit(background_img, (0, 0))            # 显示logo            self.screen.blit(logo_img, logo_rect)            # 显示游戏人数文字            self.screen.blit(one_player_text, one_player_rect)            self.screen.blit(two_players_text, two_players_rect)            # 显示标记抉择的人数的tank            if is_dual_mode:                # 双人模式                select_player_num_tank_rect.right, select_player_num_tank_rect.top = two_players_rect.left - 10, two_players_rect.top                self.screen.blit(select_player_num_tank, select_player_num_tank_rect)            else:                # 单人模式                select_player_num_tank_rect.right, select_player_num_tank_rect.top = one_player_rect.left - 10, one_player_rect.top                self.screen.blit(select_player_num_tank, select_player_num_tank_rect)            # 显示提醒            self.screen.blit(game_tip, game_tip_rect)            # 显示screen这个相框的内容(此时在这个相框中的内容像照片、文字等会显示进去)            pygame.display.update()            # FPS(每秒钟显示画面的次数)            clock.tick(60)  # 通过肯定的延时,实现1秒钟可能循环60次    def game_end_interface(self, is_win):        """        显示游戏完结画面        """        # 背景        background_img = pygame.image.load("resources/images/others/background.png")        # 游戏失败图        game_over_img = pygame.image.load("resources/images/others/gameover.png")        game_over_img = pygame.transform.scale(game_over_img, (150, 75))        game_over_img_rect = game_over_img.get_rect()        game_over_img_rect.midtop = WIDTH / 2, HEIGHT / 8        # 游戏胜利、失败字体        color_white = (255, 255, 255)        font = pygame.font.Font(FONTPATH, 60)        # 游戏胜利与否的提醒        if is_win:            font_render = font.render('Congratulations, You win!', True, color_white)        else:            font_render = font.render('Sorry, You fail!', True, color_white)        font_rect = font_render.get_rect()        font_rect.centerx, font_rect.centery = WIDTH / 2, HEIGHT / 3        # 用于抉择退出或从新开始        # 用于抉择的坦克光标        tank_cursor = pygame.image.load("resources/images/playerTank/tank_T1_0.png").convert_alpha().subsurface((0, 144), (48, 48))        tank_rect = tank_cursor.get_rect()        # 从新运行        restart_render_white = font.render('RESTART', True, color_white)        restart_rect = restart_render_white.get_rect()        restart_rect.left, restart_rect.top = WIDTH / 2.4, HEIGHT / 2        # 退出        quit_render_white = font.render('QUIT', True, color_white)        quit_rect = quit_render_white.get_rect()        quit_rect.left, quit_rect.top = WIDTH / 2.4, HEIGHT / 1.6        # 标记以后抉择的是退出还是持续游戏        is_quit_game = False        # 创立计时器对象,用于管制刷新频率        clock = pygame.time.Clock()        # 主循环        while True:            # 查看键盘事件            for event in pygame.event.get():                if event.type == pygame.QUIT:                    pygame.quit()                    sys.exit()                elif event.type == pygame.KEYDOWN:                    if event.key == pygame.K_RETURN:                        return is_quit_game                    elif event.key == pygame.K_UP or event.key == pygame.K_DOWN or event.key == pygame.K_w or event.key == pygame.K_s:                        is_quit_game = not is_quit_game            # 显示背景            self.screen.blit(background_img, (0, 0))            self.screen.blit(game_over_img, game_over_img_rect)            self.screen.blit(font_render, font_rect)            if not is_quit_game:                tank_rect.right, tank_rect.top = restart_rect.left - 10, restart_rect.top                self.screen.blit(tank_cursor, tank_rect)                self.screen.blit(quit_render_white, quit_rect)                self.screen.blit(restart_render_white, restart_rect)            else:                tank_rect.right, tank_rect.top = quit_rect.left - 10, quit_rect.top                self.screen.blit(tank_cursor, tank_rect)                self.screen.blit(quit_render_white, quit_rect)                self.screen.blit(restart_render_white, restart_rect)            # 刷新显示画面,此时才会真正的显示            pygame.display.update()            # 管制频率,FPS为60,每秒钟60次刷新            clock.tick(60)    def parse_game_level_file(self):        """        解析关卡文件        """        # 每个地图元素占用的像素(配置文件,例如1.lvl中正文里阐明了Grid SIZE: 24 * 24 pixels)        grid_size = 24        # 定义大本营        # home_dict = dict()        with open("./levels/3.lvl", errors='ignore') as f:            num_row = -1  # 用来标记地图元素是整个地图的第几行(总共26行,26列)            for line in f.readlines():                line = line.strip('\n')  # 切除每行行尾的换行符                # 如果以后要解决的行是地图元素,那么就持续解决                if line[0] in ["S", "B", "I", "R", "C", "T"]:                    # 地图元素                    num_row += 1                    # print("以后是第%d行" % num_row)                    for num_col, elem in enumerate(line.split(' ')):                        # print("以后是第%d行,第%d列" % (num_row, num_col))                        position = BORDER_LEN + num_col * grid_size, BORDER_LEN + num_row * grid_size                        if elem == 'B':                            # 创立砖墙对象,而后增加到精灵组                            Brick(position)                        elif elem == 'I':                            # 创立铁墙对象,而后增加到精灵组                            Iron(position)                        elif elem == 'R':                            # 创立河流对象,而后增加到精灵组                            River(position)                        elif elem == 'C':                            # 创立冰对象,而后增加到精灵组                            Ice(position)                        elif elem == 'T':                            # 创立树对象,而后增加到精灵组                            Tree(position)                elif line.startswith('%HOMEPOS'):                    # 大本营地位                    home_position = line.split(':')[-1]                    home_position = int(home_position.split(',')[0]), int(home_position.split(',')[1])                    home_position = (BORDER_LEN + home_position[0] * grid_size, BORDER_LEN + home_position[1] * grid_size)                    # 创立大本营类                    PlayerHome(home_position)                elif line.startswith('%PLAYERTANKPOS'):                    # 我方坦克初始地位                    player_tank_positions = line.split(':')[-1]                    player_tank_positions = [(int(pos.split(',')[0]), int(pos.split(',')[1])) for pos in player_tank_positions.split(' ')]                    player_tank_positions = [[BORDER_LEN + pos[0] * grid_size, BORDER_LEN + pos[1] * grid_size] for pos in player_tank_positions]                    # 从这个图片中切除一部分来当做要应用的图片,即玩家1的坦克                    # image = pygame.image.load("resources/images/playerTank/tank_T1_0.png").convert_alpha()                    # image = image.subsurface((0, 0), (48, 48))                    # player1_dict = {                    #     "image": image,                    #     "top_left": player_tank_positions[0]                    # }                    # 创立我方玩家1的坦克,而后增加到列表中                    PlayerTank("player1", player_tank_positions[0])                    # 从这个图片中切除一部分来当做要应用的图片,即玩家2的坦克                    # image = pygame.image.load("resources/images/playerTank/tank_T2_0.png").convert_alpha()                    # image = image.subsurface((0, 0), (48, 48))                    # player2_dict = {                    #     "image": image,                    #     "top_left": player_tank_positions[1]                    # }                    # player_group.append(player1_dict)                    # player_group.append(player2_dict)                    # 创立我方玩家2的坦克,而后增加到列表中                    PlayerTank("player2", player_tank_positions[1])                elif line.startswith('%ENEMYTANKPOS'):                    # 敌方坦克初始地位                    position = line.split(':')[-1]                    position = [[int(pos.split(',')[0]), int(pos.split(',')[1])] for pos in position.split(' ')]                    position = [(BORDER_LEN + pos[0] * grid_size, BORDER_LEN + pos[1] * grid_size) for pos in position]                    # 依据敌方坦克的初始地位创立多个坦克                    for pos in position:                        EnemyTank(pos)    def game_run_level(self, is_dual_mode):        """        运行游戏        """        # 背景图片        background_img = pygame.image.load("resources/images/others/background.png")        # 调用解析关卡配置文件        self.parse_game_level_file()        # 帧率控制对象        clock = pygame.time.Clock()        # 运行游戏的主循环        is_win = False        is_running = True        while is_running:            # 事件检测(例如点击了键盘、鼠标点击等)            for event in pygame.event.get():                if event.type == pygame.QUIT:                    pygame.quit()                    sys.exit()            # 通过键盘管制坦克挪动            PlayerTank.move_player_tank(is_dual_mode, [Brick.group, River.group, Ice.group, Iron.group])            # 敌方坦克开发            EnemyTank.fire()            # 碰撞检测            # 子弹碰大本营(无论是我方还是敌方子弹,只有碰到都认为本关卡游戏完结)            if pygame.sprite.spritecollide(PlayerHome.home, Bullet.group, True, None) or pygame.sprite.spritecollide(PlayerHome.home, Bullet.group_enemy, True, None):                is_running = False  # 如果碰撞到大本营,那么通过批改这个变量为False让while循环完结,游戏行将完结运行                is_win = False            # 如果敌方坦克没有了,则认为我方胜利            if len(EnemyTank.group) == 0:                is_running = False                is_win = True            # 如果我方坦克没有了生命值,则认为游戏输了            if (not is_dual_mode and PlayerTank.player_group[0].life_num == 0) or \                    (is_dual_mode and PlayerTank.player_group[0].life_num == 0 and PlayerTank.player_group[1].life_num == 0):                is_running = False                is_win = False            # 显示游戏背景            self.screen.blit(background_img, (0, 0))            # 显示砖墙            Brick.show(self.screen)            # 显示铁墙            Iron.show(self.screen)            # 显示河流            River.show(self.screen)            # 显示冰            Ice.show(self.screen)            # 显示树            Tree.show(self.screen)            # 显示大本营            PlayerHome.show(self.screen)            # 显示我方坦克            PlayerTank.show(self.screen, is_dual_mode)            # 显示我方坦克发射的子弹            Bullet.move_and_show(self.screen)            # 显示敌方坦克            EnemyTank.auto_move_and_show(self.screen, [Brick.group, River.group, Ice.group, Iron.group])            # 刷新要显示的内容,从而真正的显示            pygame.display.update()            # 每秒钟管制为60帧            clock.tick(60)        return is_win    def clean(self):        """        清理上一次游戏留下的残留        """        EnemyTank.group.empty()        PlayerTank.group.empty()        PlayerTank.player_group.clear()        Brick.group.empty()        Ice.group.empty()        River.group.empty()        Iron.group.empty()        Tree.group.empty()        Bullet.group.empty()        Bullet.group_enemy.empty()    def run(self):        # 显示游戏开始画面,让用户抉择游戏人数        is_dual_mode = self.game_start_interface()        # 调用游戏关卡函数,从而开始游戏        is_win = self.game_run_level(is_dual_mode)        # 接下来依据is_win来显示对应的输赢界面        while True:            if self.game_end_interface(is_win):  # 如果返回为True,那么就意味着退出游戏,否则持续游戏                break            # 持续从新游戏            # 清理上一次游戏的残留            self.clean()            # 创立用来显示画面的对象(了解为相框)            pygame.display.set_mode((WIDTH, HEIGHT))            # 显示游戏开始画面,让用户抉择游戏人数            is_dual_mode = self.game_start_interface()            # 调用游戏关卡函数,从而开始游戏            is_win = self.game_run_level(is_dual_mode)if __name__ == '__main__':    """    整体流程的管制    """    game = Game()    game.run()