使用Python开发飞机大战游戏,本文目录如下
这次用Python中的pygame
模块来完成一个飞机大战的小游戏;基本思路是通过方向键来控制飞机的左右移动射击飞船。先来看下最后的效果
为了新手也能完成,本文记录了编写的全部流程,也就是每次修改的代码也包括在内,并且给大多数代码都加上了能看懂的注释,看一下最终的的统计字数
一共敲了4万个字符,希望能帮到感兴趣的读者!
制作小飞机
搞起来
目标:创建一个可以左右移动的小飞机,用户可以通过空格space
键来控制飞机发射子弹。创建背景创建一个空背景
首先编写一个空的pygame
窗口,文件名为plane_war.py
display.set_mode
返回的是一个Surface
数据类型
效果图创建设置类
一个游戏通常有n多个设置,如果每次想改变其中的某一个值的话在主文件中寻找容易眼花缭乱,现在创建一个新的文件settings.py
,专门用来存储这些信息
添加小飞机
这里用到的小飞机绘制小飞机
现在图像也有了,来创建一个plane.py
模块,其中有一个Plane
类,来存储飞机的各种行为
get_rect
会返回Surface的矩形的区域,.centerx
和.bottom
是其两个属性
改写plane_war.py
将小飞机绘制在屏幕上
效果图创建一个存储运行函数的模块
为了不使plane_war.py
太长而影响阅读,来创建一个名为game_func.py
的模块,用其飞机大战运行的函数,使其逻辑更容易理解
check_events
函数用来完成窗口不会关闭的功能,update_screen
用来完成更新图像的功能,有3个形参,Surface对象、背景图像、小飞机函数
因为check_events
完成了退出游戏的操作,所以plane_war.py
就不需要sys模块了,更新后的plane_war.py
如下
控制小飞机
通过修改小飞机的坐标来完成移动,在用户按下方向键的时候小飞机的坐标进行有规律的变化控制小飞机移动
当用户按键时,都会在pygame
中注册一个事件,任何一个事件都是通过pygame.event.get()
获取的,因此可以在函数体内,为每个按键都注册一个KEYDOWN
事件。
现在将check_events
函数改写,通过检测按下键位,来对小飞机进行移动
现在按一下小飞机移动一个像素,一般的游戏都是通过按下不送则一直移动,Pygame
中的pygame.KEYUP
可以检测用户是否松开按键现在结合KEYDOWN
和KEYUP
来完成一个持续移动控制小飞机持续移动
来定义一个标志位,来判断用户是否按下按键,默认为Flase
一旦检测到用户按下俺家则为True
,小飞机就可以持续移动
由于小飞机是通过plane.py
文件来控制的,对这个文件进行改写
update
方法是标志位为True时,小飞机就开始移动
改写game_func.py
中的check_events
函数
最后只要在plane_war.py
中调用update
方法就可以完成持续移动的操作完成左右移动
用同样的方法完成向左移动
改写后的plane.py
文件
改写后的game_func.py
中的check_events
函数
调整速度
现在的小飞机一次是按1px来移动的,那速度是相当的缓慢,修改一下小飞机的移动速度
首先在setting.py
中添加一行
现在对plane.py
做修改
将plane_war.py
中的plane
增加一个属性
限制小飞机的活动范围
现在小飞机已经可以飞呀飞,但是没有东西限制他,很容易就飞出了屏幕。现在将其限制在屏幕中,避免飞出去。
只需要修改plane.py
中的update
方法重构game_func.py
中的check_events
函数
随着小飞机的功能愈来愈多,现在将check_events
重构为3个函数,捕捉用户按键和用户松开键分别定义两个函数
重构后的check_events
效果图完成射击功能
通过玩家按下空格来发射子弹(一小小小的矩形)添加子弹的设置
在settings.py
中的__init__
方法中添加以下数据
创建Bullet类
创建存储子弹的Bullet
类的bullet.py
文件
Bullet
类继承于pygame.sprite
中的Sprite
类,此类可以将游戏中的元素进行编组,可以同时操作编组中的所有元素将子弹存储到编组中
首先在plane_war.py
中创建一个编组,用于存储所有有效的子弹,以便能够管理发射出去的子弹;这个编组是pygame.sprite.Group
类的一个实例;pygame.sprite.Group
类类似于列表,但是提供了有助于开发游戏的额外功能。在主循环中,我们将使用这个编组在屏幕上绘制子弹,以及更新没颗子弹的位置。
开火
通过修改game_func.py
中的函数来完成发射子弹的操作
用户按下空格之后会创建一个子弹(一个名为new_bullet的Bullet实例),并使用add
追加到编组中方法bullets.sprites
返回一个列表,包含了编组中的所有精灵,遍历编组中的精灵,并通过draw_bullet()绘制到屏幕上效果图:
现在已经完成基本的射击功能了,虽然子弹到达屏幕顶端后消失了,这仅仅是因为pygame
无法绘制屏幕外面的东西,这些子弹实际还是存在的,他们的y
坐标为负数且越来越少,会继续消耗内存删除已经消失的子弹
这里通过.copy
进行浅拷贝,然后检测子弹是否消失,然后再将其删除
对plane_war.py
中的while
语句中添加下面这一句
注意:在fg.update_screen
之前进行添加限制子弹的数量
为了不使这个小游戏跟开挂似得,肯定要限制一下发射子弹的数量,在settings.py
中添加一行
在check_keydown_events
函数体中增加一个判断即可简化plane_war.py
中的while
语句
将发射子弹移步到game_func.py
文件中并创建一个update_bullets
此时的while
语句中就4行代码
小飞机添加完毕的效果制作飞船
现在小飞机也创建完成了,现在就该创建小飞机的敌人了,同样通过一个类来控制其所有行为,先来看看这个卡哇伊的飞船
目标:创建好非常让其随意移动,可以射杀飞船、当飞船碰到小飞机GAMEOVER,飞船碰到地面也GAMEOVER创建飞船创建Spaceship
类
创建一个名为spaceship.py
的文件来存储Spaceship
类
这里除了位置基本与Plane
类相同实例化Spaceship
类
在plane_war.py
中添加Spaceship
实例
这里导入了一下新创建的Spaceship
类,在while
循环外创建一个实例,给update_screen
传递一个飞船的实例让飞船出现在屏幕上
修改update_screen
函数
注意其顺序
现在这个好看的小飞船已经出现在了屏幕的左上角创建一群小飞船
要绘制一群小飞船,需要确定一行能容纳多少个飞船以及要绘制多少行飞船。确定一行可以容纳多少个飞船
确定一行可以容纳多少个外星人,需要看一下可以用的水平空间有多大。我们的游戏的屏幕宽度在settings.py
中的screen.width
存储,但需要在屏幕两遍都留下一定的边距,把它设置为小飞船的宽度。由于有两个边距,可以放置飞船的的水平空间为屏幕的宽度减去飞船宽度的2倍
公式为
根据这些公式来创建飞船创建一行飞船
为了创建一行飞船,首先在plane_war.py
中创建一个spaceships
的空编组用来存储全部的飞船,在调用game_func.py
中创建飞船群的函数
改造game.func.py
文件并编写创建飞船群函数create_fleet
效果图
因为一个飞船的宽度是占两个的位置,所以最后的空隙有点大,后期反正这个飞船是动起来的,这里先暂时忽略
将create_fleet
改写一下,拆分为三个函数体
添加多行小飞船
添加多行就跟一行添加多个是类似的,同样用屏幕的高度减去飞船高度的2倍,这里需要注意的是为了不让小飞机死的很快下面留两倍的高度,还要减去小飞机的高度
在game.func.py
中进行改写
这个写的话游戏刚开始我们的飞机就死掉了,现在来做一下修改
首先修改`spaceship.py
由于其高度进行了改变,原来的公式也要进行相应的改变
available_space_y = setting.screen_heitght - 7 * spaceship_height - plane_height # 由之前的3变为7(因为高缩小了一般)
效果图让飞船动起来
首先在settings.py
中增加小飞船相应的设置
在spaceship.py
中增加判断是否位于边缘的方法和移动的方法
在game_func.py
中对spaceship.py
方法实例化
最后在主文件的while
语句中增加
射击飞船
现在子弹和飞船碰撞在一起飞船并不会消失,而是从飞船上穿了过去,并没有达到射击飞船的效果,现在我们将完成这种效果
在这里我们使用game.sprite.groupcollide()
方法,此方法检测两个rect
是否有元素重叠,并返回一个字典检测子弹与飞船碰撞
子弹击中飞船后飞船需要马上消失,所以需要在更新子弹的位置后面检测碰撞
方法game.sprite.groupcollide()
将每个子弹的rect
和每个飞船的rect
进行比较,返回一个字典,其中包含了发证碰撞的子弹和飞船。这个字典中每个键都是射中飞船的一颗子弹,相应的值为被击中的飞船
在函数update_bullets()
中来检测碰撞
修改plane_war.py
中的fg.update_bullets
为其增加一个参数
生成新的飞船
当把所有的飞船非射击完毕以后,其不会生成新的飞船
这里需要在update_bullets()
之后来判断其长度是否为0,如果为0则调用create_fleet
测试效果
我这里为了测试我将子弹的宽度给修改了自己写的游戏想怎么改就怎么改,游戏意思,哈哈~总结游戏结束
当然了,这么玩就失去了游戏的乐趣了,肯定是不可以啊。
现在就增加难度,当飞船碰到飞机、飞船到达地面时就要搞点事情了,不过也不能不给小飞机机会