20182324 2019-2020-2 《Python 程序设计》实验4报告
课程:《Python 程序设计》
班级: 1823
姓名: yyh
学号: 20182324
实验教师:王志强
实验日期:2020年6月10日
必修/选修: 公选课
1. 实验内容
-
Python综合应用:爬虫、数据处理、可视化、机器学习、神经网络、游戏、网络安全等。
-
本次实践选择 pygame 制作微信的飞机大战游戏,采取主要代码参考网上,细节处理自己改动的方式
-
为什么要选择 pygame ?
爬虫太简单,只需要掌握基础 HTML 知识即可,在学习了 Web 程序开发后对爬虫有了一定的了解;数据处理是一个值得学习的领域,做好了能够实现自动化,目前已有初步了解; pygame 从来没碰过,是一片全新的值得挑战的领域;机器学习、网络安全较难,在有限的时间内暂不考虑。相比之下,从学习更多新知识的角度考虑,我选择了有一定难度与综合性,但又不至于花费太多精力,能够快速入门的 pygame 进行实验。 -
这次实验的代码完全是自己写的吗?
我可以很负责地说:不是。因为自己对于 pygame 完全是零基础,因此主要代码是参考网上别人写的,进行一些具有自己想法的改动。正如上学期学 Java 时王老师所说:加点代码,改点代码是理解的最好方式。 -
为什么没有自己从头至尾地写代码?
众所周知,互联网带来很大的一个好处就是可以借鉴。与其从什么是 pygame、怎么创建 pygame 这样一章一章地固化学习(甚至很可能学完就忘了),最后做出一个完全不实用、不好看的程序,不如直接在别人的基础上进行扩展,通过推敲与学习别人是怎么写这些代码的,从而达到理解这些方法的效果。 -
自己在这次实验上进行了哪些改动?
这次实验查找的网上代码主要实现了飞机的左右水平移动、发射子弹等功能。自己在其基础上,增添了飞机上下竖直移动、鼠标按键发射子弹、结果显示与统计等功能。 -
在这些改动中学到了什么?
通过学习既有代码,了解了 pygame 游戏代码的基本结构,了解了事件的组成,游戏的进行即是一个又一个事件的发生与重复;通过研究既有左右移动的功能,对键盘事件有了一定的认识,并在此基础上扩展至了上下左右移动(包括我们熟知的 WSAD 移动);主动学习了鼠标事件,实现了鼠标按键发射子弹的功能;主动学习了 EasyGUI,实现了登录与统计界面,也为下一步的可视化设计打了一定基础。
-
2. 实验过程及结果
-
直接借鉴的主程序就不用说了,主要谈谈自己进行的改动
-
(1) 通过 pygame.key.get_pressed() 事件判断键盘按键,实现 ↑ ↓ ← → 和 W A S D 两种按键方式对飞机进行移动
# 右键或D if keys_pressed[pygame.K_RIGHT] or keys_pressed[pygame.K_d]: self.hero.speed = 4 # 左键或A elif keys_pressed[pygame.K_LEFT] or keys_pressed[pygame.K_a]: self.hero.speed = -4 # 上键或W elif keys_pressed[pygame.K_UP] or keys_pressed[pygame.K_w]: self.hero.speed = -4 # 下键或S elif keys_pressed[pygame.K_DOWN] or keys_pressed[pygame.K_s]: self.hero.speed = 4
通过按键判断是水平还是竖直方向上的移动
if keys_pressed[pygame.K_RIGHT] or keys_pressed[pygame.K_LEFT] or keys_pressed[pygame.K_a] or keys_pressed[pygame.K_d]: # 我方战机在水平方向移动 self.rect.x += self.speed elif keys_pressed[pygame.K_UP] or keys_pressed[pygame.K_DOWN] or keys_pressed[pygame.K_w] or keys_pressed[pygame.K_s]: # 我方战机在竖直方向移动 self.rect.y += self.speed
-
(2) 通过 pygame.key.get_pressed() 判断键盘按键实现空格发射子弹,并通过 pygame.mouse.get_pressed() 判断鼠标按键实现鼠标左键发射子弹
# 使用键盘提供的方法获取键盘按键 - 按键元组 keys_pressed = pygame.key.get_pressed() # 使用鼠标提供的方法获取是否按下鼠标左键 mouse_pressed = pygame.mouse.get_pressed() # 空格或鼠标左键发射子弹 if keys_pressed[pygame.K_SPACE] or mouse_pressed[0]: # 1. 创建子弹精灵 bullet = Bullet() # 2. 设置精灵的位置 bullet.rect.bottom = self.rect.y bullet.rect.centerx = self.rect.centerx # 3. 将精灵添加到精灵组 self.bullets.add(bullet)
-
(3) 启动游戏需输入用户名,若不输入则系统自动指派一个,该用户名仅用于统计排行榜。通过 EasyGUI 的 enterbox 实现。
# 输入用户名 userid = g.enterbox("请输入用户名:", "登录", "20182324yyh") # 如果不输入用户名,则为其默认指定一个 if userid == "" or userid == None: userid = "游客" + str(random.randint(1000, 10000))
-
(4) 在游戏界面的左上角实时显示玩家名称与得分。通过 pygame.font.SysFont 和 screen.blit 方法实现。
game_user = pygame.font.SysFont('simsunnsimsun', 20, True) # 字体 self.screen.blit(game_user.render(u'%s' % userid, True, [0, 0, 0]), [20, 20]) # 显示用户名 game_font = pygame.font.SysFont('simsunnsimsun', 20, True) # 字体 self.screen.blit(game_font.render(u'当前得分:%d' % sum, True, [0, 0, 0]), [20, 40]) # 显示实时得分
-
(5) 游戏结束后弹框显示本次游戏的得分,并可选择查看排行榜。得分弹框通过 EasyGUI 的 ccbox 实现,排行榜通过 EasyGUI 的 textbox 实现,数据存入 sqlite 数据库中。数据库中共有三个字段:Unix 时间戳(主键)、玩家名称、玩家得分。在输出排行榜时需对 Unix 时间戳进行还原处理。
sql() box = g.ccbox("用户 " + userid + " 您本次游戏得分:" + str(sum), "游戏结束", ("确定", "排行榜")) if box == 0: s1 = " 时间 玩家 得分 " s = "" yyh20182324 = sqlite3.connect("排行榜.db") cursor = yyh20182324.cursor() cursor.execute('select * from Ranklist order by core desc') for line in cursor.fetchall(): # 月 if time.gmtime(line[0])[1] < 10: month = "0" + str(time.gmtime(line[0])[1]) else: month = str(time.gmtime(line[0])[1]) # 日 if time.gmtime(line[0])[2] < 10: day = "0" + str(time.gmtime(line[0])[2]) else: day = str(time.gmtime(line[0])[2]) # 时 if (time.gmtime(line[0])[3] + 8) % 24 < 10: hour = "0" + str((time.gmtime(line[0])[3] + 8) % 24) else: hour = str((time.gmtime(line[0])[3] + 8) % 24) # 分 if time.gmtime(line[0])[4] < 10: minute = "0" + str(time.gmtime(line[0])[4]) else: minute = str(time.gmtime(line[0])[4]) # 秒 if time.gmtime(line[0])[5] < 10: second = "0" + str(time.gmtime(line[0])[5]) else: second = str(time.gmtime(line[0])[5]) calendar = str(time.gmtime(line[0])[0]) + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second s = s + calendar + " " + line[1] + " " + str(line[2]) + " " g.textbox(s1, "排行榜", s) yyh20182324.close()
调用数据库的方法
# 排行榜数据库文件 def sql(): yyh20182324 = sqlite3.connect("排行榜.db") cursor = yyh20182324.cursor() cursor.execute('create table if not exists Ranklist(time int(10) primary key, userid text, core int)') cursor.execute('insert into Ranklist(time, userid, core) values (?, ?, ?)', (int(time.time()), userid, sum)) yyh20182324.commit() yyh20182324.close()
-
(6) 游戏过程截图
3. 实验过程中遇到的问题和解决过程
-
问题 1 :使用
pip install --upgrade pip
命令升级 pip 后,安装模块时提示:ModuleNotFoundError:No module named pip._internal -
问题 1 解决方案:系 pip 升级失败。参考 pip升级时报错--- No module named 'pip._internal' 文章,使用 python get-pip.py --force-reinstall 命令强制重装 pip
-
问题 2 :使用全局变量统计得分时报错 UnboundLocalError: local variable 'sum' referenced before assignment
-
问题 2 解决方案:在函数内部对全局变量进行操作时,应先使用 global 声明,否则程序将把该变量视为未定义的局部变量
-
问题 3 :使用 pygame.font 显示文字时报错 pygame.error: font not initialized
-
问题 3 解决方案:在主程序开始前必须执行
pygame.init()
使 pygame 初始化,或执行 pygame.font.init() 使字库初始化 -
问题 4 :pygame 无法显示多行文字,使用 " " 也无效
-
问题4解决方案:
Font.render
不支持多行显示,想要显示多行文字,只能创建多个 render 对象进行逐行显示 -
问题 5 :pygame 中文显示乱码
-
问题 5 解决方案:字体引用不正确。可使用
pygame.font.get_fonts()
来查看字体名,例如 宋体 对应 simsunnsimsun ,pygame.font.SysFont('simsunnsimsun',20)
-
问题 6 :enterbox 取消输入用户名后报错TypeError: can only concatenate str (not "NoneType") to str
-
问题 6 解决方案:enterbox 不输入任何东西返回 None,即 Java 中的 null,表示空值。None 的类型为 NoneType,要想判断该类型,直接判断该变量值是否等于 None 即可。注意不是 null。
其他(感悟、思考等)
一学期的课程又结束了,一期一度的大作业真的很考验人的综合运用能力。这学期的 Python 总体而言没有上学期的 Java 累,可能也是选修课与必修课的区别。通过对 Python 的学习,真真切切地感受到它的方便快捷,不愧为今天最受欢迎的编程语言。总的来说上王老师的课能够学到很多东西,不管是主动学习还是被动学习,只有真真正正学到了才能做出成果,相比于闭卷考试,这种考核方式更值得提倡。
我对这门课的建议是:坚持写 blog 的优良传统不要放弃
最后: