python 之路,200行Python代码写了个打飞机游戏!
早就知道 pygame 模块,就是没怎么深入研究过,恰逢这周未没约到妹子,只能自己在家玩自己啦,一时兴起,花了几个小时写了个打飞机程序。
很有意思,跟大家分享下。
先看一下项目结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | """ PlayPlane/ |-- bin/ | |-- main.py 程序运行主体程序 |-- config/ | |-- settings.py 程序配置(例如: 游戏背景音乐的加载等) |-- material 程序素材放置(打飞机游戏素材放置) |-- ... |-- src/ 程序主体模块存放 | |-- __init__.py | |-- bullet.py 我方飞机发射子弹实现代码存放 | |-- enemy.py 敌方飞机实现代码存放 | |-- plane.py 我方飞机实现代码存放 |-- manage.py 程序启动文件 |-- README.md """ |
再晒下项目成果图
实现步骤
一、首先在 config/settings.py 中进行以下功能的实现
- 游戏初始化
- 游戏混音器初始化
- 背景音乐初始化
- 我方飞机挂了音乐
- 敌方飞机挂了音乐
- 子弹发射音乐
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | #! /usr/bin/env python # -*- coding: utf-8 -*- import pygame pygame.init() # 游戏初始化 pygame.mixer.init() # 混音器初始化 # 游戏背景音乐 pygame.mixer.music.load( "material/sound/game_music.wav" ) pygame.mixer.music.set_volume( 0.2 ) # 子弹发射音乐 bullet_sound = pygame.mixer.Sound( "material/sound/bullet.wav" ) bullet_sound.set_volume( 0.2 ) # 我方飞机挂了的音乐 me_down_sound = pygame.mixer.Sound( "material/sound/game_over.wav" ) me_down_sound.set_volume( 0.2 ) # 敌方飞机挂了的音乐 enemy1_down_sound = pygame.mixer.Sound( "material/sound/enemy1_down.wav" ) enemy1_down_sound.set_volume( 0.2 ) config / settings.py |
注:游戏素材滑动到文章底部点击链接即可下载
二、小试牛刀
飞机和子弹都是可移动的,那么怎么实现让它们动起来呢(我方飞机可以玩家进行操控,敌机就是随机性的出现,子弹暂由我方飞机发射)。
在 Pygame 中,所有移动对象都可看做是一个精灵(sprite),精灵之间能够进行相互的交互通信,例如如何让碰撞检测更加精准等等。
那么先让我们先在屏幕上制作一个游戏板,根据 settings.py 配置,并让它有声音播放,首先我们在 bin/main.py 中这么写:
我们可以直接运行它,那么我们会看到以下画面,并且还会有激情的声音吆!!!但是我们要将文件配置为绝对路径才可以运行,因为刚刚在 settings 中的加载的音乐文件为相对路径。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #! /usr/bin/env python # -*- coding: utf-8 -*- import sys from config.settings import * bg_size = 480 , 852 # 初始化游戏背景大小(宽, 高) screen = pygame.display.set_mode(bg_size) # 设置背景对话框 pygame.display.set_caption( "飞机大战" ) # 设置标题 background = pygame.image.load(os.path.join(BASE_DIR, "material/image/background.png" )) # 加载背景图片,并设置为不透明 def main(): pygame.mixer.music.play(loops = - 1 ) # loops 对应的值为 -1 则音乐会无限循环播放 while True : # 绘制背景图 screen.blit(background, ( 0 , 0 )) # 响应用户的操作(一定要有响应的用户操作) for event in pygame.event.get(): if event. type = = 12 : # 如果用户按下屏幕上的关闭按钮,触发QUIT事件,程序退出 pygame.quit() sys.exit() # 再而我们将背景图像并输出到屏幕上面 pygame.display.flip() if __name__ = = '__main__' : main() |
接下来呢,我们将要制作我方飞机,敌方飞机和子弹如何让它们展示在游戏画板上,继而让它们变得可移动起来,请看代码实现方案...
从游戏画板上添加飞机,首先我们应怎样在屏幕上输出飞机???
上述讲过,pygame 中的 sprite(精灵)可使一张图片或者一个静态物体动起来,那么制作飞机需要考虑并做些什么呢?
- 飞机的初始位置
- 通过按键 上下左右 来调控飞机的位置移动
- 飞机只能呆在制作的游戏画板中
- 飞机的速度
- 飞机死亡的加载
- 设定一个状态标识飞机的存活
- 让飞机具有动态的喷气式效果
那么如何实现以上的功能呢?接下来结合上述的示例代码我们先将我方飞机绘制到画板上方,并且我们通过按键 J 判定我方飞机的存活状态为死亡,绘制飞机的死亡画面并重置飞机
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | """ 创建飞机 在pygame中, 所有可移动的对象均叫可看作一个精灵(sprite) 该类并实现了碰撞方法 spritecollide 我方飞机和敌方飞机指定掩膜属性以及生存状态标志位 添加 self.mask 属性(可以实现更精准的碰撞效果) """ # 倒入精灵模块, 使飞机可以动起来 import pygame class OurPlane(pygame.sprite.Sprite): def __init__( self , bg_size): super (OurPlane, self ).__init__() # 确定我方飞机背景图(有俩张,可以让它们不停的切换,形成动态效果) self .image_one = pygame.image.load( "material/image/hero1.png" ) self .image_two = pygame.image.load( "material/image/hero2.png" ) # 获取我方飞机的位置 self .rect = self .image_one.get_rect() # 本地化背景图片的尺寸 self .width, self .height = bg_size[ 0 ], bg_size[ 1 ] # 获取飞机图像的掩膜用以更加精确的碰撞检测 self .mask = pygame.mask.from_surface( self .image_one) # 定义飞机初始化位置,底部预留60像素 self .rect.left, self .rect.top = ( self .width - self .rect.width) / / 2 , ( self .height - self .rect.height - 60 ) # 设置飞机移动速度 self .speed = 10 # 设置飞机存活状态(True为存活, False为死亡) self .active = True # 加载飞机损毁图片 self .destroy_images = [] self .destroy_images.extend( [ pygame.image.load( "material/image/hero_blowup_n1.png" ), pygame.image.load( "material/image/hero_blowup_n2.png" ), pygame.image.load( "material/image/hero_blowup_n3.png" ), pygame.image.load( "material/image/hero_blowup_n4.png" ) ] ) def move_up( self ): """ 飞机向上移动的操作函数,其余移动函数方法类似 """ if self .rect.top > 0 : # 如果飞机尚未移动出背景区域 self .rect.top - = self .speed else : # 若即将移动出背景区域,则及时纠正为背景边缘位置 self .rect.top = 0 def move_down( self ): """ 飞机向下移动 """ if self .rect.bottom < self .height - 60 : self .rect.top + = self .speed else : self .rect.bottom = self .height - 60 def move_left( self ): """ 飞机向左移动 """ if self .rect.left > 0 : self .rect.left - = self .speed else : self .rect.left = 0 def move_right( self ): """ 飞机向右移动 """ if self .rect.right < self .width: self .rect.right + = self .speed else : self .rect.right = self .width def reset( self ): # 初始化飞机(飞机挂了, 初始化到初始位置) self .rect.left, self .rect.top = ( self .width - self .rect.width) / / 2 , ( self .height - self .rect.height - 60 ) # 重置飞机的存活状态 self .active = True |
上面的代码写了一个 我们的飞机 (OurPlane) 类,它初始化了一些属性以及 上下左右 移动的方法和重置方法,接下来将要运用它展示到游戏画板上面
由于飞机是一直存在的,接下我们主程序 main 下面的死循环中这样写
def main(): pygame.mixer.music.play(loops=-1) # loops 对应的值为 -1 则音乐会无限循环播放 our_plane = OurPlane(bg_size) # 初始化 switch_image = False # 定义飞机的切图效果标识<span style="color: rgba(0, 0, 255, 1)">while</span><span style="color: rgba(0, 0, 0, 1)"> True: </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 绘制背景图</span>
screen.blit(background, (0, 0))
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 飞机状态是存活</span> <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> our_plane.active: </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> switch_image: screen.blit(our_plane.image_one, our_plane.rect) </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">: screen.blit(our_plane.image_two, our_plane.rect) switch_image </span>= <span style="color: rgba(0, 0, 255, 1)">not</span> switch_image <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 让切图状态不停的变换</span> <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">: </span><span style="color: rgba(0, 0, 255, 1)">pass</span> <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 响应用户的操作(一定要有响应的用户操作)</span> <span style="color: rgba(0, 0, 255, 1)">for</span> event <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> pygame.event.get(): </span><span style="color: rgba(0, 0, 255, 1)">if</span> event.type == 12: <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 如果用户按下屏幕上的关闭按钮,触发QUIT事件,程序退出</span>
pygame.quit()
sys.exit()</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 再而我们将背景图像并输出到屏幕上面</span>
pygame.display.flip()
if name == 'main':
main()
def main(): pygame.mixer.music.play(loops=-1) # loops 对应的值为 -1 则音乐会无限循环播放 our_plane = OurPlane(bg_size) # 初始化 switch_image = False # 定义飞机的切图效果标识<span style="color: rgba(0, 0, 255, 1)">while</span><span style="color: rgba(0, 0, 0, 1)"> True: </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 绘制背景图</span>
screen.blit(background, (0, 0))
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 飞机状态是存活</span> <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> our_plane.active: </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> switch_image: screen.blit(our_plane.image_one, our_plane.rect) </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">: screen.blit(our_plane.image_two, our_plane.rect) switch_image </span>= <span style="color: rgba(0, 0, 255, 1)">not</span> switch_image <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 让切图状态不停的变换</span> <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">: </span><span style="color: rgba(0, 0, 255, 1)">pass</span> <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 获得用户所有的键盘输入序列(如果用户通过键盘发出“向上”的指令,其他类似)</span> key_pressed =<span style="color: rgba(0, 0, 0, 1)"> pygame.key.get_pressed() </span><span style="color: rgba(0, 0, 255, 1)">if</span> key_pressed[K_w] <span style="color: rgba(0, 0, 255, 1)">or</span><span style="color: rgba(0, 0, 0, 1)"> key_pressed[K_UP]: our_plane.move_up() </span><span style="color: rgba(0, 0, 255, 1)">if</span> key_pressed[K_s] <span style="color: rgba(0, 0, 255, 1)">or</span><span style="color: rgba(0, 0, 0, 1)"> key_pressed[K_DOWN]: our_plane.move_down() </span><span style="color: rgba(0, 0, 255, 1)">if</span> key_pressed[K_a] <span style="color: rgba(0, 0, 255, 1)">or</span><span style="color: rgba(0, 0, 0, 1)"> key_pressed[K_LEFT]: our_plane.move_left() </span><span style="color: rgba(0, 0, 255, 1)">if</span> key_pressed[K_d] <span style="color: rgba(0, 0, 255, 1)">or</span><span style="color: rgba(0, 0, 0, 1)"> key_pressed[K_RIGHT]: our_plane.move_right() </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 响应用户的操作(一定要有响应的用户操作)</span> <span style="color: rgba(0, 0, 255, 1)">for</span> event <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> pygame.event.get(): </span><span style="color: rgba(0, 0, 255, 1)">if</span> event.type == 12: <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 如果用户按下屏幕上的关闭按钮,触发QUIT事件,程序退出</span>
pygame.quit()
sys.exit()</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 再而我们将背景图像并输出到屏幕上面</span>
pygame.display.flip()
if name == 'main':
main()
def main(): pygame.mixer.music.play(loops=-1) # loops 对应的值为 -1 则音乐会无限循环播放 our_plane = OurPlane(bg_size) # 初始化 switch_image = False # 定义飞机的切图效果标识 our_plane_destroy_index = 0</span><span style="color: rgba(0, 0, 255, 1)">while</span><span style="color: rgba(0, 0, 0, 1)"> True: </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 绘制背景图</span>
screen.blit(background, (0, 0))
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 飞机状态是存活(如果按键 为 J, 让飞机死亡并绘制爆炸效果)</span> <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> our_plane.active: </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> switch_image: screen.blit(our_plane.image_one, our_plane.rect) </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">: screen.blit(our_plane.image_two, our_plane.rect) switch_image </span>= <span style="color: rgba(0, 0, 255, 1)">not</span> switch_image <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 让切图状态不停的变换</span> <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">: </span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)"> 飞机死亡也是进行按顺序的图片切换, 那么在死循环之外定义索引 </span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(0, 0, 0, 1)"> me_destroy_index </span>= (our_plane_destroy_index + 1) % 4 <span style="color: rgba(0, 0, 255, 1)">if</span> me_destroy_index == 1<span style="color: rgba(0, 0, 0, 1)">: me_down_sound.play() </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 爆炸声音效果</span> our_plane.reset() <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 初始化飞机</span> <span style="color: rgba(0, 0, 255, 1)">if</span> our_plane_destroy_index >=<span style="color: rgba(0, 0, 0, 1)"> len(our_plane.destroy_images): our_plane_destroy_index </span>=<span style="color: rgba(0, 0, 0, 1)"> 0 </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">: screen.blit(our_plane.destroy_images[our_plane_destroy_index], our_plane.rect) our_plane_destroy_index </span>+= 1 <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 获得用户所有的键盘输入序列(如果用户通过键盘发出“向上”的指令,其他类似)</span> key_pressed =<span style="color: rgba(0, 0, 0, 1)"> pygame.key.get_pressed() </span><span style="color: rgba(0, 0, 255, 1)">if</span> key_pressed[K_w] <span style="color: rgba(0, 0, 255, 1)">or</span><span style="color: rgba(0, 0, 0, 1)"> key_pressed[K_UP]: our_plane.move_up() </span><span style="color: rgba(0, 0, 255, 1)">if</span> key_pressed[K_s] <span style="color: rgba(0, 0, 255, 1)">or</span><span style="color: rgba(0, 0, 0, 1)"> key_pressed[K_DOWN]: our_plane.move_down() </span><span style="color: rgba(0, 0, 255, 1)">if</span> key_pressed[K_a] <span style="color: rgba(0, 0, 255, 1)">or</span><span style="color: rgba(0, 0, 0, 1)"> key_pressed[K_LEFT]: our_plane.move_left() </span><span style="color: rgba(0, 0, 255, 1)">if</span> key_pressed[K_d] <span style="color: rgba(0, 0, 255, 1)">or</span><span style="color: rgba(0, 0, 0, 1)"> key_pressed[K_RIGHT]: our_plane.move_right() </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 按键为 j 飞机更改存活标识</span> <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> key_pressed[K_j]: our_plane.active </span>=<span style="color: rgba(0, 0, 0, 1)"> False </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 响应用户的操作(一定要有响应的用户操作)</span> <span style="color: rgba(0, 0, 255, 1)">for</span> event <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> pygame.event.get(): </span><span style="color: rgba(0, 0, 255, 1)">if</span> event.type == 12: <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 如果用户按下屏幕上的关闭按钮,触发QUIT事件,程序退出</span>
pygame.quit()
sys.exit()</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 再而我们将背景图像并输出到屏幕上面</span> pygame.display.flip()</pre>
那么上述的功能都已经实现了,接下来就开始真正的 "打飞机"
三、接下来可以制作我方飞机,敌方战机,子弹等,这些功能均在 src/ 目录下实现
- 我方飞机根据按键上下左右进行移动,初始化位置,喷气式图片加载切换及重置效果等
#! /usr/bin/env python # -*- coding: utf-8 -*-"""
创建飞机
在 pygame 中, 所有可移动的对象均叫可看作一个精灵 (sprite)
该类并实现了碰撞方法 spritecollide我方飞机和敌方飞机指定掩膜属性以及生存状态标志位 添加 self.mask 属性(可以实现更精准的碰撞效果)
"""
# 倒入精灵模块, 使飞机可以动起来
import pygameclass OurPlane(pygame.sprite.Sprite):
</span><span style="color: rgba(0, 0, 255, 1)">def</span> <span style="color: rgba(128, 0, 128, 1)">__init__</span><span style="color: rgba(0, 0, 0, 1)">(self, bg_size): super(OurPlane, self).</span><span style="color: rgba(128, 0, 128, 1)">__init__</span><span style="color: rgba(0, 0, 0, 1)">() </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 确定我方飞机背景图</span> self.image_one = pygame.image.load(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">material/image/hero1.png</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">) self.image_two </span>= pygame.image.load(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">material/image/hero2.png</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">) </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 获取我方飞机的位置</span> self.rect =<span style="color: rgba(0, 0, 0, 1)"> self.image_one.get_rect() </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 本地化背景图片的尺寸</span> self.width, self.height = bg_size[0], bg_size[1<span style="color: rgba(0, 0, 0, 1)">] </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 获取飞机图像的掩膜用以更加精确的碰撞检测</span> self.mask =<span style="color: rgba(0, 0, 0, 1)"> pygame.mask.from_surface(self.image_one) </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 定义飞机初始化位置,底部预留60像素</span> self.rect.left, self.rect.top = (self.width - self.rect.width) // 2, (self.height - self.rect.height - 60<span style="color: rgba(0, 0, 0, 1)">) </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 设置飞机移动速度</span> self.speed = 10 <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 设置飞机存活状态(True为存活, False为死亡)</span> self.active =<span style="color: rgba(0, 0, 0, 1)"> True </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 加载飞机损毁图片</span> self.destroy_images =<span style="color: rgba(0, 0, 0, 1)"> [] self.destroy_images.extend( [ pygame.image.load(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">material/image/hero_blowup_n1.png</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">), pygame.image.load(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">material/image/hero_blowup_n2.png</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">), pygame.image.load(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">material/image/hero_blowup_n3.png</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">), pygame.image.load(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">material/image/hero_blowup_n4.png</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">) ] ) </span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> move_up(self): </span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)"> 飞机向上移动的操作函数,其余移动函数方法类似 </span><span style="color: rgba(128, 0, 0, 1)">"""</span> <span style="color: rgba(0, 0, 255, 1)">if</span> self.rect.top > 0: <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 如果飞机尚未移动出背景区域</span> self.rect.top -=<span style="color: rgba(0, 0, 0, 1)"> self.speed </span><span style="color: rgba(0, 0, 255, 1)">else</span>: <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 若即将移动出背景区域,则及时纠正为背景边缘位置</span> self.rect.top =<span style="color: rgba(0, 0, 0, 1)"> 0 </span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> move_down(self): </span><span style="color: rgba(0, 0, 255, 1)">if</span> self.rect.bottom < self.height - 60<span style="color: rgba(0, 0, 0, 1)">: self.rect.top </span>+=<span style="color: rgba(0, 0, 0, 1)"> self.speed </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">: self.rect.bottom </span>= self.height - 60 <span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> move_left(self): </span><span style="color: rgba(0, 0, 255, 1)">if</span> self.rect.left ><span style="color: rgba(0, 0, 0, 1)"> 0: self.rect.left </span>-=<span style="color: rgba(0, 0, 0, 1)"> self.speed </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">: self.rect.left </span>=<span style="color: rgba(0, 0, 0, 1)"> 0 </span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> move_right(self): </span><span style="color: rgba(0, 0, 255, 1)">if</span> self.rect.right <<span style="color: rgba(0, 0, 0, 1)"> self.width: self.rect.right </span>+=<span style="color: rgba(0, 0, 0, 1)"> self.speed </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">: self.rect.right </span>=<span style="color: rgba(0, 0, 0, 1)"> self.width </span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> reset(self): </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 初始化飞机(飞机挂了, 初始化到初始位置)</span> self.rect.left, self.rect.top = (self.width - self.rect.width) // 2, (self.height - self.rect.height - 60<span style="color: rgba(0, 0, 0, 1)">) self.active </span>=<span style="color: rgba(0, 0, 0, 1)"> True
src/plane.py
- 敌方飞机随机移动出现及重置 (制作出我方飞机之后,敌机和子弹其实都是大同小异的)
-
#! /usr/bin/env python # -*- coding: utf-8 -*-
"""
定义敌机
"""from random import randint
import pygame
class SmallEnemy(pygame.sprite.Sprite):
"""
定义小飞机敌人
"""
def init(self, bg_size):
super(SmallEnemy, self).init()
self.image = pygame.image.load("material/image/enemy1.png")
self.rect = self.image.get_rect()
self.width, self.height = bg_size[0], bg_size[1]
self.mask = pygame.mask.from_surface(self.image) # 获取飞机图像的掩膜用以更加精确的碰撞检测
self.speed = 2
# 定义敌机出现的位置, 保证敌机不会在程序已开始就立即出现
self.rect.left, self.rect.top = (
randint(0, self.width - self.rect.width), randint(-5 * self.rect.height, -5),
)
self.active = True
# 加载飞机损毁图片
self.destroy_images = []
self.destroy_images.extend(
[
pygame.image.load("material/image/enemy1_down1.png"),
pygame.image.load("material/image/enemy1_down2.png"),
pygame.image.load("material/image/enemy1_down3.png"),
pygame.image.load("material/image/enemy1_down4.png")
]
)</span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> move(self): </span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)"> 定义敌机的移动函数 :return: </span><span style="color: rgba(128, 0, 0, 1)">"""</span> <span style="color: rgba(0, 0, 255, 1)">if</span> self.rect.top <<span style="color: rgba(0, 0, 0, 1)"> self.height: self.rect.top </span>+=<span style="color: rgba(0, 0, 0, 1)"> self.speed </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">: self.reset() </span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> reset(self): </span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)"> 当敌机向下移动出屏幕时, 以及敌机死亡 :return: </span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(0, 0, 0, 1)"> self.rect.left, self.rect.top </span>= (randint(0, self.width - self.rect.width), randint(-5 *<span style="color: rgba(0, 0, 0, 1)"> self.rect.height, 0)) self.active </span>= True</pre>
- 子弹按照我方飞机正中上方发射及频率调控,重置
#! /usr/bin/env python # -*- coding: utf-8 -*-"""
子弹的实现
"""import pygame
class Bullet(pygame.sprite.Sprite):
</span><span style="color: rgba(0, 0, 255, 1)">def</span> <span style="color: rgba(128, 0, 128, 1)">__init__</span><span style="color: rgba(0, 0, 0, 1)">(self, position): super(Bullet, self).</span><span style="color: rgba(128, 0, 128, 1)">__init__</span><span style="color: rgba(0, 0, 0, 1)">() self.image </span>= pygame.image.load(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">material/image/bullet1.png</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">) self.rect </span>=<span style="color: rgba(0, 0, 0, 1)"> self.image.get_rect() self.rect.left, self.rect.top </span>=<span style="color: rgba(0, 0, 0, 1)"> position self.speed </span>= 30<span style="color: rgba(0, 0, 0, 1)"> self.active </span>=<span style="color: rgba(0, 0, 0, 1)"> True self.mask </span>=<span style="color: rgba(0, 0, 0, 1)"> pygame.mask.from_surface(self.image) </span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> move(self): </span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)"> 子弹移动, 超出屏幕范围, 则设置死亡 :return: </span><span style="color: rgba(128, 0, 0, 1)">"""</span> <span style="color: rgba(0, 0, 255, 1)">if</span> self.rect.top <<span style="color: rgba(0, 0, 0, 1)"> 0: self.active </span>=<span style="color: rgba(0, 0, 0, 1)"> False </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">: self.rect.top </span>-=<span style="color: rgba(0, 0, 0, 1)"> self.speed </span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> reset(self, position): </span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)"> 复位函数 :param position: :return: </span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(0, 0, 0, 1)"> self.rect.left, self.rect.top </span>=<span style="color: rgba(0, 0, 0, 1)"> position self.active </span>=<span style="color: rgba(0, 0, 0, 1)"> True
src/bullet.py
在上面的内容中,使用面向对象的形式制作了游戏中可移动的对象并继承 pygame.sprite.Sprite
四、然后在 bin/main.py 中进行主体功能的实现
- 初始化背景图及大小
- 我方飞机移动及发射子弹
- 敌方飞机移动
- 我方飞机和敌方飞机碰撞检测
- 键盘按键监测效果
- 我方飞机和敌方飞机挂了效果绘制
import sysfrom pygame.locals import *
from config.settings import *
from src.plane import OurPlane
from src.enemy import SmallEnemy
from src.bullet import Bulletbg_size = 480, 852 # 初始化游戏背景大小 (宽, 高)
screen = pygame.display.set_mode(bg_size) # 设置背景对话框
pygame.display.set_caption("飞机大战") # 设置标题
background = pygame.image.load("material/image/background.png") # 加载背景图片, 并设置为不透明# 获取我方飞机
our_plane = OurPlane(bg_size)def add_small_enemies(group1, group2, num):
"""
添加小型敌机
指定个敌机对象添加到精灵组(sprite.group)
参数 group1、group2 是两个精灵组类型的形参,用以存储多个精灵对象(敌机)。
需要注意的一点是 group 既然是特定的精灵组结构体,在向其内部添加精灵对象时需要调用其对应的成员函数 add()
:return:
"""
for i in range(num):
small_enemy = SmallEnemy(bg_size)
group1.add(small_enemy)
group2.add(small_enemy)def main():
# 响应音乐
pygame.mixer.music.play(-1) # loops 接收该参数, -1 表示无限循环 (默认循环播放一次)
running = True
switch_image = False # 切换飞机的标识位 (使飞机具有喷气式效果)
delay = 60 # 对一些效果进行延迟, 效果更好一些
enemies = pygame.sprite.Group() # 生成敌方飞机组 (一种精灵组用以存储所有敌机精灵)
small_enemies = pygame.sprite.Group() # 敌方小型飞机组 (不同型号敌机创建不同的精灵组来存储)
add_small_enemies(small_enemies, enemies, 4) # 生成若干敌方小型飞机<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 定义子弹, 各种敌机和我方敌机的毁坏图像索引</span> bullet_index =<span style="color: rgba(0, 0, 0, 1)"> 0 e1_destroy_index </span>=<span style="color: rgba(0, 0, 0, 1)"> 0 me_destroy_index </span>=<span style="color: rgba(0, 0, 0, 1)"> 0 </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 定义子弹实例化个数</span> bullet1 =<span style="color: rgba(0, 0, 0, 1)"> [] bullet_num </span>= 6 <span style="color: rgba(0, 0, 255, 1)">for</span> i <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> range(bullet_num): bullet1.append(Bullet(our_plane.rect.midtop)) </span><span style="color: rgba(0, 0, 255, 1)">while</span><span style="color: rgba(0, 0, 0, 1)"> running: </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 绘制背景图</span>
screen.blit(background, (0, 0))
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 微信的飞机貌似是喷气式的, 那么这个就涉及到一个帧数的问题</span> clock =<span style="color: rgba(0, 0, 0, 1)"> pygame.time.Clock() clock.tick(</span>60<span style="color: rgba(0, 0, 0, 1)">) </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 绘制我方飞机的两种不同的形式</span> <span style="color: rgba(0, 0, 255, 1)">if</span> <span style="color: rgba(0, 0, 255, 1)">not</span> delay % 3<span style="color: rgba(0, 0, 0, 1)">: switch_image </span>= <span style="color: rgba(0, 0, 255, 1)">not</span><span style="color: rgba(0, 0, 0, 1)"> switch_image </span><span style="color: rgba(0, 0, 255, 1)">for</span> each <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> small_enemies: </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> each.active: </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 随机循环输出小飞机敌机</span> <span style="color: rgba(0, 0, 255, 1)">for</span> e <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> small_enemies: e.move() screen.blit(e.image, e.rect) </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">: </span><span style="color: rgba(0, 0, 255, 1)">if</span> e1_destroy_index ==<span style="color: rgba(0, 0, 0, 1)"> 0: enemy1_down_sound.play() screen.blit(each.destroy_images[e1_destroy_index], each.rect) e1_destroy_index </span>= (e1_destroy_index + 1) % 4 <span style="color: rgba(0, 0, 255, 1)">if</span> e1_destroy_index ==<span style="color: rgba(0, 0, 0, 1)"> 0: each.reset() </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 当我方飞机存活状态, 正常展示</span> <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> our_plane.active: </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> switch_image: screen.blit(our_plane.image_one, our_plane.rect) </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">: screen.blit(our_plane.image_two, our_plane.rect) </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 飞机存活的状态下才可以发射子弹</span> <span style="color: rgba(0, 0, 255, 1)">if</span> <span style="color: rgba(0, 0, 255, 1)">not</span> (delay % 10): <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 每十帧发射一颗移动的子弹</span>
bullet_sound.play()
bullets = bullet1
bullets[bullet_index].reset(our_plane.rect.midtop)
bullet_index = (bullet_index + 1) % bullet_num</span><span style="color: rgba(0, 0, 255, 1)">for</span> b <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> bullets: </span><span style="color: rgba(0, 0, 255, 1)">if</span> b.active: <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 只有激活的子弹才可能击中敌机</span>
b.move()
screen.blit(b.image, b.rect)
enemies_hit = pygame.sprite.spritecollide(b, enemies, False, pygame.sprite.collide_mask)
if enemies_hit: # 如果子弹击中飞机
b.active = False # 子弹损毁
for e in enemies_hit:
e.active = False # 小型敌机损毁<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 毁坏状态绘制爆炸的场面</span> <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">: </span><span style="color: rgba(0, 0, 255, 1)">if</span> <span style="color: rgba(0, 0, 255, 1)">not</span> (delay % 3<span style="color: rgba(0, 0, 0, 1)">): screen.blit(our_plane.destroy_images[me_destroy_index], our_plane.rect) me_destroy_index </span>= (me_destroy_index + 1) % 4 <span style="color: rgba(0, 0, 255, 1)">if</span> me_destroy_index ==<span style="color: rgba(0, 0, 0, 1)"> 0: me_down_sound.play() our_plane.reset() </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 调用 pygame 实现的碰撞方法 spritecollide (我方飞机如果和敌机碰撞, 更改飞机的存活属性)</span> enemies_down =<span style="color: rgba(0, 0, 0, 1)"> pygame.sprite.spritecollide(our_plane, enemies, False, pygame.sprite.collide_mask) </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> enemies_down: our_plane.active </span>=<span style="color: rgba(0, 0, 0, 1)"> False </span><span style="color: rgba(0, 0, 255, 1)">for</span> row <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> enemies: row.active </span>=<span style="color: rgba(0, 0, 0, 1)"> False </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 响应用户的操作</span> <span style="color: rgba(0, 0, 255, 1)">for</span> event <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> pygame.event.get(): </span><span style="color: rgba(0, 0, 255, 1)">if</span> event.type == 12: <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 如果用户按下屏幕上的关闭按钮,触发QUIT事件,程序退出</span>
pygame.quit()
sys.exit()</span><span style="color: rgba(0, 0, 255, 1)">if</span> delay ==<span style="color: rgba(0, 0, 0, 1)"> 0: delay </span>= 60<span style="color: rgba(0, 0, 0, 1)"> delay </span>-= 1 <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 获得用户所有的键盘输入序列(如果用户通过键盘发出“向上”的指令,其他类似)</span> key_pressed =<span style="color: rgba(0, 0, 0, 1)"> pygame.key.get_pressed() </span><span style="color: rgba(0, 0, 255, 1)">if</span> key_pressed[K_w] <span style="color: rgba(0, 0, 255, 1)">or</span><span style="color: rgba(0, 0, 0, 1)"> key_pressed[K_UP]: our_plane.move_up() </span><span style="color: rgba(0, 0, 255, 1)">if</span> key_pressed[K_s] <span style="color: rgba(0, 0, 255, 1)">or</span><span style="color: rgba(0, 0, 0, 1)"> key_pressed[K_DOWN]: our_plane.move_down() </span><span style="color: rgba(0, 0, 255, 1)">if</span> key_pressed[K_a] <span style="color: rgba(0, 0, 255, 1)">or</span><span style="color: rgba(0, 0, 0, 1)"> key_pressed[K_LEFT]: our_plane.move_left() </span><span style="color: rgba(0, 0, 255, 1)">if</span> key_pressed[K_d] <span style="color: rgba(0, 0, 255, 1)">or</span><span style="color: rgba(0, 0, 0, 1)"> key_pressed[K_RIGHT]: our_plane.move_right() </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 绘制图像并输出到屏幕上面</span> pygame.display.flip()</pre>
五、畅汗淋漓,一气呵成打飞机
1 2 3 4 5 6 7 8 9 | from bin .main import main if __name__ = = '__main__' : """ 环境: python3 + pygame running 起来就可以打飞机了O(∩_∩)O~. """ main() |
最终效果!
完整项目代码 : https://github.com/triaquae/jerkoff
另外,本项目所用到的基础知识视频 已上传至 路飞学城 ,需要者自取! http://luffy.oldboyedu.com/course/detail/python/5