python小游戏之超级玛丽进阶版(1~4关)。好玩到爆炸~【内附github源码,及其详细备注】


前言

哈喽!大家好,这里是cwt的成长日记!
有多少人还记得“超级玛丽”这款经典游戏?
有多少人还记得那个戴帽子、大胡子、穿着背带裤的马里奥!
前段时间给大家更新了超级玛丽的基础版,只包含了第一关的部分功能。此次更新完善了第一关细节,并且更新到第4关。大家可以一口气玩四关喽。
后续关卡细节方面大家可以自由发挥,制作出属于自己独一无二的超级玛丽!!!

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

一、环境搭建

语言:python3
编译器:pycharm(好用)
#各自官网均可下载

主要模块:pygame (pip install pygame 即可安装该模块)

二、素材包

1.关卡,角色造型、各种道具等

在这里插入图片描述

2.各种音乐文件、各种道具等

在这里插入图片描述

3.各个关卡相关信息

在这里插入图片描述

三、马里奥项目

1.整体框架和细节(更新版)

    main.py游戏主入口
    source
	    components成分,部件
	        -box.py 盒子(游戏中带问号的方格)
	        -brick.py 砖块
	                def __init__(self,x,y,brick_type,group,color=None,name='brick'):
	        -coin.py 金币
	        -enemy.py 敌人
	        -player.py 主角
	                def __init__(self,name):
	                    pygame.sprite.Sprite.__init__(self)
	                    self.name = name
	                    self.load_data()  #加载角色数据,各种造型帧即其各种状态的速度
	                    self.setup_states()  #初始化角色各种状态
	                    self.setup_velocities()  #设置速率
	                    self.setup_timers()  #设置计时器
	                    self.load_images()  #载入主角的各种帧造型
	                def update(self,keys,level):
	                    self.current_time = pygame.time.get_ticks()  #以毫秒为单位获取时间
	                    self.handle_states(keys,level) #处理各种状态
	                    self.is_hurt_immune() #判断是否为无敌模式
	                def handle_states(self,keys,level): #处理各种状态
	        -powerup.py 强化(游戏中蘑菇)
	            def create_powerup(centerx,centery,type,flag):
	                """create powerup based on type and mario state"""
	                if flag ==0:
	                    return Mushroom(centerx,centery)
	                elif flag ==1:
	                    return Fireflower(centerx, centery)
	                elif flag ==2:
	                    return LifeMushroom(centerx, centery)
	                elif flag == 3:
	                    return Star(centerx, centery)
	
	        -stuff.py 物品(不起眼的小道具,旗子水管等)
	            class Item(pygame.sprite.Sprite):
	                def __init__(self,x,y,w,h,name):
	                    '''每个物品都有了隐形的轮廓,方便使用,方便进行碰撞检测,不需要传入图像,因为地图上有这些的图像了'''
	                    pygame.sprite.Sprite.__init__(self)
	
	            class Checkpoint(Item):
	            '''检查点实际就是一个长方体精灵,碰到之后才会触发下面的事件'''
	                def __init__(self, x, y, w, h, checkint_type, enemy_groupid=None, name='checkpoint'):
	                    Item.__init__(self,x,y,w,h,name)
	                    self.checkpoint_type = checkint_type
	                    self.enemy_groupid = enemy_groupid
	
	        -info.py 游戏信息(显示在屏幕上方的英文字体等)
	            def __init__(self,state):
	                self.state = state
	                self.create_state_lable()  #创某阶段特有的文字
	                self.create_info_labels()    #创建各阶段通用性息
	                self.flash_coin = coin.FlashingCoin() #初始化金币类
	             def create_state_lable(self):  #创造某阶段特有的文字
	             def create_info_labels(self):  #创建各阶段通用性息
	             def create_lable(self,label,size=40,width_scale=1.25,height_csale=1): #文字生成图片
	             def update(self):  #调用金币类更新类更新方法,实现金币闪烁
	             def draw(self,surface):    #把静态文字,动态金币等信息画上去
	    states状态
	        -main_menu.py 主页面(游戏刚进入的主菜单)
	            -MainMenu()类
	              def __init__(self): #初始化游戏信息字典
	                    game_info={
	                    }
	              def start(self,game_info,current_time):  #使用start函数对该状态进行初始化,
	                    self.current_time=current_time
	                    self.game_info=game_info
	                    self.setup_background()   #设置背景底图
	                    self.setup_cursor()       #设置光标
	                    self.setup_palyer()        #设置游戏角色
	                    self.info=info.Info('main_menu',self.game_info)   #初始化该类,设置文字信息
	                    self.finished=False     #主页面是否结束(状态机)
	                    self.next = 'load_screen'
	              def update()
	
	        -load_screen.py 载入页面
	             def start(self,game_info,current_time):
	                self.game_info=game_info #获取信息
	                self.game_info['statue']='load_screen' #修改游戏状态
	                self.finished= False #告诉程序当前状态未结束
	                self.next = 'level' #下个关卡为level
	                self.duration=2000 #持续时间
	                self.timer = 0  #定时器
	                self.info = info.Info('load_screen',self.game_info) #初始化页面信息
	             def update():
	             def draw():
	        -level.py 游戏关卡
	         def start(self,game_info,current_time):
	
	                self.current_time=current_time
	                self.game_info=game_info
	                self.game_info['statue'] = 'level'
	                self.finished= False
	                self.next = 'game_over'
	
	                self.info=info.Info('level',self.game_info)
	
	                #self.flag=plagpole.Flag(470,116)
	
	                self.load_map_data()  #加载关卡数据
	                self.setup_background() #设置背景,新建了一个北京大小的图层
	                self.setup_start_posotions()#新建起始位置的方法
	                self.setup_player()
	                self.setup_ground_items() #设置大地,管道,楼梯,加载到精灵组
	                self.setup_brick_and_box()
	                self.setup_enemy()
	                self.setup_checkpoints()    #初始化检查点
	                self.setup_flag()
	
	                self.sound=sound.Sound(self)
	    constants.py 存储游戏常量
	    sound.py 音效/音乐
	    set_up.py 启动代码(如设置屏幕宽高,载入素材等)
	    tools.py 主控,工具代码 (好用的工具,如图片加载工具)
	         -Game()类,游戏主控. Game().run运行游戏. Game().update更新游戏
	              def __init__(self,state_dict,start_state):
	                self.screen=pygame.display.get_surface() #获取当前显示的 Surface 对象,获得屏幕
	                self.clock=pygame.time.Clock()#创建一个时钟,控制帧率
	                self.keys=pygame.key.get_pressed() #获取按键状态
	                self.state_dict=state_dict  #获取主菜单的状态字典
	                self.state=self.state_dict[start_state] #保存传入的状态(初始化该状态的类)
	              def update(self):
	                -判断游戏当前状态是否结束,如果结束就传入下一个状态,初始化该状态并调用该状态的start()方法,目的是传递游戏数据。没有结束就调用当前状态的更新方法
	              def run(self):
	                -循环获取事件,监听事件状态(键盘按键)
	                -调用自身update方法
	                -更新屏幕
	                -设置游戏帧率(越大越流畅)
	         -load_graphics(path:图片路径)函数:加载图片 return:graphics{name:img}   本项目加载的是一张大图,大图上有很多小图
	            -pygame.image.load("图片路径").convert()
	         -get_image(sheet:传入加载后的图片,x:图片在大图的x坐标,y:图片在大图的y坐标,width:图片宽,height:图片高,colorkey:改颜色设置为透明,scale:放大倍速)
	         -load_all_music(directory, accept=('.wav', '.mp3', '.ogg', '.mdi')): #加载音乐
	         -load_all_sound(directory, accept=('.wav','.mpe','.ogg','.mdi')): #加载按键声
	
	难点:
	
	    Mario的位置:由其速度决定,由速度计算出位置
	    画面跟随:简单解决方法,1.锁定主角移动背景(略显沉闷)
	                        2.主角动,背景也动
	                          -新建一个新图层
	                          -把游戏里的事件正常画在该图层
	                          -把游戏窗口显示的画面渲染到屏幕上
	    碰撞检测:
	        常规做法:
	            -要把所有可能与Mario发生碰撞的物体一一罗列出来,依次做碰撞检测
	            -python中,可用精灵组加碰撞检测api实现
	        柱子水管等:
	            - 从json文件中读出坐标,逐个个实例化
	            - 创建了一个精灵组,把所有相同类型的放入同一组
	            - 然后在Mario位置更新函数中进行配置
	
	    信息传递:在第一个调用类的初始化函数中初始化game_info,用start函数代替初始化函数初始化类的各种函数,这样start可以反复调用,实现该阶段重置效果
	            传递到其他类中传入game_info
	
	    宝箱顶起4过程:1正常状态,2被顶了下,3微微隆起,4打开破碎
	
	    检查点技术:实际就是一个无形的矩形(为了解决Mario不论速度快慢都能见到对应阶段的野怪)
	
	    bug:mario从右边碰到物体会身体会嵌入一些
	
	    精灵组:为了方便碰撞检测和统一操作
	
	    mario吃蘑菇变身时只写了x方向检测,注意y方向检车会使mario变成fall状态

