15 飞机大战:pygame入门、python基础串连
0 pygame模块的导入
- import pygame导入pygame包
- 使用pygame.init()导入pygame的所有模块。只有导入模块pygame才能使用。
- 使用pygame.quit()卸载pygame的所有模块。游戏结束后,释放内存。
1 pygame.Rect :用于描述矩形区域的类
创建矩形对象的命令:变量 = Rect(x,y,width,height)
Rect中的size属性:用于封装对象的宽和高。
Rect的对象储存该对象的坐标元组,因此可以直接使用Rect的对象当坐标作其它方法的形参。
2 游戏设计的思路
我们把程序分成两个部分:游戏的初始化部分和游戏循环
在代码中如下表现:
import pygame pygame.init() # 游戏的初始化部分 # 绘制游戏窗口 screen =pygame.display.set_mode((480,700)) # 绘制图像 bg = pygame.image.load("./images/background.png") hero = pygame.image.load("./images/me1.png") # 加载图像 screen.blit(bg,(0,0)) screen.blit(hero,(0,0))
# 设置游戏时钟 # 更新图像 pygame.display.update() # 游戏循环 -> 意味着游戏的开始 while True: pass pygame.quit()
3 绘制游戏的主窗口:pygame.display模块
set_mode()方法:创建游戏窗口
set_mode(resolution=(0,0),flag=0,depth=0) ->Surface
参数详解:
- resolution即分辨率,调用此方法时不用再写resolution=。直接写分辨率元组。
- flags参数指定屏幕的附加选项,如屏幕是否全屏等。
- depth参数表示颜色的位数,默认自动匹配。
- ->表示返回Surface,暂时理解为返回一个屏幕(或画板),需要使用一个变量接收这个方法。
举个例子:
screen = pygame.display.set_mode((550,550)) #创建一个550x550的黑色屏幕 #程序执行结束后,屏幕即关闭,所以,在代码最下方写一个while死循环让程序不停止可以暂时解决问题
4 绘制图像
窗口中你能看见的图像,都是绘制上去的,如游戏背景,游戏角色等等。
绘制图像需要三步:
1.使用pygame.image.load()加载图像的数据
2.使用游戏屏幕对象调用blit方法将图像绘制到指定位置
3.调用pygame.display.update()方法更新屏幕的显示
细节:
- load()方法,括号中写图像的路径:如:./images/1.png。load()方法需要一个变量来接收,通常命名为图片的名称。如:background = pygame.image.load("./images/1.png") (注:点表示当前文件所在的目录,文件路径要用字符串形式)
- blit()方法,括号中的参数为:图像名(即接收load方法的变量),位置(左上角为0,0,右x正,下y正)
- update()方法必须有,不然不会显示。
5 pygame.time模块及设置游戏时钟
time模块能提供一些关于时间的工具,其中Clock类用来作为游戏的时钟对象。
时钟对象包含很多方法,如后续要使用到的刷新频率(绘制频率)方法tick()。
创建一个时钟对象:
clock = pygame.time.Clock()
6 刷新频率方法:tick()
此方法为Clock类下的一个方法,用来设置绘制刷新的频率。
用法如:
clock.tick(60) #60表示60帧/s
7 简单的图像移动实现及残影问题解决
使图像移动的思路
- 新建一个矩形框对象(使用pygame.Rect),使它的初始位置等于代表的图片的初始位置,它的宽高为代表的图片的宽高。
- 在某种条件下(如每循环一次,如用户按下某个键),使得矩形框的坐标发生变化,而矩形框对象代表的图片按照变化后的坐标绘制图像,即实现了图片的移动。
残影的问题及解决方法
- 如果仅绘制..飞机不绘制背景图的话,飞机就会留下残影,解决方法是,每次绘制都必须绘制背景,且背景优先绘制(代码写前面)。
简单移动示例
- 随着while循环的不断执行,让飞机的矩形框对象的座标不断发生改变。
-
while True: clock.tick(60) #60表示60帧/s # 主角飞机矩形框移动 hero_rect.y -= 1 #hero_rect对象为飞机的矩形框对象 if hero_rect.y <= -126: hero_rect.y=700 # 绘制 screen.blit(bg,(0,0)) #先画背景! screen.blit(hero,hero_rect) #再画飞机。here_rect对象储存矩形框的位置元组 # 更新显示 pygame.display.update()
8 事件的概念及监听的概念
事件
- 事件即用户对游戏做的操作,如鼠标点击、按下键盘、关闭游戏等。
监听
- 监听用来精准判断用户做的事件。只有捕捉到用户做的事件,才能对其作出响应。
监听代码(捕获事件)
- 可以通过以下代码获得当前用户所做的所有操作的事件列表(用户可以同时做很多事件)
event_list = pygame.event.get()
使用print方法输出事件列表查看事件
event_list = pygame.event.get() if len(event_list) > 0:#没有事件时不输出 print(event_list)
获取一段事件列表如下
[<Event(5-MouseButtonDown {'pos': (391, 607), 'window': None, 'button': 1})>] [<Event(6-MouseButtonUp {'pos': (391, 607), 'window': None, 'button': 1})>] [<Event(4-MouseMotion {'rel': (0, 2), 'pos': (391, 609), 'buttons': (0, 0, 0), 'window': None})>]
9 处理捕获到的事件(响应)
思路
- 捕获的是一个事件列表,我们使用for遍历,当遍历到的内容有我们想要的内容时,做出我们想要指定的操作。
- 如:遍历到Quit事件(点击红叉关闭窗口),我们就要写一个关闭游戏的代码(不写关不了)。
事件类型
示例:Quit事件的处理
- 使用循环遍历事件列表
- 当列表中有Quit事件时
- 执行exit()方法退出程序
-
for event in event_list: if event.type == pygame.QUIT: exit() #退出程序
10 精灵及精灵组
传统游戏设计思路的局限性
- 使用传统的游戏设计思路,需要程序要对每张图像针对很多事件作不同的操作,代码量大,不美观。
精灵及精灵组横空出世
- pygame下的模块sprite及它的两个类Sprite、Group
- pygame.sprite.Sprite 精灵类,我们可以把精灵看做角色
- pygame.sprite.Group 而精灵组就是角色组
11 新的游戏思路
planeGame为游戏的入口,它要设置一系列的属性和方法。
12 精灵详解
我们可以把精灵看做角色,而精灵组看作角色组。
使用精灵来统一定义某个角色类如:己方飞机类,敌方飞机类,boss类等。
为了规范,python精灵要专门占据一个python文件。
精灵类需要派生子类,也就是说精灵类并不是一个具体的角色类,它派生的子类才是。
示例,派生英雄子类:
思路:
- 使用init方法传入两个变量,图片地址和初始速度,初始速度默认值为1。
- 使用load方法加载图像
- 使用get_rect方法获得图像对象的矩形框,如:self.rect = self.image.get_rect() 其中image为加载后的image变量。
- 通过update方法调整矩形的位置
13 滚动背景的方法
思路
- 使用两张背景图,在两张背景向下移动的过程中,当第1张图完全移出屏幕后,立即将第一张图的坐标改为初始位置1。当第二张图完全移出屏幕时,立即将第二张图的坐标位置改为位置1。如此循环即可。
14 添加敌机与定时器set_timer()
定时器
- set_timer(eventid, milliseconds) -> None
参数说明:第一个参数eventid通常基于常量pygame.USEREVENT来指定
- USEREVENT是一个整数,再增加的事件可以使用USEREVENT+ 1来指定,以此类推
定义并监听创建敌机的定时器事件
- pygame的定时器使用套路非常固定
- 1.定义定时器变量 eventid
- 2.在初始化方法中,调用set_timer方法设置定时器事件
- 3.在游戏循环中,监听定时器事件
15 Rect类bottom属性、
bottom=y + height
当把bottom设置为0时,飞机的y就为负的height,飞机就会从屏幕外一个飞机height的距离飞进来。
16 销毁精灵 kill()方法
即使消除不需要的对象可以释放内存。
kill()方法可以将精灵从所有组中销毁
17 我方飞机(英雄)及
初始化方法
- 指定英雄图片
- 初始速度=0
- 指定英雄初始位置
- 定义bullets子弹精灵组保存子弹
重写update方法
- 英雄需要水平移动
- 保证不能移出屏幕
增加bullets属性,记录所有子弹精灵
增加fire方法,用于发射子弹
18 获取键盘事件方法pygame.key.get_pressed()及英雄移动
此方法返回键盘的元组,每个键盘都有一个值,当键盘没有被触发时,值为0,当键盘被按下时,值为1
# 方法一:此方法按下键盘只触发一次操作 if event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT:#KEYDDOWN表示事件类型为按下键盘,K_RIGHT表示右键 print("按下右键") # 方法二:使用键盘元组来判断是否按键,解决按下只被执行一次的问题 keys_pressed = pygame.key.get_pressed() # 判断元组中对应的按键索引值是否为1(1为按下) if keys_pressed[pygame.K_RIGHT]: print("向右移动..")
19 子弹事件
先写一个英雄开火的方法
def fire(self): print("发射子弹")#先不用具体写
子弹每隔500ms就会从英雄发出,所以需要用到事件,同创造敌机事件类似,首先:创造子弹事件。
创造子弹事件常量:
HERO_FIRE_EVENT = pygame.USEREVENT + 1 #我也不知道为啥具体,总之因为创造敌机事件使用了USEREVENT
所以这里要+1
设置定时器-子弹事件
pygame.time.set_timer(HERO_FIRE_EVENT,500)
在监听器中监听事件
elif event.type == HERO_FIRE_EVENT: self.hero.fire()#监听到则执行英雄的fire方法
20 定义子弹类
因为子弹也是对象,对于物体,要写一个独立的类。
- 子弹从英雄的正上方向上发射
- 子弹超出屏幕则销毁
20 绘制子弹
和背景、英雄、敌机一样,要显示在屏幕,就要绘制出来。
创建子弹精灵组
在监听器中监听子弹事件,如果监听到,则在fire方法中新建子弹对象,并加入子弹精灵组。
子弹精灵组update、draw
21 精灵组碰撞销毁方法groupcollide
pygame的精灵模块提供了一个精灵组碰撞方法,非常好用。
groupcollide(group1,group2,dokill1,dokill2,collided = None)-->Sprite_dict
第一个及第二个参数为:要检测碰撞的两个精灵组,第三个第四个参数时布尔型,True就表示碰撞后代表的精灵组被销毁,第五个我不知道。
碰撞检测方法卸载主程序的__check_collide中
22 精灵与精灵组的碰撞销毁方法spritecollide
精灵与精灵组的碰撞销毁
pygame.sprite.spritecollide(sprite,group,dokill,collided=None)->Sprite_list
第一个参数为精灵
第二个参数为精灵组
当dokill为True时,精灵与精灵组发生碰撞,死掉的是精灵组的成员。当False,谁都不会死。
方法的返回值Sprite_list为精灵组,如果发生碰撞返回精灵组。
要想精灵死,需要利用这个方法的返回值:
enemys = pygame.sprite.spritecollide(self.hero,self.enemy_group,False) #让英雄死 if len(enemys) > 0: self.hero.kill()