|
一、整体介绍:1.1前言:游戏代码基于Python制作经典游戏案例-水果忍者做出一些改动,优化并增加了一些功能。作为自己Python阶段学习的结束作品,文章最后有源码链接。1.2Python主要知识:(1)面向对象编程 类的定义与实例化、封装、继承(使用pygame.sprite.Sprite作为基类)(2)模块与库导入标准库(time,math,random)、导入第三方库( pygame)(3)事件处理事件监听(pygame.event.get()处理用户输入和游戏事件)、响应事件(根据不同事件,如关闭窗口、定时器事件,执行相应操作)(4)图形绘制图像加载( pygame.image.load()加载图像)、图像绘制(blit()方法将图像绘制到窗口上)、图像旋转( pygame.transform.rotate()旋转图像)(5)随机数生成(6)计时与帧率控制使用pygame.time.Clock()控制游戏的帧率(7)文件操作使用open()读取和写入文本文件,保存和读取最佳分数、逐行读取文件内容并解析数据(8)碰撞检测(9)Sprite和Group使用pygame.sprite.Sprite创建精灵(如水果、刀光、背景)、使用pygame.sprite.Group管理和更新多个精灵,方便批量处理(10)数学运算使用三角函数,math.sin()和math.cos(),计算水果的抛出轨迹(11)音频处理使用pygame.mixer播放背景音乐和音效,增强游戏体验(12)逻辑控制(13)字体与文本渲染使用pygame.font.Font()创建字体对象,并使用render()方法渲染文本以显示分数和信息(14)参数传递与返回值1.3游戏素材二、完善功能:(1)优化游戏参数例如:首页旋转圆环速度,水果上抛高度等,使游戏体验更加平滑。(2)禅宗模式倒计时禅宗模式在游戏右上方增加了时间倒计时的图形化界面。(3)增加额外音效由于pygame同时播放音乐,会有覆盖现象。即后播放音乐会覆盖之前播放音乐,导致原版游戏结束,bgm.play_over被bgm.play_menu覆盖,播放不出来。使用独立线程对代码要求较高,取巧,利用睡眠(time.sleep)。玩家切到炸弹结束游戏,暂停0.3s画面,而不是原版的突然重新开始。(4)游戏历史最高分数利用IO流逐行读取txt文件,和原版分数一样的window.blit函数绘制在游戏界面,不过分数的更新要在结束程序后会执行。Bug:游戏的局部和实例变量较多,有些资源可能会被程序占用而无法释放。目前主要bug,在游戏碰撞检测的时候,偶尔会出现分数停止更新的情况。本人才疏学浅,至今没有有效解决,希望大佬们多多包涵,最好能够帮助解决,完善游戏。三、代码设计:importtimeimportmathimportrandomimportpygamefrompygame.constantsimport*pygame.init()"""背景图片"""classBackground(pygame.sprite.Sprite):def__init__(self,window,x,y,image_path):pygame.sprite.Sprite.__init__(self)self.window=windowself.image=pygame.image.load(image_path)self.rect=self.image.get_rect()self.rect.x=xself.rect.y=ydefupdate(self):self.window.blit(self.image,self.rect)"""被抛出的水果类"""classThrowFruit(pygame.sprite.Sprite):def__init__(self,window,image_path,speed,turn_angel,flag):pygame.sprite.Sprite.__init__(self)#游戏窗口self.window=window#导入水果图像并获取其矩形区域self.image=pygame.image.load(image_path)self.rect=self.image.get_rect()#水果抛出时x坐标取随机数self.rect.x=random.randint(0,Manager.WIDTH-10)#水果初始y坐标self.rect.y=Manager.HEIGHT#抛出时速度self.speed=speed#旋转速度self.turn_angel=turn_angel#水果抛出时与窗口下水平线的夹角弧度,因为要用到随机函数,所以取整数,使用时除以100self.throw_angel=157#水果抛出后所经历的时间,初始化为0self.fruit_t=0#旋转的总角度self.v_angel=0#水果抛出时的初速度self.v0=6#水果标记self.flag=flagdefupdate(self):"""水果运动状态更新"""#在弧度制中,一个完整的圆周对应的角度是360度,对应的弧度是2π(即360度=2π弧度)。#因此,可以通过以下公式将角度转换为弧度:弧度=角度×π/180#当角度为90度时,根据上述公式,可以计算出对应的弧度为:90度×π/180=0.5π=1.57(约)#如果水果的初始X坐标位于窗口左边区域,取抛出时弧度在70度至90度之间ifself.rect.x=Manager.WIDTH/2:self.throw_angel=random.randint(157,175)#水果旋转后的新图像new_fruit=pygame.transform.rotate(self.image,self.v_angel)self.window.blit(new_fruit,(self.rect.x+self.rect.width/2-new_fruit.get_width()/2,self.rect.y+self.rect.height/2-new_fruit.get_height()/2))#如果水果落出屏幕,没有被切,经典模式X加一,并销毁水果对象ifself.rect.y>=Manager.HEIGHT+self.rect.height:ifself.flag!=5:Manager.classic_miss+=1self.kill()#水果抛出后的运动时水平匀速运动以及竖直向上的变速运动到达最高点时下落,所以可以判断水果做的是斜上抛运动#可以利用重力加速度来求出每隔一段时间水果运动后的y坐标#公式:v0*t*sin(α)-g*t^2/2self.rect.y-=self.v0*self.fruit_t*math.sin(self.throw_angel/100)-(Manager.G*self.fruit_t**2/10)/2#计算水果在水平方向的匀速运动位移之后的X坐标#公式:v0*t*cos(α)self.rect.x+=self.v0*self.fruit_t*math.cos(self.throw_angel/100)#累加经过的时间self.fruit_t+=0.1#累加旋转总角度self.v_angel+=self.turn_angel"""水果切片类"""classHalfFruit(pygame.sprite.Sprite):def__init__(self,window,image_path,x,y,turn_angel,v_angel,v0):pygame.sprite.Sprite.__init__(self)self.window=windowself.image=pygame.image.load(image_path)self.rect=self.image.get_rect()self.rect.x=xself.rect.y=yself.turn_angel=turn_angelself.fruit_t=0self.v_angel=v_angelself.v0=v0defupdate(self):"""水果运动状态更新"""#水果旋转后的新图像new_fruit=pygame.transform.rotate(self.image,self.v_angel)#将旋转后的新图像贴入游戏窗口,注意,旋转后的图像尺寸以及像素都不一样了(尺寸变大了),所以坐标需要进行适当处理#在原先图片矩形的中心位置绘制self.window.blit(new_fruit,(self.rect.x+self.rect.width/2-new_fruit.get_width()/2,self.rect.y+self.rect.height/2-new_fruit.get_height()/2))ifself.rect.y>=Manager.HEIGHT:self.kill()self.rect.y+=Manager.G*self.fruit_t**2/2self.rect.x+=self.v0*self.fruit_tself.fruit_t+=0.01self.v_angel+=self.turn_angel"""水果刀光类"""classKnife(object):def__init__(self,window):self.window=windowself.apple_flash=pygame.image.load("./images/apple_flash.png")self.banana_flash=pygame.image.load("./images/banana_flash.png")self.peach_flash=pygame.image.load("./images/peach_flash.png")self.watermelon_flash=pygame.image.load("./images/watermelon_flash.png")self.strawberry_flash=pygame.image.load("./images/strawberry_flash.png")defshow_apple_flash(self,x,y):self.window.blit(self.apple_flash,(x,y))defshow_banana_flash(self,x,y):self.window.blit(self.banana_flash,(x,y))defshow_peach_flash(self,x,y):self.window.blit(self.peach_flash,(x,y))defshow_watermelon_flash(self,x,y):self.window.blit(self.watermelon_flash,(x,y))defshow_strawberry_flash(self,x,y):self.window.blit(self.strawberry_flash,(x,y))"""模式选项类"""classOptionMode(pygame.sprite.Sprite):def__init__(self,window,x,y,image_path,turn_angel,flag):pygame.sprite.Sprite.__init__(self)self.window=windowself.image=pygame.image.load(image_path)self.rect=self.image.get_rect()self.rect.x=xself.rect.y=yself.turn_angel=turn_angelself.v_angel=0self.flag=flagdefupdate(self):new_image=pygame.transform.rotate(self.image,-self.v_angel)self.window.blit(new_image,(self.rect.x+self.rect.width/2-new_image.get_width()/2,self.rect.y+self.rect.height/2-new_image.get_height()/2))self.v_angel+=self.turn_angel"""游戏音乐类"""classBgm(object):def__init__(self):pygame.mixer.init()defplay_menu(self):pygame.mixer.music.load("./sound/menu.mp3")pygame.mixer.music.play(-1,0)defplay_classic(self):pygame.mixer.music.load("./sound/start.mp3")pygame.mixer.music.play(1,0)defplay_throw(self):pygame.mixer.music.load("./sound/throw.mp3")pygame.mixer.music.play(1,0)defplay_splatter(self):pygame.mixer.music.load("./sound/splatter.mp3")pygame.mixer.music.play(1,0)defplay_over(self):pygame.mixer.music.load("./sound/over.mp3")pygame.mixer.music.play(1,0)defplay_boom(self):pygame.mixer.music.load("./sound/boom.mp3")pygame.mixer.music.play(1,0)"""游戏逻辑类"""classManager(object):#窗口尺寸WIDTH=640HEIGHT=480#游戏中的定时器常量THROWFRUITTIME=pygame.USEREVENTpygame.time.set_timer(THROWFRUITTIME,3000)#根据窗口大小,选取随机整数重力加速度,水果下落更有层次感,使用时除以10G=random.randint(19,21)#经典模式miss掉的水果数classic_miss=0#打开文本文件withopen('best.txt','r')asfile:#逐行读取文件内容forlineinfile:if'zen_mode'inline:zen_best=int(line.split(':')[-1].strip())if'classic_mode'inline:classic_best=int(line.split(':')[-1].strip())def__init__(self):#生成游戏窗口self.window=pygame.display.set_mode((Manager.WIDTH,Manager.HEIGHT))self.window_icon=pygame.image.load("./images/score.png")pygame.display.set_icon(self.window_icon)pygame.display.set_caption("FruitNinja")#创建游戏中用到的的精灵组self.background_list=pygame.sprite.Group()self.circle_option=pygame.sprite.Group()self.option_fruit_list=pygame.sprite.Group()self.fruit_half_list=pygame.sprite.Group()self.throw_fruit_list=pygame.sprite.Group()#导入背景图像并添加入背景精灵组self.background=Background(self.window,0,0,"./images/background.jpg")self.home_mask=Background(self.window,0,0,"./images/home-mask.png")self.logo=Background(self.window,20,10,"./images/logo.png")self.ninja=Background(self.window,Manager.WIDTH-320,45,"./images/ninja.png")self.home_desc=Background(self.window,20,135,"./images/home-desc.png")self.zen_new=Background(self.window,175,215,"./images/new.png")self.background_list.add(self.background)self.background_list.add(self.home_mask)self.background_list.add(self.logo)self.background_list.add(self.ninja)self.background_list.add(self.home_desc)self.background_list.add(self.zen_new)#创建旋转的圈并添加进精灵组self.dojo=OptionMode(self.window,Manager.WIDTH-600,Manager.HEIGHT-250,"./images/dojo.png",1,None)self.new_game=OptionMode(self.window,Manager.WIDTH-405,Manager.HEIGHT-250,"./images/new-game.png",1,None)self.game_quit=OptionMode(self.window,Manager.WIDTH-160,Manager.HEIGHT-150,"./images/quit.png",-1,None)self.circle_option.add(self.dojo)self.circle_option.add(self.new_game)self.circle_option.add(self.game_quit)#创建主菜单界面旋转的水果并添加进精灵组self.home_peach=OptionMode(self.window,Manager.WIDTH-600+self.dojo.rect.width/2-31,Manager.HEIGHT-250+self.dojo.rect.height/2-59/2,"./images/peach.png",-1,"option_peach")self.home_watermelon=OptionMode(self.window,Manager.WIDTH-405+self.new_game.rect.width/2-49,Manager.HEIGHT-250+self.new_game.rect.height/2-85/2,"./images/watermelon.png",-1,"option_watermelon")self.home_boom=OptionMode(self.window,Manager.WIDTH-160+self.game_quit.rect.width/2-66/2,Manager.HEIGHT-150+self.game_quit.rect.height/2-68/2,"./images/boom.png",1,"option_boom")self.option_fruit_list.add(self.home_peach)self.option_fruit_list.add(self.home_watermelon)self.option_fruit_list.add(self.home_boom)#设置定时器self.clock=pygame.time.Clock()#模式标记self.mode_flag=0#音效self.bgm=Bgm()#刀光self.knife=Knife(self.window)#游戏分数self.classic_score=0self.zen_score=0defcreate_fruit(self):"""创建水果"""ifself.mode_flag==1:boom_prob=random.randint(4,6)ifboom_prob==5:self.bgm.play_throw()boom=ThrowFruit(self.window,"./images/boom.png",None,5,5)self.throw_fruit_list.add(boom)fruit_image_path=["./images/apple.png","./images/banana.png","./images/peach.png","./images/watermelon.png","./images/strawberry.png"]fruit_number=random.randint(1,4)forninrange(fruit_number):rand_fruit_index=random.randint(0,len(fruit_image_path)-1)self.bgm.play_throw()fruit=ThrowFruit(self.window,fruit_image_path[rand_fruit_index],None,5,rand_fruit_index)self.throw_fruit_list.add(fruit)defcreate_fruit_half(self,fruit_flag,fruit_x,fruit_y,turn_angel,v_angel):iffruit_flag=="option_peach":"""禅宗模式的桃子被切开"""fruit_left=HalfFruit(self.window,"./images/peach-1.png",fruit_x-50,fruit_y,turn_angel,v_angel,-5)fruit_right=HalfFruit(self.window,"./images/peach-2.png",fruit_x+50,fruit_y,-turn_angel,v_angel,5)self.fruit_half_list.add(fruit_left)self.fruit_half_list.add(fruit_right)iffruit_flag=="option_watermelon":"""经典模式西瓜被切开"""fruit_left=HalfFruit(self.window,"./images/watermelon-1.png",fruit_x-50,fruit_y,turn_angel,v_angel,-5)fruit_right=HalfFruit(self.window,"./images/watermelon-2.png",fruit_x+50,fruit_y,-turn_angel,v_angel,5)self.fruit_half_list.add(fruit_left)self.fruit_half_list.add(fruit_right)iffruit_flag==0:"""苹果被切开"""fruit_left=HalfFruit(self.window,"./images/apple-1.png",fruit_x-50,fruit_y,turn_angel,v_angel,-5)fruit_right=HalfFruit(self.window,"./images/apple-2.png",fruit_x+50,fruit_y,-turn_angel,v_angel,5)self.fruit_half_list.add(fruit_left)self.fruit_half_list.add(fruit_right)iffruit_flag==1:"""香蕉被切开"""fruit_left=HalfFruit(self.window,"./images/banana-1.png",fruit_x-50,fruit_y,turn_angel,v_angel,-5)fruit_right=HalfFruit(self.window,"./images/banana-2.png",fruit_x+50,fruit_y,-turn_angel,v_angel,5)self.fruit_half_list.add(fruit_left)self.fruit_half_list.add(fruit_right)iffruit_flag==2:"""梨被切开"""fruit_left=HalfFruit(self.window,"./images/peach-1.png",fruit_x-50,fruit_y,turn_angel,v_angel,-5)fruit_right=HalfFruit(self.window,"./images/peach-2.png",fruit_x+50,fruit_y,-turn_angel,v_angel,5)self.fruit_half_list.add(fruit_left)self.fruit_half_list.add(fruit_right)iffruit_flag==3:"""西瓜被切开"""fruit_left=HalfFruit(self.window,"./images/watermelon-1.png",fruit_x-50,fruit_y,turn_angel,v_angel,-5)fruit_right=HalfFruit(self.window,"./images/watermelon-2.png",fruit_x+50,fruit_y,-turn_angel,v_angel,5)self.fruit_half_list.add(fruit_left)self.fruit_half_list.add(fruit_right)iffruit_flag==4:"""草莓被切开"""fruit_left=HalfFruit(self.window,"./images/strawberry-1.png",fruit_x-50,fruit_y,turn_angel,v_angel,-5)fruit_right=HalfFruit(self.window,"./images/strawberry-2.png",fruit_x+50,fruit_y,-turn_angel,v_angel,5)self.fruit_half_list.add(fruit_left)self.fruit_half_list.add(fruit_right)defimpact_check(self):"""碰撞检测"""foriteminself.option_fruit_list:"""主页的模式选择"""mouse_pos=pygame.mouse.get_pos()ifmouse_pos[0]>item.rect.leftandmouse_pos[0]item.rect.topandmouse_pos[1]item.rect.leftandmouse_pos[0]item.rect.topandmouse_pos[1]self.zen_best:file_path='best.txt'withopen(file_path,'r')asfile:content=file.read()content=content.replace(str(self.zen_best),str(self.zen_score))withopen(file_path,'w')asfile:file.write(content)returndefclassic_mode(self):"""经典模式"""pygame.font.init()self.bgm.play_classic()score_image=Background(self.window,10,5,"./images/score.png")text=pygame.font.Font("./images/simhei.ttf",30)#设置分数显示的字体best=pygame.font.Font("./images/simhei.ttf",20)#设置历史最好分数显示的字体x_nums=pygame.sprite.Group()miss_times=pygame.sprite.Group()xxx=Background(self.window,Manager.WIDTH-30,5,"./images/xxx.png")xx=Background(self.window,Manager.WIDTH-60,5,"./images/xx.png")x=Background(self.window,Manager.WIDTH-90,5,"./images/x.png")x_nums.add(xxx)x_nums.add(xx)x_nums.add(x)whileTrue:#设置游戏帧率self.clock.tick(60)pygame.display.update()self.check_key()self.background_list.sprites()[0].update()score_image.update()text_score=text.render("%d"%self.classic_score,1,(255,213,156))self.window.blit(text_score,(50,8))best_score=best.render("BEST%d"%self.classic_best,1,(255,179,78))self.window.blit(best_score,(10,40))x_nums.update()miss_times.update()temp_flag=self.impact_check()iftemp_flag==3:#经典模式炸弹结束游戏更新历史最高记录ifself.classic_score>self.classic_best:file_path='best.txt'withopen(file_path,'r')asfile:content=file.read()content=content.replace(str(self.classic_best),str(self.classic_score))withopen(file_path,'w')asfile:file.write(content)self.bgm.play_boom()time.sleep(0.4)returnself.throw_fruit_list.update()self.fruit_half_list.update()ifManager.classic_miss==1:xf=Background(self.window,Manager.WIDTH-90,5,"./images/xf.png")miss_times.add(xf)elifManager.classic_miss==2:xf=Background(self.window,Manager.WIDTH-90,5,"./images/xf.png")miss_times.add(xf)xxf=Background(self.window,Manager.WIDTH-60,5,"./images/xxf.png")miss_times.add(xxf)elifManager.classic_miss>=3:#经典模式正常结束更新历史最高记录ifself.classic_score>self.classic_best:file_path='best.txt'withopen(file_path,'r')asfile:content=file.read()content=content.replace(str(self.classic_best),str(self.classic_score))withopen(file_path,'w')asfile:file.write(content)self.bgm.play_over()time.sleep(0.4)Manager.classic_miss=0returndefmain(self):"""主页"""self.bgm.play_menu()whileTrue:#设置游戏帧率self.clock.tick(60)self.background_list.update()self.circle_option.update()self.option_fruit_list.update()self.fruit_half_list.update()temp_flag=self.impact_check()pygame.display.update()iftemp_flag==1:self.zen_mode()self.__init__()self.bgm.play_over()self.bgm.play_menu()iftemp_flag==2:self.classic_mode()self.__init__()self.bgm.play_over()self.bgm.play_menu()eliftemp_flag==0:self.bgm.play_over()time.sleep(0.3)pygame.quit()exit()eliftemp_flag==3:self.__init__()self.bgm.play_menu()self.check_key()if__name__=='__main__':manager=Manager()manager.main()源码:链接:https://pan.baidu.com/s/11YM7GzqzFz1QkcGbJHnDCQ 提取码:daz5
|
|