2.游戏流程

在这里插入图片描述

3.程序入口

#编写者:cwt
#时间:2022/7/4 20:39
#游戏入口
from source import tools,setup
from source.statues import main_menu,level,load_screen,level2
import pygame

def main():
    state_dict = {
        'main_menu' : main_menu.MainMenu(),
        'load_screen' : load_screen.LoadScreen(),
        'level' : level.Level(),
        'game_over': load_screen.GameDver(),
        'level2': level.Level2(),
        'load_level2': load_screen.Load_level2(),
        'level3': level.Level3(),
        'load_level3': load_screen.Load_level3(),
        'level4': level.Level4(),
        'load_level4': load_screen.Load_level4()

    }
    '''test'''
    game=tools.Game(state_dict,'main_menu')   #初始化游戏主控
    # state=main_menu.MainMenu()   #初始化主页面
    # state = level.Level()
    # state = load_screen.LoadScreen()
    game.run()     #运行游戏

if __name__ == '__main__':
    main()

4.游戏主控(主要更新了加载音乐和音效的方法)


def load_all_music(directory, accept=('.wav', '.mp3', '.ogg', '.mdi')):
    songs = {}
    for song in os.listdir(directory):
        name, ext = os.path.splitext(song)
        if ext.lower() in accept:
            songs[name] = os.path.join(directory, song)
    return songs

