所用技术和软件
python 2.7
pygame 1.9.3
pyCharm
准备工作
安装好 pygame 在第一次使用 pygame 的时候,pyCharm 会自动 install pygame。
下载好使用的素材。
技术实现
初始化 pygame
首先要初始化 pygame ,之后设定一些基本的要点,比如窗口大小(尽量避免魔法数字),窗口标题以及背景图像。pygame 通过加载图片,最后返回一个 surface 对象,我们不需要关系图片的格式。但是通过 convert() 这个函数,会使我们的图片转换效率提高。
默认图片左上角为原点 (0,0)。
显示窗口
如果我们这样设定,当我们运行的时候,窗口会一闪而过,并不会出现我们想象的画面。因为窗口只是运行一下就会关闭,所以我们要写一个循环,使窗口一直保持出现。当然如果我们简单的写一个 while True那么我们的程序就出现了死循环,卡死。
所以还需要写个退出。
显示飞机
首先我们要初始化我们的主角飞机
仍旧需要加载我们需要的资源,我们的资源文件里已经准备好各种各样的飞机,但是他们都在一张切图上。
同时我们的资源文件里还有一个叫做 shoot.pack 的文件,里面记录了每个图片所在的位置。
我们通过下面的代码加载资源图片,并且获得我们需要的主角飞机。
效果如下
让飞机 “飞” 起来
飞机已经出现在我们的屏幕上了,现在需要让飞机动起来让他可以上下左右的移动。
首先要获取键盘事件,获取键盘上什么按键被按下。
key_pressed = pygame.key.get_pressed()
通过 key_pressed 获取当前的键盘按键。并进行判断,这里写了四个函数进行对 player 移动。
下一步就是完善这四个方法。
简单的说就是按下方向键的时候(w,a,s,d)飞机向四周移动,但是不能移动离开屏幕。
此时我们就应该把我们的飞机形成一个类,类里面有控制飞机的方法。
这里写类比较麻烦一点
Player的出现
首先要明确一点,这个类需要什么。
我们之前对 player 有什么操作?定义了他的图片和他出现的位置。所以我们的构造方法就要初始化这些值。所有的这些对象,我们在 pygame 里叫做精灵(sprite),这个概念也在其他游戏开发中使用。
简单的说就是获取飞机的图片,初始化飞机的矩形区域。rect 该属性会获得四个值。分别是左上角 x ,y 坐标,矩形的宽度。topleft 初始化飞机的左上角坐标,也就是飞机出现的位置。如下图所示。
player的控制
当飞机出现了,我们就应该实现我们在循环里写的方法。我们首先要判断它还在不在屏幕内,不能让飞机飞出屏幕。可以通过 rect.top,rect.bottom,rect.left,rect.right四个方法获取飞机图片的上下左右四个边界值。
这样我们就能对飞机进行判断
这里的 move 是我们对飞机的移动的位移定义的常量。
让子弹飞
子弹要沿着发射方向射出去。可以在屏幕上一直移动,直到移出屏幕。
我们只要有定义一个子弹对象,让这个对象显示在屏幕上就可以。先定义飞机子弹类,基本和定义 player 一样,获得图片,裁剪图片,设置图片初始位置,在屏幕上显示图片
运行结果
下一步就是让飞机的子弹跟随飞机。我们需要在 Player 类里面添加方法。首先我们规定,按下空格发射子弹。
这样我们的子弹就会跟随飞机出现。
下一步就是让子弹在屏幕上移动。
创建移动的方法。
player 的飞机就算基本绘制好了
绘制敌机
下一步就是绘制敌机。敌机是从屏幕上方移动到屏幕下方。我们任就需要一个类来设置敌机。设置类任就和我们前面的差不多,加载资源,设置 rect,设置位置。
最后在屏幕显示出来
1
screen.blit(enemy_img, enemy_rect)
现在我们就应该想想敌机的特点了,其实他和子弹的特点基本一直,只不过方向不一样而已。还有一点是敌机是随机生成的。
移动实现
碰撞检测
飞机和敌机还有子弹都有了,我们现在需要进行完成碰撞检测。有下面几种场景。
敌机和玩家碰撞在一起
子弹和敌机碰撞在一起
无论是那种情况的碰撞,其实就是两张图片有了交集。如图pygame 给我们提供了碰撞检测的方法。首先两个对象必须是 sprite 。通过 pygame.sprite.collide_rect() 进行碰撞检测。
我们先进行一个测试
if pygame.sprite.collide_rect(enemy, player):print'检测成功'
结果
检测成功
此时我们就可以完成,当玩家和敌机发生碰撞,游戏结束,当子弹和敌机碰撞,敌机消失。
同样的 pygame 给我们提供了一个 pygame.sprite.groupcollide() 用于 Group 之间的碰撞检测.当发生碰撞的时候这两个对象都会在 Group 中移出。
用于检测敌机和子弹
pygame.sprite.groupcollide(enemies, player.bullets, 1, 1)
敌机和子弹的关系已经和好的处理。处理敌机和玩家飞机的关系。
我们需要在 Player 里添加一个属性判断当前玩家是否被击中的 boolean 值.当集中的时候把属性改为 True.当为 True 的时候游戏结束.也就是我们一开始设置的循环就会结束.所以我们需要更改之前写的一些代码,使它更加完善。
在 Player 类里面添加是否击中属性。
执行结果
当玩家被击中的时候,在显示一张 GameOver 图片提示
做到这里基本算是实现了飞机大战.但是还有很多细节处理。
细节处理
精细的碰撞检测
从图上看,当敌机看似还没有和我们接触时,但是已经 GameOver 了。实际情况是这样的,所有的图片都是矩形,当两张图片的矩形边框线碰撞的时候,就算两个对象碰撞,所以我们要更加精细的使用碰撞检测。
我们可以按着图片中心的某个长度为半径,在这个半径内发生碰撞才是碰撞。
pygame 给我们提供了这样的方法。pygame.sprite.collide_circle_ratio() 可以自己算出一个半径,作为检测半径。并且可以做出一个有效检测的百分比。
动画
做了怎么就,感觉它没有一点动效,感觉死气沉沉的。无论是飞机飞行,还是飞机被击中,都没有一个明确的反馈。对于2d游戏,动画其实就是一张一张的图片不停的变化。就和电影的原理类似。要想让我们的飞机动起来,我们需要定义一个列表来存放这些图片,然后写个循环,让他一直不停的更换图片就好。
首先我们更改我们的主角 Player任就是老套路,加载图片。把加载的图片放到list 里。
飞机正常飞行的图片只有两张。所以我们要循环变化这两张图片。所以每发射一个子弹,图片变化两张。
正常发射子弹的动画效果已经做完。我们还需要进行被击中爆炸的动画效果。
击中的原理和正常也一样。只不过先要判断当前飞机状态,是否被击中。
248,30,495,1457 这些数字是什么?如何计算出来的。先说 495 这个数字。495 这个数字很随便,只是控制子弹的发射间隔。完全可以自定义。但是495这个数字一旦确定,其他三个数字基本确定。248 为 495 的一半,因为发射一个子弹,图片要变化两张。30 这个数字基本也是自定义的,只要比1大就好,他影响了结束动画出现的时间。
1488 这个数字是通过 248 确定的,是 248 的 6倍,因为飞机被射击后会有四张图片的显示。同理,把敌机接触子弹的动画写出来。
加载图片
之后的处理逻辑基本相似,不多介绍
这样的话基本完成了动画效果。
音乐
有了动画还的有音乐。
音乐的处理只要在特定的地方播放音乐就好,比如子弹发射的时候,背景音乐,被击中的时候,游戏结束的时候,等等。他们的处理逻辑都一样。先加载资源,然后在播放。
背景音乐的播放。
pygame 在处理背景音乐的时候都在 pygame.mixer 方法中。其中播放音乐的play中的参数,第一个为播放几次,-1 为循环播放,后面的浮点表示 从第几秒开始播放。
其他音乐处理一样,不多解释。
分数&等级
分数
首先绘制得分情况,在屏幕上显示多少分。
绘制字体基本和绘制精灵是差不多的。首先要生成字体两个参数分别是字体和字号
等级
有了分数,那么再加点等级会使游戏更加有趣味性。
同样的先绘制等级。
基本到这里算是写了一个相对完整的游戏。
我有一个微信公众号,经常会分享一些python技术相关的干货;如果你喜欢我的分享,可以用微信搜索“python语言学习”关注
欢迎大家加入千人交流答疑裙:699+749+852