20184302 2019-2020-2 《Python程序设计》实验四报告
课程:《Python程序设计》
班级: 1843
姓名: 李新锐
学号:184302
实验教师:王志强
实验日期:2020年5月16日
必修/选修: 公选课
1.实验内容
python综合应用:使用pygame来设计游戏————Flappy bird
2. 实验过程及结果
(一)首先我们通过import语句将我们此次实验中需要使用到的库导入:
import pygame
import sys
import random
import time
第一个是python用来设计和实现二维游戏的专用库,可以在命令行中使用pip install pygame语句来下载。sys库是一个关于系统操作的库,以及后面的随机库和时间库都是python自带的,可以不用下载。
(二)我们使用pygame.init()语句来初始化pygame。
(三)关于游戏的设计我们可以想象成演员在舞台上表演,首先我们要确定一下舞台的大小,使用
size = width, height = 288, 512
screen = pygame.display.set_mode(size)
来设置窗口,288和512分别是舞台的长和宽,也可以采取将元组直接写入的方法,我这样设计可以提高代码的可读性。
(四)动画的定义就是将一张张静态的具有略微差别的图片进行快速的播放从而形成视觉暂留现象,所以在这里我们需要使用条件永远为真的循环while True。在循环中由于机器的运算是非常快的,如果太快会影响我们对动画的观看效果,所以我们需要使用clock = pygame.time.Clock()语句也就是pygame中的time模块来调节时间,以及在无限循环中输入clock.tick(60)语句也就是使用clock的tick方法,其中的60是每秒钟执行的次数。
(五)同时如果我们不想玩了需要能够随时关闭游戏,所以使用
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
语句来随时接收在pygame运行中的事件,pygame.QUIT表示点击窗口中的X按钮,sys.exit()表示退出系统程序。
(六)同时我们需要将一张背景图片放入舞台中,在这里我设置了一个随机数,从0和1中随机选取一个数,因为我这里背景设立了两个,所以如果是1的情况就使用白天的背景,反之则使用黑夜的背景,这样便增加了可玩性。使用background = pygame.image.load('./bg_day.png')语句,表示载入图片,需注意此时还没有将图片载入到窗口中,如果想载入需要使用screen.blit(background, (0,0))其中的(0,0)表示一张图片的左上角该点在窗口中的位置,在窗口中我的横向为x轴,越往右坐标越大,纵向为y轴,越往下坐标越大。如果没有背景图片素材的情况,可以使用screen.fill((0,0,0))语句来进行填充,其中的(0,0,0)rgb值,即三原色的值。
(七)pygame.display.flip()表示显示所有的加载的事物,最后在程序完成之后需要使pygame.quit()来退出。
(八)如果一场电影没有演员只要风景那么是无趣的,所以我们设置两个类,在此游戏主要的两个角色便是鸟和管道。所以我们设置鸟类和管道类
class Bird(object)和class Line(object)。到此整体框架便搭建完毕,接下来我们就需要设计具体的细节。
(九)同时我们需要产生一个思想,在这个游戏中鸟是不动的,而是管道在动,由于参照物不同所以人们看到的是鸟在动。
(十)鸟类:在该类的初始化def init(self)中一开始我们便设置了一个随机数从0到2取值,因为鸟的外貌有三个,希望增加可玩性所以每次从三个中随机选择一个。我们使用列表来保存加载到对象中的图片,这样以后可以方便操作。同时一个鸟有四个状态,一个是死亡状态,每次我们可以选择另外三个状态中的一个。将该状态在status列表中的次序设为body变量。self.status=[pygame.image.load('./bird0_0.png'),pygame.image.load('./bird0_1.png'),pygame.image.load('./bird0_2.png'),pygame.image.load('./medals_2.png')]
由于鸟不动所以我们设置它的坐标是固定的birdx和birdy。同时我们希望鼠标进行点击或者按键盘的空格键时鸟会上升,如果不按则会掉落,所以我们一开始默认它的jump也就是是否跳跃状态为False。以及最开始鸟是没有死亡的,所以它的死亡状态也为False。由于正确的游戏体验和玩法所以鸟的跳跃速度jumpspeed在一开始是大于重力速度gravity的。由于我们模拟现实所以跳跃速度会越来越慢,而下降速度则会越来越快。速度表现在动画中就是坐标的改变。
self.jumpspeed -=1
self.birdy =self.birdy-self.jumpspeed
(十一)管道类:在这个类中对于管道我们需要有上管道和下管道,我们希望管道从最左边入场所以将管道的横坐标设为self.wallx=288
同时为了增加趣味性我们希望能够每次管道的高度能够进行调整,但是需要保证留有一定的距离,所以我们将管道的纵坐标设为一个在一定范围内的随机值,管道的随机值之所以有负数是因为两个管道的长度加起来本来就比整个窗口的长度要长所以需要隐藏一部分。接下来进入管道的移动方法,因为管道的移动其实就是坐标的改变,我们希望小鸟向前飞,也就是管道向后移动,所以横坐标需要减少,当管道的右边移动到窗口的最左边也就是刚消失是我们希望它能够再循环进行,所以当横坐标为-52时就需要重新回到左边再次循环,需要注意此时横坐标不是零因为我们是以图片的左上角的点为该图片的重心,所以当图片左移完全消失在窗口中的时候,左上角的点已经完全移出去了。
(十二)分数:我们希望设置一个记录分数的计分器,所以我们导入字体
pygame.font.init() #初始化字体类
font = pygame.font.SysFont(None,70) #None表示使用默认字体,70表示字体的大小
screen.blit(font.render(":"+str(score),-1,(255,255,255)), (179, 0)) #使用该语句来显示字体
在font.render中":"+str(score)表示要显示的内容,加入强制类型转化是因为score是整型我们只能输出字符串,在这里我们希望增加程序的美观感所以我们一开始在左上角载入了一张图片,来作为表达分数记录的前缀,-1是用来代替bool值中的False,它主要是来开启抗锯齿模式,使得显示得更加美观,之后为字体的颜色,仍然使用rgb值表示。最后我们放到screen.blit中显示到窗口中。注意需要在主函数中初始化score变量为0.score变量的设置是在管道类中,我们如果按照小鸟越过一个管道即为1分即使用越过这个状态则太为繁琐,所以我们进行分析,鸟越过一个管道,则肯定没有撞上这个管道,则这个管道肯定会到达窗口最左端,所以我们及需要在管道循环中加入一个全局变量global score每次当管道到达左边时score=score+1即可。
(十三)start函数:我们希望能够不要一运行程序就开始游戏,这样会大大降低游戏的游玩体验和一运行就开始游戏会让人难以反映,所以我们在运行程序后会显示一个界面来有一个缓冲,使人可以停顿一会儿,然后点击任意按钮后开始游戏。所以我们设立了start函数来实现这个想法,主要的设计思想是我们先运行一个pygame,等到接收到键盘的信号或者鼠标的信号就退出,再运行游戏主体的pygame。在这个函数中我们同样需要pygame的初始化否则会报错,其中一个小技巧就是使用w一个整型变量,初始值为0,当发现鼠标或者键盘的点击事项后则将w的值置为1,首先退出检查事件的小循环,在while True大循环中当w的值为1时则退出大循环,这时就进入游戏的pygame的大循环中了。在开始界面我们导入了一个按钮并将界面的背景置为白色,主要是为了更加美观,实际中游戏的开始即界面的退出与按钮无关。
(十四)之后我们需要将演员搬上舞台了,轮到它们表演了!首先我们需要创建鸟类和管道类的对象:
bird =Bird()
Line = Line()
我们在大循环中的小循环事件监测中输入语句:
if (event.type == pygame.KEYDOWN or event.type == pygame.MOUSEBUTTONDOWN) and not bird.dead:
bird.jump = True
bird.jumpspeed = 10
bird.gravity = 5
表示当接受到的事件的类型为键盘点击(pygame.KEYDOWN)和鼠标点(pygame.MOUSEBUTTONDOWN)时,如果这个时候小鸟还没有死则鸟是否跳跃则为真。同时我们设置跳跃的速度值和下降的重力速度,由于前两个事件发生一个小鸟都会跳跃所以之间的关系为or,而小鸟如果死亡即bird.dead==True与则前两个不论输入什么都不会成立,所以与前两个事件是and并且需要加()。同时为了增加游戏的趣味性,我们希望小鸟进行跳跃时状态会改变也就是图片会改变,所以我们设置:
if bird.jump == True:
#bird.body =1
screen.blit(bird.status[1], (bird.birdx, bird.birdy))
else:
screen.blit(bird.status[0], (bird.birdx, bird.birdy))
也可以通过标识bird.body来改变状态。
最后引入小鸟类的bird.birdUpdate()方法。
对于管道类由于我们希望可以每次使管道出现的位置是随机的,所以使用
if Line.wallx == -52:
Line.wally1=random.randint(-230,-100)
Line.wally2=random.randint(250,320)
当管道移动到最左端,我们就改变它下一次出现的纵坐标,由于管道的图片与整个窗口相比较是偏长的,上管道的纵坐标就需要超出窗口的上边界所以纵坐标为负值,但是下管道不同,下管道的上下方向与上管道相反,管道口在上,同样其重心点在左上角所以不需要超出下边界,其具体的值需要根据与窗口以及两管道口之间的距离最小也需要鸟的高度再宽一点来确定。
同时引入管道的移动方法:
Line.lineUpdate()
同时我们需要载入一个陆地图片land=pygame.image.load('./land.png')来充当陆地,同时如果小鸟碰到陆地则也会死亡,陆地需要根据窗口的具体大小来确定一下具体位置,在这里需要注意一下下管道的距离不要隐藏在陆地中。
(十五)如果一个游戏中没有失败和死亡则是没有意思的,所以我们设定如果小鸟碰到管道或者地面则死亡或者碰到天花板死亡,下面来进行碰撞检测,我们需要将小鸟和管道想象成两个矩形,如果这两个矩形发生重叠则视为碰撞我们调用pygame中的方法来创建矩形:self.birdrect=pygame.Rect(144,256,38,38)前两个参数表示这个矩形的左上角的坐标位置我们,后两个参数为矩形的大小我们需要与小鸟的大小保持一致,我们可以在小鸟图片属性中来查看小鸟矩形的大小,其实我们看到的小鸟是一只形状不规则的小鸟图形怎么会是矩形呢?原因就是在包围小鸟的矩形中除去小鸟的实体外我们都设置为了透明颜色所以在窗口中我们看不到。接下来我们希望这个矩形根据小鸟的移动而进行移动,所以我们在birdUpdate方法中设置如下语句self.birdrect[1]=self.birdy
表示该矩形的纵坐标跟随小鸟的纵坐标移动。
对于管道同样我们需要设置符合他们的矩形,由于管道的纵坐标是随机变换的所以我们需要在变换后进行设置:
uprect = pygame.Rect(Line.wallx, Line.wally1, Line.wallup.get_width(), Line.wallup.get_height())
downrect = pygame.Rect(Line.wallx, Line.wally2, Line.walldown.get_width(), Line.walldown.get_height())
我们在其中使用了get_height()和get_width()方法来获取上管道和下管道的高度和宽度
使用矩形的colliderect方法来检测碰撞:
if uprect.colliderect(bird.birdrect) or downrect.colliderect(bird.birdrect) or bird.birdy>450 or bird.birdy<0:
其中的参数就是要检测的与之发生碰撞的矩形。与陆地碰撞我们就不需要使用这么麻烦的方法了,我们可以通过陆地的摆放来计算得知陆地在纵坐标中的位置,如果小鸟的纵坐标超过那个位置则判断为死亡,同时后面的与天花板碰撞也可以如此。一旦小鸟被判断为死亡则设置如下参数:
bird.dead = True
bird.body = 3
screen.blit(bird.status[3], (bird.birdx, bird.birdy))
pygame.display.flip()
将小鸟的死亡状态改为True,同时此时我们需要调出小鸟的死亡图片,进行加载并显示到窗口中。
此时循化还没有结束,所以我们可以做一些gameover的界面:
time.sleep(1)
pygame.init()
size = width, height = 288, 512
screen = pygame.display.set_mode((288, 512))
screen.fill((0, 0, 0))
gameover=pygame.image.load('./text_game_over.png')
screen.blit(gameover, (30, 230))
pygame.display.flip()
break
time.sleep(1)表示将程序休眠一秒钟,防止显示太快令人看不清楚,这是我们便再开始一个pygame来显示一下一个GAMEOVER的图片,同时退出大循环,再退出程序。
到此我们的程序就编辑完成了,之后便可点击运行来进行编译调试,如果出现问题也可以使用Debug来检查。
最终我们就编辑出来一款可以随着鼠标或者键盘的点击来控制小鸟上下躲避管道的游戏了。
3. 实验过程中遇到的问题和解决过程
- 问题1:在我想要创立一个新的窗口时并且此时已经有一个窗口了,如果不加入 pygame.init()则会失败
- 问题1解决方案:在想要创建的新窗口的开头加入 pygame.init(),同时要在最后的结尾才加入pygame.quit()否则会显示失败或者编译失败。或者我们可以使用一个函数来另外存放,此时的规则与前面叙述的相同。
- 问题2:对于小鸟进行跳跃的时候图片的转变没有解决。
- 问题2解决方案:对于此问题有进行过许多方法的尝试可是都没有成功,有尝试过在一开始小鸟的jump状态进行改变的时候就进行改变,也有尝试过最后将jump状态改回False,但是都会导致小鸟的下落速度奇快。现在小鸟的状态只会在第一个jump时进行改变之后会一直维持。
- 问题3:对于管道矩形的定义一开始放在了,管道纵坐标随机取值的前面,导致管道矩形是上一个管道矩形的坐标位置,使得碰撞检测出错。
- 问题3解决方案:将管道矩形的定义放在管道纵坐标随机取值的后面。
4.其他
全课总结
本学期的python课程告一段落,但是我们对python的探索却还远远没有结束,Python是我接触的第二个高级语言,高级语言相比于一些中级语言的一些简单的语法形式更加复杂了,例如for循环的实现相比于c较为麻烦,但是作为高级语言同时也有它的优势,它所能够实现的功能更加丰富了,例如我们此次所做的二维游戏,还有课上所学的爬虫,命令行等等,但是我们需要掌握许多种类的模块,库,方法,并且需要学会一定的灵活自由运用,同时能够实现多种方法相结合使用,要想实现方法,就必须了解高级语言的总体思想——面向对象的程序设计,就是需要具有封装,继承,派生,多态的思维能力。正是因为高级语言的复杂性,我们就更加需要与数学和逻辑结合紧密来构建我们的程序,这就需要我们对于一些基本的语法要特别的熟悉例如循环,if语句等等,同时对于例如变量,注释数据类型,逻辑运算符,位运算符,输入输出语句这类基本语法更是要烂熟于心,也要注意一些编码规范和命名规则来使自己的代码看起来更加专业。在Python中我们接触到了一类新的东西就是序列,有点相当于c语言中的数组,但是并不完全一致,序列包含很多,有列表,元组,字典都是会在以后经常使用的基础知识,需要我们能够牢记,对于它们的操作例如增删查改,遍历等等操作都需要熟悉。其中令我最头痛的就是正则表达式了,它是用来查找匹配的字符串的非常好用的工具,但是它的格式却特别的繁琐,实在是难以记忆。如果我们的程序都写在一个主函数中或者每次输入的数据都必须手动输入或每次程序运行完成之后的结果只能是保存一瞬,那么我们的程序就是不好的程序,是失败的程序,所以我们就了解了函数和文件的一些基本操作,函数与类相似却不同,文件的基本操作保护删除,创建,写入,读取等等可以使我们的运行结果保存在内存中,这样关机后也不会丢失。同时令我印象深刻的是raise语句,可以使用它来抛出异常,是一个简便的检查错误的语句,更加令我惊奇的是使用python来输入Excel,Word,还有MySQL,由于管理信息系统的实验正好需要使用MySQL,所以让我有了学以致用的感觉,正是一次的编译失败改正再失败再改正的循环,使我感受到了python功能的强大,让我爱上了python。
课程感想体会
一学期的Python课程使我初步了解了python这门语言,同时也能使用python来处理一些重复的工作,甚至照猫画虎地编出来我的一个游戏,能够做到即学即用这便是学习的目的和意义,所以我能够选修上这门课真的非常幸运,也使我的到来许多的对于高级语言的启发,这次的网络教学让我们能将老师的讲解和老师发布的一些视频结合使用,真的非常便捷方便,可以反复地巩固知识。老师的讲解也特别用心,幽默风趣,让人能够劳逸结合喜欢上Python,同时老师留的作业也难度适中,能够兼顾没有接触过语言的人,同时也能够对课程的重点进行练习巩固。四次的实验也能够对整个课程的中心进行把控,报告的撰写使得我们对于博客的运用和写博客的语言也做了学习,也是通过老师让我们对码云这个网站的运用可以操作自如,真的非常方便,可以随时地进行代码的编辑。还有对于英语打卡加法的活动以及一些彩蛋令人感到课程充满趣味。通过这门课真的收获满满,当然离不开老师作业和实验及其他的措施的督促,还有自己做出程序的荣誉感,对于Python的探索仍在继续。
意见和建议
实验课我更加希望老师可以交一些好玩和实用的编程,让我们更加了解一些高级语言的精妙和魅力之处。同时对于课上基本语法的讲解可以更加具体和详细一点,最好能够再多做一些演示。
程序运行视频:https://www.iqiyi.com/v_19ry17av1w.html
最终代码:https://gitee.com/li_xin_rui666/untitled1000/blob/master/ball/bird.py
运用素材:https://gitee.com/li_xin_rui666/untitled1000/tree/master/ball
5.参考资料
-
《python从入门到编程》
-
《零基础学python》
-
明日科技教学视频