def load_all_sound(directory, accept=('.wav','.mpe','.ogg','.mdi')):

    effects = {}
    for fx in os.listdir(directory):
        name, ext = os.path.splitext(fx)
        if ext.lower() in accept:
            effects[name] = pygame.mixer.Sound(os.path.join(directory, fx))
    return effects

5.关卡类(增加第二关第三关第四关)


#编写者:cwt
#时间:2022/7/4 21:23
#关卡

class Level:
    def start(self,game_info):
        self.game_info=game_info
        self.finished= False
        self.next = 'game_over'
        self.info=info.Info('level',self.game_info)

        print(self.flag)
        self.load_map_data()
        self.setup_background()
        self.setup_start_posotions()#新建起始位置的方法
        self.setup_player()
        self.setup_ground_items()
        self.setup_brick_and_box()
        self.setup_enemy()
        self.setup_checkpoints()    #初始化检查点
        self.setup_flag()
class Level2(Level):
    def __init__(self):
        Level.__init__(self)

    def load_map_data(self):
        file_name = 'level_2.json'
        file_path = os.path.join('D:/Users/Administrator/PycharmProjects/superMario/source/data/maps/', file_name)
        with open(file_path) as f:
            self.map_data = json.load(f)

    def is_or_not_finished(self,keys):
        #print(self.player.baoqi,self.player.rect.y)
        if keys[pygame.K_9]:
            self.finished = True
            self.next = 'load_level3'

        if self.player.baoqi== True and self.player.rect.y>400:

            self.finished=True
            self.next = 'load_level3'

class Level3(Level):
    def __init__(self):
        Level.__init__(self)

    def load_map_data(self):
        file_name = 'level_3.json'
        file_path = os.path.join('D:/Users/Administrator/PycharmProjects/superMario/source/data/maps/', file_name)
        with open(file_path) as f:
            self.map_data = json.load(f)

    def is_or_not_finished(self,keys):
        # print(self.player.baoqi,self.player.rect.y)
        if keys[pygame.K_9]:
            self.finished = True
            self.next = 'load_level4'
        if self.player.baoqi == True and self.player.rect.y > 400:
            self.finished = True
            self.next = 'load_level4'

