二、飞机大战#

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. 展示背景#

  1. 加载背景图片
1
2
# 加载背景图片
bgSurface = pygame.image.load('img/img_bg_level_1.jpg')
  1. 死循环中展示
1
2
3
4
5
while True:
    # 展示背景
    window.blit(bgSurface,(0,0))
    # 刷新
    pygame.display.flip()

展示效果:

3. 展示我方飞机#

1、加载我方飞机

1
2
# 加载我方飞机图片
heroSurface = pygame.image.load('img/hero2.png')

2、展示我方飞机

1
2
3
4
5
6
7
while True:
    # 展示背景
    window.blit(bgSurface,(0,0))
    # 展示我方飞机
    window.blit(heroSurface,(200,600))
    # 刷新
    pygame.display.flip()

效果展示:

4. 面向对象提取#

对于飞机大战中可以分为三种角色:我方飞机、子弹、敌方坦克

可以对每一种角色分别提取类

类具备的属性和方法

1
2
3
4
5
6
7
8
属性:
    展示的窗口
    展示的坐标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))

展示飞机

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 创建飞机类
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、按下ADSW进行移动

1
2
3
4
5
6
7
8
9
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方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Plane:
    ...
    def fire(self):
        '''
        发射子弹
        :return:
        '''
        # 创建子弹对象
        bullet = Bullet(self.window,self.x,self.y)
        # 添加子弹到bullets容器中
        self.bullets.append(bullet)

3、点击回车键发射子弹

1
2
3
4
elif event.type==KEYDOWN:
    if event.key==K_RETURN:
        # 发射一枚子弹
        plane.fire()

4、展示子弹

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Plane:
    def display(self):
        '''
        把自己展示出来
        :return:
        '''
        self.window.blit(self.surface,(self.x,self.y))
        # 把飞机发射的所有子弹都展示出来
        for bullet in self.bullets:
            # 展示子弹
            bullet.display()

发射子弹展示效果:

8. 子弹位置确定#

子弹要展示到飞机中间位置

计算公式:

1
2
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方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class Plane:
    def fire(self):
        '''
        发射子弹
        :return:
        '''
        # 创建子弹对象
        bullet = Bullet(self.window,self.x,self.y,self.width)
        # 添加子弹到bullets容器中
        self.bullets.append(bullet)

子弹位置:

9. 子弹的自动移动#

子弹自动移动其实就是每次渲染完之后可以自动修改坐标即可

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class Bullt:
    ...
    def display(self):
        '''
        把自己展示出来
        :return:
        '''
        self.window.blit(self.surface,(self.x,self.y))
        # 自动移动
        self.y -= 2

10. 子弹的越界处理#

子弹移动到屏幕外之后,可以销毁掉,避免继续展示消耗内存和性能

1、在子弹类中定义needDestroy,用来判断是否需要被销毁

1
2
3
4
5
6
7
8
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、自动移动

1
2
3
4
5
6
7
8
9
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、定义容器保存敌方飞机

1
2
# 定义敌方飞机容器
enemyList = []

2、循环创建敌方飞机

1
2
3
4
5
6
# 创建敌方飞机对象
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、加载游戏结束文字

1
2
3
# 加载游戏结束文字
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、子弹停止自动移动

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class Bullet:
    def display(self):
        '''
        把自己展示出来
        :return:
        '''
        self.window.blit(self.surface,(self.x,self.y))
        # 自动移动
        if not gameOver:
            self.y -= 2