二、飞机大战
1. 展示飞机大战窗口
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 | import pygame
from pygame.locals import *
import sys
"""
1.标题和图标
2.退出事件
"""
# 初始化
pygame.init()
# 创建游戏窗口
window = pygame.display.set_mode(size=(600,800))
# 窗口标题
pygame.display.set_caption('飞机大战')
# 加载图标图片
iconSurface = pygame.image.load('img/app.ico')
# 设置图标
pygame.display.set_icon(iconSurface)
# 保证窗口不关闭
while True:
# 处理窗口关闭
eventList = pygame.event.get()
for event in eventList:
# 判断是否是退出类型
if event.type==QUIT:
# 退出游戏
pygame.quit()
# 退出程序
sys.exit()
|
2. 展示背景
- 加载背景图片
| # 加载背景图片
bgSurface = pygame.image.load('img/img_bg_level_1.jpg')
|
- 死循环中展示
| while True:
# 展示背景
window.blit(bgSurface,(0,0))
# 刷新
pygame.display.flip()
|
展示效果:
3. 展示我方飞机
1、加载我方飞机
| # 加载我方飞机图片
heroSurface = pygame.image.load('img/hero2.png')
|
2、展示我方飞机
| while True:
# 展示背景
window.blit(bgSurface,(0,0))
# 展示我方飞机
window.blit(heroSurface,(200,600))
# 刷新
pygame.display.flip()
|
效果展示:
4. 面向对象提取
对于飞机大战中可以分为三种角色:我方飞机、子弹、敌方坦克
可以对每一种角色分别提取类
类具备的属性和方法
| 属性:
展示的窗口
展示的坐标x
展示的坐标y
展示的图片
宽度和高度
方法:
展示自己的功能
|
提取我方飞机
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | class Plane:
def __init__(self,x,y,window):
# 坐标
self.x = x
self.y = y
# 被展示到的窗口
self.window = window
# 飞机展示的Surface
self.surface = pygame.image.load('img/hero2.png')
def display(self):
'''
被展示
:return:
'''
# 展示我方飞机
self.window.blit(self.surface, (self.x, self.y))
|
展示飞机
| # 创建飞机类
plane = Plane(200,600,window)
# 保证窗口不关闭
while True:
# 展示背景
window.blit(bgSurface,(0,0))
# 展示飞机
plane.display()
# 刷新
pygame.display.flip()
|
只需要创建一个我方飞机对象,调用display方法即可展示到窗口上
5. 飞机移动
1、飞机具备向上向下向左向右的功能
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 | class Plane:
...
def moveLeft(self):
'''
向左移动
:return:
'''
self.__x -= 1
def moveRight(self):
'''
向右移动
:return:
'''
self.__x += 1
def moveUp(self):
'''
向上移动
:return:
'''
self.__y -= 1
def moveDown(self):
'''
向下移动
:return:
'''
self.__y += 1
|
2、按下A
、D
、S
、W
进行移动
| elif event.type == KEYDOWN: # 按下事件
if event.key == K_a:
plane.moveLeft()
elif event.key == K_d:
plane.moveRight()
elif event.key == K_w:
plane.moveUp()
elif event.key == K_s:
plane.moveDown()
|
6. 飞机越界处理
飞机可以向四个方向移出到屏幕外,需要做越界处理
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 | def moveLeft(self):
'''
飞机向左移动
:return:
'''
self.x -= 1
# 越界处理
if self.x<0:
self.x = 0
def moveRight(self):
'''
飞机向右移动
:return:
'''
self.x += 1
# 越界处理
if self.x>windowW-self.width:
self.x = windowW-self.width
def moveUp(self):
'''
飞机向上移动
:return:
'''
self.y -= 1
# 越界处理
if self.y<0:
self.y = 0
def moveDown(self):
'''
飞机向上移动
:return:
'''
self.y += 1
# 越界
if self.y>windwH-self.height:
self.y = windwH-self.height
|
向上下左右移出屏幕外的时候,强制的将坐标设置到屏幕边缘即可
7. 发射子弹
1、提取子弹类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | class Bullet:
def __init__(self, window, x, y):
# 需要展示的窗口
self.window = window
# 坐标
self.x = x
self.y = y
# 展示的surface
self.surface = pygame.image.load('img/bullet_10.png')
# 子弹的宽度和高度
self.width = self.surface.get_width()
self.height = self.surface.get_height()
def display(self):
'''
把自己展示出来
:return:
'''
self.window.blit(self.surface,(self.x,self.y))
|
2、我方飞机定义发射子弹的fire
方法
| class Plane:
...
def fire(self):
'''
发射子弹
:return:
'''
# 创建子弹对象
bullet = Bullet(self.window,self.x,self.y)
# 添加子弹到bullets容器中
self.bullets.append(bullet)
|
3、点击回车键发射子弹
| elif event.type==KEYDOWN:
if event.key==K_RETURN:
# 发射一枚子弹
plane.fire()
|
4、展示子弹
| class Plane:
def display(self):
'''
把自己展示出来
:return:
'''
self.window.blit(self.surface,(self.x,self.y))
# 把飞机发射的所有子弹都展示出来
for bullet in self.bullets:
# 展示子弹
bullet.display()
|
发射子弹展示效果:
8. 子弹位置确定
子弹要展示到飞机中间位置
计算公式:
| bx = px + (pw - bw) / 2
by = py - bh / 2
|
bx、by:子弹的x和y坐标
px、py:飞机的x和y坐标
pw:飞机的宽度
bw:子弹的宽度
1、修改Bullet的初始化方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | class Bullet:
def __init__(self, window, px, py,pw):
'''
:param window:
:param px: 飞机的x坐标
:param py: 飞机的y坐标
:param pw: 飞机的宽度
'''
# 展示的surface
self.surface = pygame.image.load('img/bullet_10.png')
# 子弹的宽度和高度
self.width = self.surface.get_width()
self.height = self.surface.get_height()
# 需要展示的窗口
self.window = window
# bx = px + (pw - bw) / 2
# by = py - bh / 2
# 坐标
self.x = px+(pw-self.width)/2
self.y = py-self.height/2
|
2、修改飞机的fire
方法
| class Plane:
def fire(self):
'''
发射子弹
:return:
'''
# 创建子弹对象
bullet = Bullet(self.window,self.x,self.y,self.width)
# 添加子弹到bullets容器中
self.bullets.append(bullet)
|
子弹位置:
9. 子弹的自动移动
子弹自动移动其实就是每次渲染完之后可以自动修改坐标即可
| class Bullt:
...
def display(self):
'''
把自己展示出来
:return:
'''
self.window.blit(self.surface,(self.x,self.y))
# 自动移动
self.y -= 2
|
10. 子弹的越界处理
子弹移动到屏幕外之后,可以销毁掉,避免继续展示消耗内存和性能
1、在子弹类中定义needDestroy
,用来判断是否需要被销毁
| class Bullet:
...
def needDestroy(self):
'''
子弹对象是否需要被销毁
:return:
'''
return self.y<-self.height
|
2、在飞机的display方法中处理销毁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | class Plane:
...
def display(self):
'''
把自己展示出来
:return:
'''
print(len(self.bullets))
self.window.blit(self.surface,(self.x,self.y))
# 判断子弹是否需要被销毁
for bullet in self.bullets:
if bullet.needDestroy():
print('需要销毁')
# 需要销毁
self.bullets.remove(bullet)
# 把飞机发射的所有子弹都展示出来
for bullet in self.bullets:
# 展示子弹
bullet.display()
|
11. 展示敌方飞机
1、提取敌方飞机类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | class EnemyPlane:
def __init__(self,window):
# 需要展示的窗口
self.window = window
# 展示的surface
self.surface = pygame.image.load('img/img-plane_1.png')
# 飞机的宽度和高度
self.width = self.surface.get_width()
self.height = self.surface.get_height()
# 坐标
self.x = 100
self.y = -self.height+100
def display(self):
'''
把自己展示出来
:return:
'''
self.window.blit(self.surface,(self.x,self.y))
|
2、创建敌方飞机对象并展示
1
2
3
4
5
6
7
8
9
10
11
12 | # 创建敌方飞机对象
enemy = EnemyPlane(window)
while True:
# 展示在window窗口上
window.blit(bg,(0,0))
# 展示飞机
plane.display()
# 展敌方飞机
enemy.display()
# 刷新
pygame.display.flip()
|
展示敌方飞机:
12. 敌方飞机的自动移动和越界处理
1、自动移动
| class EnemyPlane:
def display(self):
'''
把自己展示出来
:return:
'''
self.window.blit(self.surface,(self.x,self.y))
# 自动移动
self.y += 1
|
2、越界处理
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 | class Plane:
def reset(self):
'''
飞机重置
:return:
'''
# 展示的surface
self.surface = pygame.image.load('img/img-plane_{}.png'.format(random.randint(1,7)))
# 飞机的宽度和高度
self.width = self.surface.get_width()
self.height = self.surface.get_height()
# 坐标
self.x = random.randint(0,windowW-self.width)
self.y = -self.height
def display(self):
'''
把自己展示出来
:return:
'''
self.window.blit(self.surface,(self.x,self.y))
# 自动移动
self.y += 1
# 如果飞机超出窗口 重置
if self.y>windwH:
self.reset()
|
越界之后重置飞机位置,不需要销毁
敌方飞机和子弹碰撞
pygame中任何控件都是一个矩形
判断飞机和子弹碰撞,其实就是判断两个矩形是否相交
1、在Bullet中定义判断是否相交的方法
1
2
3
4
5
6
7
8
9
10
11
12 | class Bullet:
def hasCollision(self,enemy):
'''
判断子弹是否和飞机发生了碰撞
@:param enemy 敌方飞机
:return:
'''
# 子弹矩形
bulletRect = pygame.Rect(self.x,self.y,self.width,self.height)
# 敌方飞机矩形
enemyRect = pygame.Rect(enemy.x,enemy.y,enemy.width,enemy.height)
return bulletRect.colliderect(enemyRect)
|
构建子弹的矩形和敌方飞机矩形,返回两个矩形是否相交
2、在Plane的display方法中判断子弹和敌方飞机是否相交
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | class Plane:
def display(self):
'''
把自己展示出来
:return:
'''
print(len(self.bullets))
self.window.blit(self.surface,(self.x,self.y))
# 是否发生碰撞
for bullet in self.bullets:
if bullet.hasCollision(enemy):
# 发生了碰撞
# 子弹销毁
self.bullets.remove(bullet)
# 敌方飞机重置
enemy.reset()
...
|
13. 我方飞机和敌方飞机碰撞
1、在Plane中定义判断碰撞的方法
1
2
3
4
5
6
7
8
9
10
11
12
13 | class Plane:
...
def hasCollison(self,enemy):
'''
是否和敌方飞机发生了碰撞
:param enemy:
:return:
'''
# 我方飞机矩形
planeRect = pygame.Rect(self.x, self.y, self.width, self.height)
# 敌方飞机矩形
enemyRect = pygame.Rect(enemy.x, enemy.y, enemy.width, enemy.height)
return planeRect.colliderect(enemyRect)
|
2、在display中判断是否相交
1
2
3
4
5
6
7
8
9
10
11
12
13 | class Plane:
def display(self):
'''
把自己展示出来
:return:
'''
...
# 判断我方飞机和敌方飞机是否发生了碰撞
if self.hasCollison(enemy):
# 敌方飞机重置
enemy.reset()
# 我方飞机掉血
self.hp -= 1
|
14. 敌方飞机扩容
敌方飞机可以默认展示5个,循环使用
1、定义容器保存敌方飞机
| # 定义敌方飞机容器
enemyList = []
|
2、循环创建敌方飞机
| # 创建敌方飞机对象
for index in range(0,5):
# 创建敌方飞机
enemy = EnemyPlane(window)
# 添加到容器中
enemyList.append(enemy)
|
3、修改我方飞机和敌方飞机碰撞检测的逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | class Plane:
def display(self):
'''
把自己展示出来
:return:
'''
...
# 判断我方飞机和敌方飞机是否发生了碰撞
for enemy in enemyList:
if self.hasCollison(enemy):
# 敌方飞机重置
enemy.reset()
# 我方飞机掉血
self.hp -= 1
# 跳出循环
break
|
4、修改子弹和敌方飞机检测的逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | def display(self):
'''
把自己展示出来
:return:
'''
...
# 子弹和敌方飞机是否发生碰撞
for bullet in self.bullets:
for enemy in enemyList:
if bullet.hasCollision(enemy):
# 发生了碰撞
# 子弹销毁
self.bullets.remove(bullet)
# 敌方飞机重置
enemy.reset()
# 跳出循环
break
|
15. 游戏结束
游戏结束之后,画面静止,显示游戏结束的文字
1、加载游戏结束文字
| # 加载游戏结束文字
font = pygame.font.Font('font/happy.ttf',50)
gameOverSurface = font.render('游戏结束',True,(255,255,255))
|
2、死循环中判断游戏结束
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | while True:
# 展示在window窗口上
window.blit(bg,(0,0))
# 展示飞机
plane.display()
# 展敌方飞机
for enemy in enemyList:
# 展示
enemy.display()
# 游戏结束判断
if plane.hp<=0:
# 修改变量
gameOver = True
# 游戏结束
window.blit(gameOverSurface,((windowW-fontW)/2,(windowH-fontH)/2))
# 刷新
pygame.display.flip()
|
gameOver是用来记录是否游戏结束的,默认为False
3、敌方飞机停止自动移动
1
2
3
4
5
6
7
8
9
10
11
12
13 | class EnemyPlane:
def display(self):
'''
把自己展示出来
:return:
'''
self.window.blit(self.surface,(self.x,self.y))
# 自动移动
if not gameOver:
self.y += 1
# 如果飞机超出窗口 重置
if self.y>windowH:
self.reset()
|
4、子弹停止自动移动
| class Bullet:
def display(self):
'''
把自己展示出来
:return:
'''
self.window.blit(self.surface,(self.x,self.y))
# 自动移动
if not gameOver:
self.y -= 2
|