声明
此项目是对python基础语法和高级语法的结合,学完python基础和高级可以拿此项目练手,说的比较细,涵盖了pygame一些知识点的讲解。
项目展示
安装准备
这里我们要用到pygame模块,pygame模块是python中针对电子游戏开发的模块,功能十分完善,安装pygame模块的方法(针对windows用户):直接在终端输入 pip install pygame,如果嫌慢可以用国内镜像源,这里我拿清华大学举例子,具体方法是:
pip install pygame -i https://pypi.tuna.tsinghua.edu.cn/simple/
安装成功后会显示successfully,也可以输入pip list 进行查看
素材准备
在飞机大战开始前我们会需要一些飞机图片,背景图片等等作为素材导入到游戏中,来让游戏更加生动精彩,素材图片你们可以自己去收集,这里我提供一些素材图片,地址如下:
https://pan.baidu.com/s/1pMM0beb
开始准备
项目准备
1.新建一个飞机大战的文件夹;
2.在里面新建一个自定义名字的python文件;
3.将刚才的游戏素材图片导入。
游戏第一印象
1.把一些静止的图象绘制到游戏窗口中
2.根据用户的交互或其他情况,移动这些图象,产生动画效果
3.根据图象是否发生重叠,判断敌机是否被摧毁,以及自己的战机是否被摧毁等情况
游戏原理
绘制窗口
初始化和退出
这里我们需要使用到pygame提供的init方法进行初始化,quit来退出
pygame.init() 导入并初始化所有pygame模块,以便后面我们需要用到
pygame.quit() 卸载所有之前pygame加载的模块,在游戏结束之前调用,及时释放内存空间
两个方法之前就是我们需要游戏代码的地方。
游戏中的坐标系
这里我自己画一个图来让大家理解
在游戏里,所有可见的元素都是以矩形区域来描述位置的,要描述一个矩形区域需要四个变量:
(x,y)(width,height) ;(x,y)是定义矩形的左上角的坐标,(width,height)是矩形窗口的宽和高,既准确大小
pygame专门提供一个类pygame.Rect用来描述矩形区域(注意Rect大小写),pygame.Rect里面包含了x,y,width,height,size等属性,定义一个plane_rect来描述自己飞机的位置和大小,举例如下:
import pygame
# 不执行pygame.init()同样可以使用Rect方法
plane_rect = pygame.Rect(100, 50, 125, 150)
print(plane_rect.x)
print(plane_rect.y)
print(plane_rect.width)
print(plane_rect.height)
输出如下:
100
50
125
150
创建游戏主窗口
pygame专门提供了一个模块pygame.display用于创建,管理游戏窗口
import pygame
pygame.init()
screen = pygame.display.set_mode((480, 700))
# 防止游戏窗口自动关闭,窗口一直显示,除非终止运行
while True:
pass
pygame.quit()
1.创建游戏窗口里set_mode方法,取决于背景图片的大小,里面第一个参数是(width,height),是一个元组一定要用() 括起来,这里我设置宽480,高700。
2.必须要用一个变量来接受set_mode返回的结果,以为后续所有图象绘制都基于这个放回的结果
3.我为了方便展示让窗口一直不关闭就用了一个while True,后续还会改进
绘制图像
图像文件初始是保存在磁盘上的,如需使用,我们就要把图像加载到内存,然后将图像绘制到指定位置既绘制到游戏窗口
绘制图像的三个步骤:
1.使用pygame.image.load()加载图像的数据
2.使用游戏屏幕对象,调用blit方法将图像绘制到指定屏幕
3.调用pygame.display.update()方法更新整个屏幕的显示,可以在所有图像绘制完之后调用update
import pygame
pygame.init()
screen = pygame.display.set_mode((480, 700))
# 绘制背景图像
# 加载图像数据,括号里面是图片地址
background = pygame.image.load('./飞机大战/images/background.png')
# blit绘制图像,(0,0)是将背景图的左上角绘制到游戏窗口的左上角
screen.blit(background, (0, 0))
# update 更新屏幕显示
pygame.display.update()
# 防止游戏窗口自动关闭,窗口一直显示,除非终止运行
while True:
# 针对未响应
pygame.event.get()
pygame.quit()
注意:如果你打开游戏窗口后提示未响应,你就在while循环下加上pygame.event.get()就可以解决
绘制自己的战机
和绘制图像一样用绘制图像的三个步骤将自己的战机绘制到游戏窗口里
# 绘制英雄的飞机
hero = pygame.image.load("./飞机大战/images/me1.png")
screen.blit(hero, (200, 500))
pygame.display.update()
以上代码要放在绘制背景图的下面,while循环的上面
游戏动画效果
动画实现原理
游戏动画原理和电源的原理类似,就是将多张静止的电源胶片或图片连续,快速的播放,产生连贯的视觉效果,一般每秒绘制60次,就能够达到非常好的效果,每次绘制的结果就叫帧,而每次用pygame.display.update()更新一次屏幕就叫一帧,这里我们就要用到循环来达到这种效果
游戏循环
上面的游戏窗口已经把游戏窗口和背景都已经设置好,而游戏循环意味着游戏正式开始,即解决刷新帧率,这里我们可以设置每秒刷新60次,即1/60秒移动所有图像的位置;也可以检测用户交互如用户操控按键和鼠标来移动战机的位置。
设置时钟对象
时钟顾名思义就是在规定的时间内执行多少次,可以理解为每秒执行多少次,即刷新帧率,pygame专门提供一个类pygame.time.Clock()可以非常方便的设置屏幕绘制速度,即刷新帧率。在游戏初始化里设置一个时钟对象,在循环里面调用
# 设置时钟对象
clock = pygame.time.Clock()
# 防止游戏窗口自动关闭,窗口一直显示,除非终止运行
while True:
# 设置游戏频率,可以指定循环内部的代码执行的频率,60表示每秒执行60次
clock.tick(60)
# 针对未响应
pygame.event.get()
英雄战机的移动
英雄战机的移动大致可以理解为在每次循环内坐标发生移动,并且让每次循环的时间设置在1/60秒,就可以看到飞机在窗口上连续的移动。我们先定义
飞机的位置,然后在每次循环内对飞机的坐标进行更改,然后每次循环结束前在对屏幕进行更新
# 设置时钟对象
clock = pygame.time.Clock()
# 定义rect记录飞机的初始位置
hero_rect = pygame.Rect(200, 500, 102, 126)
while True:
# 设置游戏频率,可以指定循环内部的代码执行的频率,60表示每秒执行60次
clock.tick(60)
# 修改飞机的位置
hero_rect.y -= 1
# 调用blit方法绘制图像,每次循环完用背景图覆盖一次,不然会造成每次循环的飞机重叠
screen.blit(background, (0, 0))
screen.blit(hero, hero_rect)
# 更新屏幕显示
pygame.display.update()
# 针对未响应
pygame.event.get()
需要注意的是,在每次循环内我们需要覆盖掉上一次战机,即用背景图进行覆盖,不然会造成每次循环的战机都留在屏幕上,所以我们在每次循环绘制战机之前,先对上一次循环的战机进行覆盖。下图就是不覆盖的结果:
战机移动的扩展
这里我们可以对战机移动进行一个扩展,就是当战机移动到顶部的时候,会从底部钻出来,这样会使战机更连贯,话不多说,我们来实现这个效果。
# 判断飞机的位置,让飞机从上面钻出,下面钻入
if hero_rect.y <= -126:
hero_rect.y = 700
其实这个效果的实现特别简单,就是飞机头部即y坐标处于-126即飞机的height的时候,令y等于窗口的底部即y=700即可
游戏循环中的监听事件
监听事件是什么?其实可以这样理解,监听就是游戏循环中判断用户的具体操作,只有捕获用户的具体操作,才能由针对的做出响应;事件就是游戏启动后用户针对游戏进行的操作,例如点击鼠标,按键盘,点击关闭按钮。
综上所述:就是判断和识别用户的所做的操作,来进行相应的响应。
pygame中通过pygame.event.get()可以获得用户当前所作动作的事件列表
识别退出游戏
退出游戏就是点击游戏窗口的×来退出游戏,这里的判断语句是针对pygame.event.get()方法,鼠标,键盘的每次移动都会实时显示在输出终端内,然后根据点击x按钮时输出的结果进行分析就可以得到以下判断语句,下面的判读退出系统几乎是确定的,所有pygame开发的游戏在退出游戏几乎都是下面的这段代码。
# 事件监听
for event in pygame.event.get():
if event.type == pygame.QUIT:
print("退出游戏....")
# quit卸载所有模块
pygame.quit()
# 退出
exit()
注意:有人可能会问为什么不用break退出,因为break只能退出当前循环即退出for循环,但是for循环上面还有一个while循环,所有要用到exit()来退出程序
精灵和精灵组
上面我们大致解决英雄战机的移动和绘制,但是针对敌机我们还不能解决,难道敌机是一个一个用上面的三个步骤进行绘制和移动吗,当然不是,那样太过于麻烦。这里我们引入一个十分重要的概念:精灵和精灵组。精灵是什么?其实精灵可以看成一个对象(这里看不懂可以去复习一下面向对象编程),是一个能够记录图像数据imag和位置rect的对象,它还有一个更新精灵位置的方法。说到这就简单了,这个精灵可以派生出很多子类,我们就可以把敌机看成这些子类,然后对每个子类重写不一样的属性和方法,然后直接调用就行,是不是简单了许多,而精灵组就是所有精灵的组合,对精灵组的所有操作都可以在精灵上实现。
敌机的实现
首先我们要知道pygame内部自带了精灵类pygame.sprites.Sprites和精灵组pygame.sprites.Group我们到时候直接用就可以了,精灵类即pygame.sprites.Sprites自带了两个个属性即图像数据image,位置属性rect,和两个方法一个是update()更新精灵位置和kill()从所有组中删除,而精灵组pygame.sprites.Group有add()方法向精灵组添加精灵,update()方法让所有精灵调用各自的update方法,以及draw(Surface)将所有精灵绘制到Surface的rect位置。
现在我们新建一个plane_sprites.py来创建敌机类
import pygame
class GameSprite(pygame.sprite.Sprite):
"""飞机大战游戏精灵"""
def __init__(self, image_name, speed=1):
# 调用父类的初始化方法,如果不调用就会对父类的init方法进行重写,就不能用父类的方法了
super().__init__()
# 定义对象的属性
# 加载图片
self.image = pygame.image.load