class Level4(Level):
    def __init__(self):
        Level.__init__(self)

    def load_map_data(self):
        file_name = 'level_4.json'
        file_path = os.path.join('D:/Users/Administrator/PycharmProjects/superMario/source/data/maps/', file_name)
        with open(file_path) as f:
            self.map_data = json.load(f)

    def setup_flag(self):
        self.pole_group = pygame.sprite.Group()
        self.flag_group = pygame.sprite.Group()
        self.finial_group = pygame.sprite.Group()
        '''测试用'''
        for item in [{"x":6470, "y":116, "type":0},
            {"x":6500, "y": 97, "type":1},
            {"x":6500, "y":137, "type":1},
            {"x":6500, "y":177, "type":1},
            {"x":6500, "y":217, "type":1},
            {"x":6500, "y":257, "type":1},
            {"x":6500, "y":297, "type":1},
            {"x":6500, "y":337, "type":1},
            {"x":6500, "y":377, "type":1},
            {"x":6500, "y":417, "type":1},
            {"x":6500, "y":450, "type":1},
            {"x":6500, "y": 97, "type":2}]:
            # for item in self.map_data['flagpole']:
            x, y, type = item['x'], item['y'], item['type']
            if type == 0:
                self.flag_group.add(plagpole.Flag(x, y))
            elif type == 1:
                self.pole_group.add(plagpole.Pole(x, y))
            else:
                self.finial_group.add(plagpole.Finial(x, y))

    def is_or_not_finished(self,keys):
        # print(self.player.baoqi,self.player.rect.y)
        if keys[pygame.K_8]:
            self.player.hurt_imune =True #伤害免疫
        if keys[pygame.K_7]:
            self.player.hurt_imune = False  # 伤害免疫
        if keys[pygame.K_9]:
            self.finished = True
            self.next = 'load_screen'
        if self.player.baoqi == True and self.player.rect.y > 400:
            self.finished = True
            self.next = 'load_screen'

6.增加角色按键音效

class Sound(object):
    """Handles all sound for the game"""
    def __init__(self, level):
        """Initialize the class"""
        self.sound_dict = setup.SOUND
        self.music_dict = setup.MUSIC
        self.level =level
        # self.overhead_info = overhead_info
        # self.game_info = overhead_info.game_info
        self.set_music_mixer()



    def set_music_mixer(self):
        """Sets music for level"""
        if self.level.player.dead:
            pg.mixer.music.load(self.music_dict['death'])
            pg.mixer.music.play()
            self.state = c.GAME_OVER
        else:

            pg.mixer.music.load(self.music_dict['main_theme'])
            pg.mixer.music.play()
            self.state = c.NORMAL


    def update(self, game_info, mario):
        """Updates sound object with game info"""
        self.game_info = game_info
        self.mario = mario
        self.handle_state()

    def  handle_state(self):
        """Handles the state of the soundn object"""
        #print('444')
        if self.state == c.NORMAL:

            if self.mario.dead:
                self.play_music('death', c.MARIO_DEAD)


    def play_music(self, key, state):
        """Plays new music"""
        pg.mixer.music.load(self.music_dict[key])
        pg.mixer.music.play()
        self.state = state

    def stop_music(self):
        """Stops playback"""
        pg.mixer.music.stop()



四、成果展示

1.游戏主页面

在这里插入图片描述

2.进入游戏

在这里插入图片描述

3.顶出蘑菇

在这里插入图片描述

4.大mario顶出顶碎砖块,顶出鲜花

在这里插入图片描述

5.火焰马里奥发射火球

在这里插入图片描述

6.视频效果

python制作超级玛丽,好玩到飞起!

地址:https://www.bilibili.com/video/BV1x24y1o7xN/?spm_id_from=333.999.0.0&vd_source=fb2219609a4ef30c028c05ca593238e9

总结

本项目参考B站up主教程,完整代码已上传github,持续更新优化,需要的小伙伴自取:https://github.com/cwt2022/Mario
对游戏感兴趣的小伙伴儿赶紧自己动手造一个吧~~
有问题的小伙伴可以在评论区留言,大家一起学习共同进步!
在这里插入图片描述

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>