pygame跳跳猫类超级玛丽接金币多关卡解迷游戏

"""跳跳猫接金币多关卡解迷游戏,本程序给上一个版本加上Coin类。
   本程序设计有10多个关卡,有些金币需要动脑筋才能接收到。"""

__author__ = "李兴球"
__date__ = "2018/12/18"

import os
import pygame
from pygame.locals import *

class Coin():
    def __init__(self,images,position,player,level_number,sound):
        self.images = [pygame.image.load(image).convert_alpha() for image in images]
        self.images = [pygame.transform.scale(image,(int(20 * image.get_width()/image.get_height()),40)) for image in self.images]
        self.position = position        
        self.current_costume = self.images[0]             # 当前造型
        self.costume_amounts = len(self.images)           # 造型数量,注意每个图片的矩形对象不一样
        self.rects = [ costume.get_rect() for costume in self.images]  # 所有造型的矩形对象        
        self.rect = self.current_costume.get_rect()       # 第一个造型的矩形对象
        self.rect.center = position                       # 第一个造型的设定坐标
        self.player = player                              # 可访问玩家对象
        self.level_number = level_number                  # 每枚金币能记住自己的关卡号,不是它的关卡,则自我死亡
        self.screen = self.player.screen                  # 屏幕对象
        self.sound = sound                                # 金币声
        self.dead = False
        self.index = 0
        self.switch_costume_interval  = 2                 # 切换造型间隔
        self.switch_costume_counter  = 0                  # 切换造型计数器
    def next_costume(self):
        if not self.dead:
           self.switch_costume_counter = self.switch_costume_counter + 1
           if self.switch_costume_counter > self.switch_costume_interval:              
              self.index = self.index + 1
              self.index = self.index % self.costume_amounts
              self.switch_costume_counter = 0
              self.current_costume = self.images[self.index]
              self.rect = self.rects[self.index]                 # 矩形对象
              self.rect.center = self.position                  # 重新设定坐标
              
    def level_check(self,current_level_number):
        """关卡号不对,则自杀"""
        if self.level_number != current_level_number:
            self.dead = True
            
    def draw(self):
        if not self.dead:
            self.screen.blit(self.current_costume,self.rect)

    def collide_player_check(self):
        if self.rect.colliderect(self.player.rect) and not self.dead:
            self.player.coins += 1
            self.dead = True
            try:
               self.sound.play()
            except:
                pass        
        
class Detector(pygame.sprite.Sprite):
    def __init__(self,screen):
        pygame.sprite.Sprite.__init__(self)
        self.screen = screen
        self.image = pygame.Surface((20,20)).convert_alpha()
        self.image.fill((0,155,123))
        self.rect = self.image.get_rect()
        self.mask = pygame.mask.from_surface(self.image)
            
class Player(pygame.sprite.Sprite):
    """玩家类"""
    def __init__(self,image,screen,level,detectors):
        """image:图像
           screen:屏幕
           level:关卡对象
           detectors:左,右,上,下碰撞检测器
        """
        pygame.sprite.Sprite.__init__(self)
        self.lifes = 3                                        # 有三条生命
        self.image = pygame.image.load(image).convert_alpha() # 小猫的造型
        self.image.set_colorkey((255,255,255))          # 设置不渲染的颜色
        self.rect = self.image.get_rect()               # 设置矩形对象,表示坐标与大小的
        self.screen = screen                            # 设置这个属性以便能访问screen  
        self.levels = levels                            # 所有关卡
        self.levels_index = 0                           # 当前关卡索引号,可以在这里直接设定起始关卡
        self.current_level = levels[self.levels_index]  # 当前关卡        
        self.level_amounts = len(levels)                # 关卡总数量
        self.detectors = detectors                      # 4个侦测器,是个列表
        self.screen_width = screen.get_width()          # 以便能访问屏幕宽度
        self.screen_height = screen.get_height()        # 设这个属性以便能访问屏幕高度
        self.xspeed = 0                                 # 水平速度
        self.yspeed = 0                                 # 垂直速度
        self.aspeed = 0.5                               # 加速度
        self.rect.centerx = self.screen_width//2        # 小猫初始的x中间坐标
        self.rect.centery = self.screen_height//2 - 100 # 小猫初始的y中间坐标
        self.__is_in_the_air = True                     # 描述是否在空中的变量

        self.mask = pygame.mask.from_surface(self.image) # 新建掩膜mask,用于碰撞检测        
        self.cantoleft = True
        self.cantoright = True
        "新增加的金币数量属性"
        self.coins = 0
        
    def update(self):        
        """模拟重力的坐标更新"""
        self.rect.move_ip(self.xspeed,self.yspeed)        
        self.yspeed = self.yspeed + self.aspeed
        
    def dead_check(self):
        if self.rect.top > self.screen_height:
            self.lifes -= 1
            self.rect.centerx = 50
            self.rect.centery = self.screen_height//2 - 100
            self.xspeed = 0 
            self.yspeed = 0
        return self.lifes
        
    def bump_obstacle_check(self):        
        """碰地形(障碍物)检测,小猫自带4个侦测器,用它们进行检测即可。"""
        if pygame.sprite.collide_mask(self.detectors[3],self.current_level):     # 下侦测器碰撞检测
            self.xspeed = 0
            self.yspeed = 0             
            self.__is_in_the_air  = False            
        
        if pygame.sprite.collide_mask(self.detectors[2],self.current_level):     # 上侦测器碰撞检测            
            self.xspeed = 0.99 *  self.xspeed
            self.yspeed = abs(self.yspeed)               
            
        if pygame.sprite.collide_mask(self.detectors[1],self.current_level):     # 右侦测器碰撞检测
            #print("碰右")
            self.xspeed = 0
            self.cantoleft = True
            self.cantoright =  False
        elif pygame.sprite.collide_mask(self.detectors[0],self.current_level):     # 左侦测器碰撞检测
            #print("碰左")
            self.xspeed = 0 
            self.cantoleft = False
            self.cantoright = True
        else:
            self.cantoleft = True
            self.cantoright = True
            
    def jump(self):
         
        if not self.__is_in_the_air:
           self.__is_in_the_air = True           
           self.yspeed = -11.5           
           
    def move_left(self):
        if self.cantoleft:self.xspeed = -5
        
    def move_right(self):
        if self.cantoright:self.xspeed = 5
        
    def stop_move(self):
        self.xspeed = 0        
        
    def draw(self):
        self.screen.blit(self.image,self.rect)
        
    def level_check(self):        
        """过关检测,坐标判断"""        
        if self.rect.right > self.screen_width:              # 超过右边缘,下一关                        
            self.levels_index = self.levels_index + 1                     
            if self.levels_index >= self.level_amounts:      # 关卡结束
                return False,self.levels_index                
            else:
                self.next_level(self.levels[self.levels_index])
                return True,self.levels_index  
                
        if self.rect.left < 0 :                               # 超过左边缘,上一关                        
            self.levels_index = self.levels_index - 1                    
            if self.levels_index < 0:                         # 关卡结束
                return False,self.levels_index                  
            else:
                self.previous_level(self.levels[self.levels_index])
                return True,self.levels_index
        return True,self.levels_index
    
    def next_level(self,level):
        self.rect.left = 10
        self.current_level = level
        
    def previous_level(self,level):
        self.rect.right = self.screen_width
        self.current_level = level                
        
class Level(pygame.sprite.Sprite):
    def __init__(self,image,screen):
        self.image = pygame.image.load(image).convert_alpha()         
        self.rect = self.image.get_rect()
        self.mask = pygame.mask.from_surface(self.image)
        self.rect.top = screen.get_height() - self.image.get_height()

def detectors_follow_cat():
    """跟随小猫有左右上下四个小方块,用来对小猫的移动进行检测"""
    detectors[0].rect.left = cat.rect.left                  # 左侦测器x坐标
    detectors[0].rect.centery = cat.rect.centery
    detectors[1].rect.right = cat.rect.right                # 右侦测器x坐标
    detectors[1].rect.centery = cat.rect.centery
    detectors[2].rect.centerx = cat.rect.centerx            # 上侦测器x坐标
    detectors[2].rect.top = cat.rect.top
    detectors[3].rect.centerx = cat.rect.centerx            # 下侦测器x坐标
    detectors[3].rect.bottom = cat.rect.bottom          
    
