http://blog.sysuschool.com/u/mygod/index.html
请稍候,载入中。。。
 
请稍候,载入中。。。
2020-11-3 9:08:00
博文_经典写法之pygame飞机大战
这学期高一开始教python语言了,另外代课初二信息技术课,上了一个多月电子表格,也开始转python(初二第二单元),初中的python较高中的,内容简单些,没有提函数,只有基础语法部分,即变量、分支、循环,然后直接用模块了,如海龟画图、opencv图像识别,而高中的课,范围就广了,python基础这块,涉及了函数定义,但还是没有提对象和面向对象,另外涉及应用的模块就多了,数据库、UI的tkinter、游戏的pygame、数据分析的matplotlib、numpy等,但是都没有深度,也没有足够的课时支持,在学业考试考纲没有定论前,难以取舍。

最近一次区教研,几位老师提到上课时学生喜欢玩chrome自带的小恐龙游戏(嵌入在chrome浏览器中的游戏),只能卸载浏览器,倍感头疼,我们的机房也同样有这个问题,学生一有机会就会偷玩,防不胜防,上个月我将学生端的chrome隐藏起来,并半开玩笑说,带学生自己写一个小恐龙游戏。

最近,也想试试来写这个游戏,上周六将之前的黑马大本营的教程翻出来,再次看了看飞机大战游戏代码,记得2019年6月份写过一次关于飞机大战的博文,那次是刚接触python语言,刚学完python的面向对象,写的是对python的面向对象的认识,这次再见,感觉倍感亲切,而且发现代码写的非常经典,非常好的面向对象范例,忍不住想写点东西标记下。

这一年来,先后学习了python的网络编程、数据库、前端框架、爬虫、数据分析,也许阅读的代码量多了,也许理解得更深了,现在回头才发现,原来这个飞机大战写得太经典了,不知道是不是官方的范例,我按这种写法,写了下小恐龙的游戏,完成了基本逻辑实现,还差一点点动作不理想,回头再解决,下面结合这次写游戏的体会,再聊聊飞机大战。

我看的飞机大战代码,不知道是不是经过处理的版本还是别的,事件处理这块简单了些,没有飞机爆炸、飞机飞行的动画实现,也没有暂停或重新开始、没有得分记录这些,但面向对象这块的写法却是非常经典。

首先,游戏分两个文件(模块化),一个plane_sprites.py,将全局变量如窗口尺寸、用户事件、刷新率和创建对象的类定义在这里,作为导入模块;一个plane_main.py为主游戏模块,本篇附两个文件,有兴趣自行下载参详。

plane_sprites模块中,类的定义也非常经典,首先定义一个父类GameSprite,继承pygame.sprite.Sprite类,其他创建对象用的类均继承于GameSprite类。

在GameSprite类的初始化方法要求传入图片文件参数和默认参数speed,speed默认1,在类中定义实例属性image、rect、speed,其中image为图片加载的surface对象,rect为该surface对象通过get_rect获取的pygame.Rect对象,保存surface对象的宽高和位置,并可设置用来定位和移动,speed为对象移动的增量。

在GameSprite类中,定义的update方法,由于类继承于pygame.sprite.Sprite类,所有实例对象可以通过精灵组的update调用,该方法定义了实例属性rect的y变化。

Hero类、Background类、Enemy类、Bullet类,均继承GameSprite类,他们的初始化方法中传入各自的图片文件和需要的speed值(可理解为初始值),Background类继承了默认speed、Hero传入speed为0(通过键盘事件修改)、Enemy接受默认值并修改为随机数、Bullet则传入speed为-2(子弹方向相反),他们都拥有实例属性image、rect、speed,并在初始化中设置了初始位置(除Background与窗口一致、Bullet在创建子弹时设置),都继承了update方法(除Hero重写),并进行了位置限制或越界死亡设置,此外Hero类定义了一个fire方法,用于发射子弹(用Bullet类创建),声明了一个bullets属性,为子弹精灵组对象,创建的子弹加入到该精灵组。

这里的self可理解为“谁的”,如Hero类,英雄的image、rect、speed、bullets属性,英雄的update、fire方法,英雄的fire方法中,用Bullet类创建子弹,并加入到英雄的bullets中(说句帮助理解的话:代码中创建的子弹bullet不是英雄的,但bullets是英雄的)。同理,可以用“谁的”去读其他类!

其二、游戏主模块plane_main.py,就一个主游戏类PlaneGame,将所有游戏逻辑实现都放在这个类中,主程序就两句代码,一句用该类创建游戏对象,一句调用该对象的start_game方法,进行游戏。

这里先说明下,我想因为是范例,游戏代码并不完整,非完整游戏,一旦运行,直接进行打飞机,英雄死亡后立刻退出,没有暂停、重新开始、得分记录、飞机炸毁动画、晋级等代码,需要自行添加。

下面按照程序执行过程理理该模块,注意按前面说的“谁的”去理解代码,这里是指“游戏的”:

1、初始化__init__(self)
定义了游戏的screen(窗口)、clock(时钟,调刷新率用)、用户事件的定时器
调用私有方法__create_sprites,创建了游戏的back_group(精灵组,含两个bg对象)、游戏的enemy_group(精灵组,空),游戏的hero对象、游戏的hero_group(精灵组,加入了hero对象),这些精灵组和hero对象均为游戏对象的属性!

2、私有方法__create_sprites,用于创建上述精灵组和对象,在初始化方法中调用,如果游戏需要重新开始而保留得分最好记录,可考虑在重新开始的函数或方法中调用,记得在调用前,用group.empty清空所有实例对象。


3、私有方法__event_handler,用于事件监听,并执行响应,包括监听退出、用户事件(发射子弹、创建敌机)、键盘按键;其中发射子弹事件,调用游戏的hero对象的fire方法,将发射的子弹加入到游戏的hero对象的bullets中;创建敌机事件,创建的敌机,加入到游戏的enemy_group中;键盘事件修改游戏的hero对象的speed。

4、私有方法__check_collide,用于碰撞检测,用pygame.sprite.groupcollide()判断两个精灵组是否碰撞,如子弹和敌机精灵组,可设置是否立刻消失,如果需要安排敌机炸毁动画,可设置其False,然后用开关变量传递该碰撞消息,以进一步动画之。用pygame.sprite.spritecollide()可判断对象和精灵组是否有碰撞,如hero对象与敌机精灵组,简单可让对象调用kill方法杀死自己,如果要动画效果,可传递消息出去。

5、私有方法__update_sprites,这个用于精灵组的所有精灵更新的,其实是根据image、rect重新绘制,对所有精灵组,即back_group、enemy_group、hero_group、bullets,进行update和draw。

6、主游戏方法start_game,循环调用事件监听__event_handler、碰撞检测__check_collide、更新__update_sprites,用while True死循环,注意设置刷新率,最后别忘了pygame.display.update()绘制窗口。

就这些了,给自己留个标记,也许下次看到又有别的想法。







mygod | 阅读全文 | 回复(0) | 引用通告 | 编辑
发表评论:
请稍候,载入中。。。
公告
请稍候,载入中。。。
时间记忆
请稍候,载入中。。。
最新日志
请稍候,载入中。。。
最新评论
请稍候,载入中。。。
最新回复
请稍候,载入中。。。
我的好友
我的相册
站点信息
请稍候,载入中。。。
生活因感动而精彩,理想在创造中放飞
Powered by Oblog.