if __name__ == "__main__":

    game_title = "跳跳猫接金币多关卡解迷游戏_作者:李兴球,风火轮少儿编程出品 www.scratch8.net"
    backgrounds_png = ["BG" + str(i+1) + ".png" for i in range(14)]
    levels_png = ["Level" + str(i+1) + ".png" for i in range(14)]
    cat_image = "catx.png"
    width,height = 960,720

    import pygame
    pygame.mixer.init()
    coin_sound = pygame.mixer.Sound(os.getcwd() + os.sep + "coin" + os.sep + "金币声.wav")
    coin_images = [os.getcwd() + os.sep + "coin" + os.sep + str(i) + ".png" for i in range(8)]
    c1 =[(349,129),(349,189),(349,249),(600,499),(700,499),(800,499),(900,499)]
    c2 =[(100,123),(318,36),(276,215),(742,297),(780,50),(840,50),(900,50)]
    c3 =[(446,364),(276,322),(126,138),(700,29),(585,216)]
    c4 =[(349,229),(500,499),(600,499),(700,499),(433,117),(610,90),(680,90),(750,90)]
    c5 =[(180,324),(230,324),(280,324),(349,229),(500,499),(600,499),(700,499)]
    c6 =[(349,229),(520,499),(600,499),(680,499)]
    c7 =[(94,53),(349,229),(200,499),(600,300),(700,499)]
    c8 =[(349,80),(337,474),(500,299),(600,299),(700,299)]
    c9 =[(349,229),(200,199),(600,199),(700,199),(800,199),(900,199)]
    c10 =[(349,229),(400,499),(500,499),(600,499),(720,399)]
    c11 =[(160,423),(210,423),(260,423),(260,423),(350,8),(450,8)]
    c12 =[(92,615),(221,495),(418,357),(476,225),(672,626),(543,100),(593,100)]
    c13 =[(349,229),(500,499),(600,499),(700,499)]
    c14 =[(80,620),(130,620),(180,620),(230,620),(280,620),(330,620),(380,620),(430,620),(480,620)]
    c14.extend([(700,411),(650,411),(600,411),(550,411),(500,411),(450,411),(400,411),(350,411),(300,411)])
    c14.extend([(170,24),(270,24),(370,24),(470,24),(570,24),(670,24),(770,24)])
    coin_position_list = [c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,c12,c13,c14]
    s = 0
    for L in coin_position_list:
        s = s + len(L)
    print("总金币数:" ,s)
        
    only_produce_once = [True for i in range(len(coin_position_list))]# 用来限定只生成一次的逻辑变量列表
    
    screen = pygame.display.set_mode((width,height))              # 新建屏幕对象(面)
    pygame.display.set_caption(game_title)                        # 设置标题
    
    levels = [ Level(image,screen) for image in levels_png]       # 所有的关卡
    backgrounds = [pygame.image.load(image) for image in backgrounds_png]   #所有的背景
    levels_amouns = len(levels)                                   # 关卡数量
    
    detectors = [Detector(screen) for i in range(4)]              # 新建4个侦测器
    cat = Player(cat_image,screen,levels,detectors)               # 新建小猫对象
    
    clock = pygame.time.Clock()
    running = True
    levels_index = 0
    "生成第一关的金币"
    only_produce_once[levels_index] = False
    coins = [Coin(coin_images,position,cat,levels_index,coin_sound) for position in coin_position_list[levels_index]]  
    while running :
        clock.tick(60)
        for event in pygame.event.get():
            if event.type == QUIT: running = False  
            if event.type == KEYUP: cat.stop_move()
            
        keys = pygame.key.get_pressed()
        if keys[K_UP] : cat.jump()
        if keys[K_LEFT] : cat.move_left()
        if keys[K_RIGHT] : cat.move_right()
        
        m1,m2,m3 = pygame.mouse.get_pressed()
        if m1 :  print(pygame.mouse.get_pos())
        
        cat.update()                                           # 首先要对猫的坐标进行更新
        lifes = cat.dead_check()                               # 掉入深渊会死,以后可能碰到什么怪物也会死\
        if lifes == 0 : break                                  # 生命数为0,没必要渲染
        detectors_follow_cat()                                 # 然后侦测器的坐标才能紧接着实时设定
        cat.bump_obstacle_check()                              # 利用侦测器对地形进行检测
        old_index = levels_index
        beyond,levels_index   = cat.level_check()              # 通过小猫的坐标进行 过关检测
        "如果返回的关卡号大于old_index,则说明过关了,这时生成一系列这关的金币"
        if levels_index > old_index  and only_produce_once[levels_index] == True:
            coins = [Coin(coin_images,position,cat,levels_index,coin_sound) for position in coin_position_list[levels_index]]
            only_produce_once[levels_index] = False 
        
        if beyond == False : break                             # beyond为False,超过关卡范围,没必要渲染图形了

        for coin in coins:            
            coin.next_costume()
            coin.collide_player_check()
            coin.level_check(levels_index)                     # 每枚金币在诞生时会记住自己的关卡号,一旦不是它所在的关卡,则它的dead为True

        screen.fill((255,255,255))
        screen.blit(backgrounds[levels_index],(0,0))           # 渲染背景图
        current_level = levels[levels_index]                   # 根据索引取当前关卡对象
        screen.blit(current_level.image,current_level.rect)    # 贴关卡图
        cat.draw()                                             # 贴猫图
        
        for coin in coins:coin.draw()

        if levels_index < levels_amouns -1 :
            title_str = "第" + str(levels_index+1) + "关,还剩:" + str(lifes) + "条命" + ",金币数:" + str(cat.coins)
        elif levels_index == levels_amouns - 1:
            title_str = "恭喜你,通关了!你还剩:" + str(lifes) + "条命" + ",金币数:" + str(cat.coins)
        pygame.display.set_caption(title_str)
        pygame.display.update()                                # 更新画面        
    
    pygame.quit()
    
    print("游戏结束,请自行编写代码,显示失败画面。")
    input()