第092讲: Pygame:飞机大战3 | 学习记录(小甲鱼零基础入门学习Python)

当敌方飞机和我方飞机发生碰撞时,两方应该是玉石俱焚的,所以在完成之前的基础操作后,现在要为每一个类增加撞击时发生的惨烈画面,具体情况如下:
enemy1:

enemy2:

第092讲: Pygame:飞机大战3 | 学习记录(小甲鱼零基础入门学习Python)_第1张图片

enemy3:

第092讲: Pygame:飞机大战3 | 学习记录(小甲鱼零基础入门学习Python)_第2张图片

me:

第092讲: Pygame:飞机大战3 | 学习记录(小甲鱼零基础入门学习Python)_第3张图片

我们现在就来写代码:

首先为每一个类把这些图片加进去:

今天突然发现,如果像我之前那么写的话,可能会发生很多的问题,因为我之前只是把所有的图像都一次性把路径传入,然后如果想要画出来的画,还要把路径传入到飞机的类中,这样就导致,在创建类的时候需要传入许多的参数,所以这样的话会导致代码更加的复杂,可读性很差,所以今天我们在这里修改一下,直接将这些碰撞爆炸的图片直接导入,不使用路径。


"""\/\/\/\/\/\/\/\/------我的飞机类------\/\/\/\/\/\/\/\/"""
class MyPlane (pygame.sprite.Sprite):       #定义我的飞机类,继承动画精灵类(因为之后要进行碰撞检测)
    def __init__(self,bg_size,me1_image,me2_image) :
        pygame.sprite.Sprite.__init__(self)
        self.me1_image = me1_image
        self.me2_image = me2_image
        self.image1 = pygame.image.load(me1_image).convert_alpha()  #第一张飞机图片
        self.image2 = pygame.image.load(me2_image).convert_alpha()  #第二张飞机图片 两张图片是为了做出飞机喷气的效果
        #添加坠机图片
        self.destroy_images = []
        self.me_destory_1_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\me_destroy_1.png").convert_alpha()    #自身飞机炸毁第1张图片
        self.me_destory_2_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\me_destroy_2.png").convert_alpha()    #自身飞机炸毁第2张图片
        self.me_destory_3_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\me_destroy_3.png").convert_alpha()    #自身飞机炸毁第3张图片
        self.me_destory_4_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\me_destroy_4.png" ).convert_alpha()   #自身飞机炸毁第4张图片
        self.destroy_images.extend([self.me_destory_1_image,self.me_destory_2_image,self.me_destory_3_image,self.me_destory_4_image]
        
        self.rect = self.image1.get_rect()  #获得飞机图片的尺寸
        self.width , self.height = bg_size[0] , bg_size[1]  #设置飞机允许活动地点
        self.rect.left , self.rect.top = [ (self.width - self.rect.width) // 2 ,  \
                                                     (self.height - self.rect.height - 60) ]  #设置我方飞机出现的位置
        self.speed = 10 #设置飞机出现的速度
        
    def moveup(self):   #飞机向上飞的函数
        if self.rect.top > 0: #如果我方飞机没有飞出 上方边界
            self.rect.top -= 10 #那么我方飞机朝上飞10个像素
        else :  #飞出 上方边界
            self.rect.top = 0   #锁定在0的位置,不再发生变化
    
    def movedown(self):     #飞机向下飞的函数
        if self.rect.bottom < (self.height - 60) : #如果我方飞机没有飞出 上方边界
            self.rect.bottom += 10 #那么我方飞机朝下飞10个像素
        else :  #飞出 下方边界
            self.rect.bottom = (self.height - 60)  #锁定在底边界上方60的位置,不再发生变化

    
    def moveleft(self):     #飞机向左飞的函数
        if self.rect.left > 0 : #如果我方飞机没有飞出 左方边界
            self.rect.left -= 10 #那么我方飞机朝下左10个像素
        else :  #飞出 左方边界
            self.rect.left = 0  #锁定在左边界的位置,不再发生变化

    def moveright(self):     #飞机向下飞的函数
        if self.rect.right < self.width : #如果我方飞机没有飞出 上方边界
            self.rect.right += 10 #那么我方飞机朝下飞10个像素
        else :  #飞出 下方边界
            self.rect.right = self.width #锁定在底边界的位置,不再发生变化
"""\/\/\/\/\/\/\/\/------敌机类------\/\/\/\/\/\/\/\/"""
"""本来这里其实可以使用类的继承,然后三种敌机分别继承这个敌机类生成新的敌机,但是在这里、
    没有这样做的必要,因为三个类的差别并不是很大,所以不用那么繁杂"""
class SmallEnemy(pygame.sprite.Sprite):  #定义我的低敌机类,继承自动画精灵
    def __init__(self,bg_size,enemy1_image):
        pygame.sprite.Sprite.__init__(self)

        self.enemy1_image = enemy1_image
        self.enemy1 = pygame.image.load(enemy1_image).convert_alpha()   #载入低级敌机照片
        #添加坠机图片
        self.destroy_images = []
        self.enemy1_down1_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy1_down1.png").convert_alpha()    #低级敌机撞到爆炸第1张图片
        self.enemy1_down2_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy1_down2.png").convert_alpha()      #低级敌机撞到爆炸第2张图片
        self.enemy1_down3_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy1_down3.png").convert_alpha()      #低级敌机撞到爆炸第3张图片
        self.enemy1_down4_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy1_down4.png").convert_alpha()      #低级敌机撞到爆炸第4张图片
        self.destroy_images.extend([self.enemy1_down1_image,self.enemy1_down2_image,self.enemy1_down3_image,self.enemy1_down4_image])
        
        self.width ,self.height = bg_size[0] , bg_size[1]   #获取生成低级敌机的活动范围
        self.rect = self.enemy1.get_rect()   #获得低级敌机的尺寸
        self.rect.left , self.rect.top = [ randint(0 , self.width - self.rect.width ) ,\
                                                     randint(-5*self.height , 0) ]      #在窗口的5倍之上的距离中产生飞机
        self.speed = 3  #设置小敌机移动的速度

    def move(self) :    #小敌机的移动函数
        if self.rect.top  < self.height :    #如果小飞机的底超出窗口的下边框
            self.rect.top += self.speed  #小飞机向下移动他的速度
        else :  #如果小飞机已经超出窗口的下边界框
            self.reset() #重置小飞机

    def reset(self) :   #重置小飞机函数
        self.rect.left , self.rect.top = [ randint(0 , self.width - self.rect.width ) ,\
                                                     randint(-5*self.height , 0) ]      #在窗口的5倍之上的距离中产生飞机

class MiddleEnemy(pygame.sprite.Sprite):  #定义我的中敌机类,继承自动画精灵
    def __init__(self,bg_size,enemy2_image):
        pygame.sprite.Sprite.__init__(self)
        self.enemy2_image = enemy2_image
        self.enemy2 = pygame.image.load(enemy2_image).convert_alpha()   #载入中级敌机照片
        #添加坠机图片
        self.destroy_images = []
        self.enemy2_down1_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy2_down1.png").convert_alpha()    #中级敌机撞到爆炸第1张图片
        self.enemy2_down2_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy2_down2.png").convert_alpha()    #中级敌机撞到爆炸第2张图片
        self.enemy2_down3_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy2_down3.png").convert_alpha()    #中级敌机撞到爆炸第3张图片
        self.enemy2_down4_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy2_down4.png").convert_alpha()    #中级敌机撞到爆炸第4张图片
        self.enemy2_hit_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy2_hit.png").convert_alpha()   #中级敌机被子弹打到后图片
        self.destroy_images.extend([self.enemy2_down1_image,self.enemy2_down2_image,self.enemy2_down3_image,self.enemy2_down4_image])
        
        self.width ,self.height = bg_size[0] , bg_size[1]   #获取生成低级敌机的活动范围
        self.rect = self.enemy2.get_rect()   #获得中级敌机的尺寸
        self.rect.left , self.rect.top = [ randint(0 , self.width - self.rect.width ) ,\
                                                     randint(-5*self.height , 0) ]      #在窗口的5倍之上的距离中产生飞机
        self.speed = 2  #设置中敌机移动的速度

    def move(self) :    #中敌机的移动函数
        if self.rect.top < self.height :    #如果中飞机的底超出窗口的下边框
            self.rect.top += self.speed  #中飞机向下移动他的速度
        else :  #如果中飞机已经超出窗口的下边界框
            self.reset() #重置中飞机

    def reset(self) :   #重置中飞机函数
        self.rect.left , self.rect.top = [ randint(0 , self.width - self.rect.width ) ,\
                                                     randint(-5*self.height , 0) ]      #在窗口的5倍之上的距离中产生飞机

class BigEnemy(pygame.sprite.Sprite):  #定义我的大敌机类,继承自动画精灵
    def __init__(self,bg_size,enemy3_n1_image,enemy3_n2_image):
        pygame.sprite.Sprite.__init__(self)
        self.enemy3_n1_image = enemy3_n1_image
        self.enemy3_n1 = pygame.image.load(enemy3_n1_image).convert_alpha()   #载入高级敌机照片
        self.enemy3_n2_image = enemy3_n1_image
        self.enemy3_n2 = pygame.image.load(enemy3_n2_image).convert_alpha()   #载入高级敌机照片
        #添加坠机图片
        self.destroy_images = []
        self.enemy3_down1_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy3_down1.png").convert_alpha()    #高级敌机撞到爆炸第1张图片
        self.enemy3_down2_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy3_down2.png").convert_alpha()    #高级敌机撞到爆炸第2张图片
        self.enemy3_down3_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy3_down3.png").convert_alpha()    #高级敌机撞到爆炸第3张图片
        self.enemy3_down4_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy3_down4.png").convert_alpha()    #高级敌机撞到爆炸第4张图片
        self.enemy3_down5_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy3_down5.png").convert_alpha()    #高级敌机撞到爆炸第5张图片
        self.enemy3_down6_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy3_down6.png").convert_alpha()    #高级敌机撞到爆炸第6张图片
        self.enemy3_hit_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy3_hit.png").convert_alpha()  #高级敌机被子弹打到后图片
        self.destroy_images.extend([self.enemy3_down1_image,self.enemy3_down1_image,self.enemy3_down3_image,self.enemy3_down4_image,\
                                                self.enemy3_down5_image,self.enemy3_down6_image ])
        
        
        self.width ,self.height = bg_size[0] , bg_size[1]   #获取生成低级敌机的活动范围
        self.rect = self.enemy3_n1.get_rect()   #获得低级敌机的尺寸
        self.rect.left , self.rect.top = [ randint(0 , self.width - self.rect.width ) ,\
                                                     randint(-5*self.height , 0) ]      #在窗口的5倍之上的距离中产生飞机
        self.speed = 1  #设置大敌机移动的速度

    def move(self) :    #大敌机的移动函数
        if self.rect.top < self.height :    #如果大飞机的底超出窗口的下边框
            self.rect.top += self.speed  #大飞机向下移动他的速度
        else :  #如果大飞机已经超出窗口的下边界框
            self.reset() #重置大飞机

    def reset(self) :   #重置大飞机函数
        self.rect.left , self.rect.top = [ randint(0 , self.width - self.rect.width ) ,\
                                                     randint(-5*self.height , 0) ]      #在窗口的5倍之上的距离中产生飞机

接下来我们要为每一个类添加 active 属性,该属性表示 还活着,也就是说,当该属性的值为 True 时,我们就让飞机显示正常的飞行状态,而当该属性为 False 时,表示该飞机已经遇难了,我们就要依次显示 坠机的画面,显示完之后,调用该类的 resert() 方法。

self.active = True

接下来修改 main 模块的main() 函数,在绘制每一种飞机时,先检测它的 active 的值:
(因为这个时候我们还没有对我方飞机进行reset()函数设置所以的话)就直接定格不再绘图片

# 绘制大型敌机
        for each in bigenemies:
            if each.active: #如果大型敌机为生存状态
                each.move()
                if switch_image:    #判断是否交换图片
                    screen.blit(each.enemy3_n1, each.rect)
                else:
                    screen.blit(each.enemy3_n2, each.rect)
                if not(delay % 5) : #设置两章照片转换的时间延时
                        switch_image = not switch_image
                # 即将出现在画面中,播放音效
                if each.rect.bottom > -50:
                    enemy3_fly_sound.play()
            else:   
                #如果大型敌机为撞毁状态
                enemy3_down_sound.play()
                if not (delay % 3) :
                    screen.blit(each.destroy_images[e3_destroy_index], each.rect)
                    e3_destroy_index = (e3_destroy_index + 1) % 4
                    if e3_destroy_index == 0:   #当索引再次变为0时
                        each.reset()    #重置飞机
        # 绘制中型敌机:
        for each in middleenemies:
            if each.active :    #如果此敌机的为存活状态
                each.move()
                screen.blit(each.enemy2, each.rect)
            else :
                #敌机撞毁
                enemy2_down_sound.play()
                if not (delay % 3) :
                    screen.blit(each.destroy_images[e2_destroy_index], each.rect)
                    e2_destroy_index = (e2_destroy_index + 1) % 4
                    if e2_destroy_index == 0:   #当索引再次变为0时
                        each.reset()    #重置飞机
        # 绘制小型敌机:
        for each in smallenemies:
            if each.active :    #如果此敌机的为存活状态
                each.move()
                screen.blit(each.enemy1, each.rect)
            else :
                #敌机撞毁
                enemy1_down_sound.play()
                if not (delay % 3) :
                    screen.blit(each.destroy_images[e1_destroy_index], each.rect)
                    e1_destroy_index = (e1_destroy_index + 1) % 4
                    if e1_destroy_index == 0:   #当索引再次变为0时
                        each.reset()    #重置飞机


        #绘制我方飞机
        if myself.active :  #如果我方飞机active的属性为真,即未和其他飞机发生碰撞
            if transform :  #如果改变属性为真  画第一张飞机图
                screen.blit(myself.image1,myself.rect)  #绘制第一张飞机图         
            else :  #如果改变属性为假  画第二张飞机图
                screen.blit(myself.image2,myself.rect)  #绘制第二张飞机图
        else:
            #我方飞机撞毁
            me_down_sound.play()
            if not (delay % 3) :
                screen.blit(me.destroy_images[me_destroy_index], me.rect)
                me_destroy_index = (me_destroy_index + 1) % 4
                if me_destroy_index == 0:
                    print("Game Over!")
                    running = False

我们接着就要写碰撞检测代码:

一旦我方飞机碰撞到敌机,导致的结果必将是敌我同归于尽。

#检查双方飞机是否发生碰撞
        enemies_down = pygame.sprite.spritecollide(myself, enemies, False)  #将所有发生碰撞的敌方飞机都加入到组中
        if enemies_down:       #如果组中有成员,说明又发生碰撞
            #myself.active = False   #我放飞机存活状态变成False
            for e in enemies_down:  
                e.active = False    #对于与我相撞的飞机的也设为False

我们碰撞检测使用 spritecollide() 方法:
碰撞和坠机是实现了,但是我们发现,两个飞机还没真正的接触就已经撞上了,这是为什么呢?

这是因为我们使用了普通的 spritecollide() 方法,这个方法默认情况下是以图片的矩形位置区域作为检测的范围,而我们飞机的矩形范围远大于自身,但是我们完全是可以做得更好的,我们可以做到完美的碰撞检测,怎么实现呢?

sprite 模块里有一个叫做 collide_mask 的函数可以利用,这个函数要求检测对象拥有一个 mask 属性,这个属性就是用于指定检测的范围,关于 mask,Pygame 还专门写了一个 mask 模块,其中有一个叫做 from_surface() 的函数,可以将 Surface 对象中非透明的部分标记为 mask 并返回。

首先,给每个类加上 mask 属性:

self.mask = pygame.mask.from_sueface(self.image) #将非透明部分标记为mask

然后在main 函数中修改碰撞检测代码,指定检测方法为 pygame.sprite.collide_mask,即:

#检查双方飞机是否发生碰撞
        enemies_down = pygame.sprite.spritecollide(myself, enemies, False,collided=pygame.sprite.collide_mask)  #将所有发生碰撞的敌方飞机都加入到组中
        if enemies_down:       #如果组中有成员,说明又发生碰撞
            #myself.active = False   #我放飞机存活状态变成False
            for e in enemies_down:  
                e.active = False    #对于与我相撞的飞机的也设为False

好了,这就是完美的碰撞检测了
第092讲: Pygame:飞机大战3 | 学习记录(小甲鱼零基础入门学习Python)_第4张图片
刚才我们的代码实际上是存在很明显的Bug,导致部分音效无法正常播放,

可以看一下这里的代码:

		# 绘制大型敌机
        for each in bigenemies:
            if each.active: #如果大型敌机为生存状态
                each.move()
                if switch_image:    #判断是否交换图片
                    screen.blit(each.enemy3_n1, each.rect)
                else:
                    screen.blit(each.enemy3_n2, each.rect)
                # 即将出现在画面中,播放音效
                if each.rect.bottom == -50:
                    enemy3_fly_sound.play()
            else:   
                #如果大型敌机为撞毁状态
                enemy3_down_sound.play()    #播放声音
                if not (delay % 3) :
                    screen.blit(each.destroy_images[e3_destroy_index], each.rect)
                    e3_destroy_index = (e3_destroy_index + 1) % 6
                    if e3_destroy_index == 0:   #当索引再次变为0时
                        enemy3_down_sound.stop()
                        each.reset()    #重置飞机

大家看看,不管是我们的飞机,还是敌机,它们是怎么坠机的,首先,我们检测 active,如果为False 的话,就播放音效,然后画一张坠机图,这样就一帧过去了,然后第二帧,又播放音效,画图的进不去,然后第三帧,播放音效,然后画下面的一张坠机图。

大家发现问题没有,你要画它坠机的图片,需要好多帧,但是这里你重复的播放了好多次它坠机的音效,一个飞机坠机只需要播放一次音效即可,你要播放很多次,导致的结果就是你把所有音效的通道都占满了,因为Pygame 默认只有8条音效的通道。

我们现在要做的就是让它只播放一次。修改如下:

        # 绘制大型敌机
        for each in bigenemies:
            if each.active: #如果大型敌机为生存状态
                each.move()
                if switch_image:    #判断是否交换图片
                    screen.blit(each.enemy3_n1, each.rect)
                else:
                    screen.blit(each.enemy3_n2, each.rect)
                # 即将出现在画面中,播放音效
                if each.rect.bottom == -50:
                    enemy3_fly_sound.play()
            else:   
                #如果大型敌机为撞毁状态
                if not (delay % 3) :
                    if e3_destroy_index == 0:   #当索引再次变为0时
                        enemy3_down_sound.play()    #播放声音
                    screen.blit(each.destroy_images[e3_destroy_index], each.rect)
                    e3_destroy_index = (e3_destroy_index + 1) % 6
                    if e3_destroy_index == 0:   #当索引再次变为0时
                        enemy3_down_sound.stop()
                        each.reset()    #重置飞机

现在再来测试一下就好多了,当然,如果你嫌默认的8个音效通道太少的话,可以使用 mixer 模块的 set_num_channels() 方法来设置通道数多一点。

下面贴出这节课实现的完整代码:

(之后的代码全部都用一个主函数的形式实现)
from pygame.locals import *
from random import *
import traceback
import tkinter as tk
import pygame
import math
import sys

"""\/\/\/\/\/\/\/\/------我的飞机类------\/\/\/\/\/\/\/\/"""
class MyPlane (pygame.sprite.Sprite):       #定义我的飞机类,继承动画精灵类(因为之后要进行碰撞检测)
    def __init__(self,bg_size,me1_image,me2_image) :
        pygame.sprite.Sprite.__init__(self)
        self.me1_image = me1_image
        self.me2_image = me2_image
        self.image1 = pygame.image.load(me1_image).convert_alpha()  #第一张飞机图片
        self.image2 = pygame.image.load(me2_image).convert_alpha()  #第二张飞机图片 两张图片是为了做出飞机喷气的效果
        #添加坠机图片
        self.destroy_images = []
        self.me_destory_1_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\me_destroy_1.png").convert_alpha()    #自身飞机炸毁第1张图片
        self.me_destory_2_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\me_destroy_2.png").convert_alpha()    #自身飞机炸毁第2张图片
        self.me_destory_3_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\me_destroy_3.png").convert_alpha()    #自身飞机炸毁第3张图片
        self.me_destory_4_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\me_destroy_4.png" ).convert_alpha()   #自身飞机炸毁第4张图片
        self.destroy_images.extend([self.me_destory_1_image,self.me_destory_2_image,self.me_destory_3_image,self.me_destory_4_image])
        
        self.rect = self.image1.get_rect()  #获得飞机图片的尺寸
        self.width , self.height = bg_size[0] , bg_size[1]  #设置飞机允许活动地点
        self.rect.left , self.rect.top = [ (self.width - self.rect.width) // 2 ,  \
                                                     (self.height - self.rect.height - 60) ]  #设置我方飞机出现的位置
        self.mask = pygame.mask.from_surface(self.image1)  #给类添加上mask属性,之后可以按照完美碰撞
        self.speed = 10 #设置飞机出现的速度
        self.active = True#设置飞机是否生存的状态

    def moveup(self):   #飞机向上飞的函数
        if self.rect.top > 0: #如果我方飞机没有飞出 上方边界
            self.rect.top -= 10 #那么我方飞机朝上飞10个像素
        else :  #飞出 上方边界
            self.rect.top = 0   #锁定在0的位置,不再发生变化
    
    def movedown(self):     #飞机向下飞的函数
        if self.rect.bottom < (self.height - 60) : #如果我方飞机没有飞出 上方边界
            self.rect.bottom += 10 #那么我方飞机朝下飞10个像素
        else :  #飞出 下方边界
            self.rect.bottom = (self.height - 60)  #锁定在底边界上方60的位置,不再发生变化

    
    def moveleft(self):     #飞机向左飞的函数
        if self.rect.left > 0 : #如果我方飞机没有飞出 左方边界
            self.rect.left -= 10 #那么我方飞机朝下左10个像素
        else :  #飞出 左方边界
            self.rect.left = 0  #锁定在左边界的位置,不再发生变化

    def moveright(self):     #飞机向下飞的函数
        if self.rect.right < self.width : #如果我方飞机没有飞出 上方边界
            self.rect.right += 10 #那么我方飞机朝下飞10个像素
        else :  #飞出 下方边界
            self.rect.right = self.width #锁定在底边界的位置,不再发生变化

"""\/\/\/\/\/\/\/\/------敌机类------\/\/\/\/\/\/\/\/"""
"""本来这里其实可以使用类的继承,然后三种敌机分别继承这个敌机类生成新的敌机,但是在这里、
    没有这样做的必要,因为三个类的差别并不是很大,所以不用那么繁杂"""
class SmallEnemy(pygame.sprite.Sprite):  #定义我的低敌机类,继承自动画精灵
    def __init__(self,bg_size,enemy1_image):
        pygame.sprite.Sprite.__init__(self)

        self.enemy1_image = enemy1_image
        self.enemy1 = pygame.image.load(enemy1_image).convert_alpha()   #载入低级敌机照片
        #添加坠机图片
        self.destroy_images = []
        self.enemy1_down1_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy1_down1.png").convert_alpha()    #低级敌机撞到爆炸第1张图片
        self.enemy1_down2_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy1_down2.png").convert_alpha()      #低级敌机撞到爆炸第2张图片
        self.enemy1_down3_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy1_down3.png").convert_alpha()      #低级敌机撞到爆炸第3张图片
        self.enemy1_down4_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy1_down4.png").convert_alpha()      #低级敌机撞到爆炸第4张图片
        self.destroy_images.extend([self.enemy1_down1_image,self.enemy1_down2_image,self.enemy1_down3_image,self.enemy1_down4_image])
        
        self.width ,self.height = bg_size[0] , bg_size[1]   #获取生成低级敌机的活动范围
        self.rect = self.enemy1.get_rect()   #获得低级敌机的尺寸
        self.rect.left , self.rect.top = [ randint(0 , self.width - self.rect.width ) ,\
                                                     randint(-5*self.height , 0) ]      #在窗口的5倍之上的距离中产生飞机

        self.mask = pygame.mask.from_surface(self.enemy1)  #给类添加上mask属性,之后可以按照完美碰撞
        self.speed = 3  #设置小敌机移动的速度
        self.active = True  #设置飞机是否生存的状态

    def move(self) :    #小敌机的移动函数
        if self.rect.top  < self.height :    #如果小飞机的底超出窗口的下边框
            self.rect.top += self.speed  #小飞机向下移动他的速度
        else :  #如果小飞机已经超出窗口的下边界框
            self.reset() #重置小飞机

    def reset(self) :   #重置小飞机函数
        self.active = True#设置飞机是否生存的状态
        self.rect.left , self.rect.top = [ randint(0 , self.width - self.rect.width ) ,\
                                                     randint(-5*self.height , 0) ]      #在窗口的5倍之上的距离中产生飞机

class MiddleEnemy(pygame.sprite.Sprite):  #定义我的中敌机类,继承自动画精灵
    def __init__(self,bg_size,enemy2_image):
        pygame.sprite.Sprite.__init__(self)
        self.enemy2_image = enemy2_image
        self.enemy2 = pygame.image.load(enemy2_image).convert_alpha()   #载入中级敌机照片
        #添加坠机图片
        self.destroy_images = []
        self.enemy2_down1_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy2_down1.png").convert_alpha()    #中级敌机撞到爆炸第1张图片
        self.enemy2_down2_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy2_down2.png").convert_alpha()    #中级敌机撞到爆炸第2张图片
        self.enemy2_down3_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy2_down3.png").convert_alpha()    #中级敌机撞到爆炸第3张图片
        self.enemy2_down4_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy2_down4.png").convert_alpha()    #中级敌机撞到爆炸第4张图片
        self.enemy2_hit_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy2_hit.png").convert_alpha()   #中级敌机被子弹打到后图片
        self.destroy_images.extend([self.enemy2_down1_image,self.enemy2_down2_image,self.enemy2_down3_image,self.enemy2_down4_image])
        
        self.width ,self.height = bg_size[0] , bg_size[1]   #获取生成低级敌机的活动范围
        self.rect = self.enemy2.get_rect()   #获得中级敌机的尺寸
        self.rect.left , self.rect.top = [ randint(0 , self.width - self.rect.width ) ,\
                                                     randint(-10*self.height , 0) ]      #在窗口的5倍之上的距离中产生飞机
        
        self.mask = pygame.mask.from_surface(self.enemy2)  #给类添加上mask属性,之后可以按照完美碰撞
        self.speed = 2  #设置中敌机移动的速度
        self.active = True  #设置飞机是否生存的状态

    def move(self) :    #中敌机的移动函数
        if self.rect.top < self.height :    #如果中飞机的底超出窗口的下边框
            self.rect.top += self.speed  #中飞机向下移动他的速度
        else :  #如果中飞机已经超出窗口的下边界框
            self.reset() #重置中飞机

    def reset(self) :   #重置中飞机函数
        self.active = True#设置飞机是否生存的状态
        self.rect.left , self.rect.top = [ randint(0 , self.width - self.rect.width ) ,\
                                                     randint(-10*self.height , 0) ]      #在窗口的5倍之上的距离中产生飞机

class BigEnemy(pygame.sprite.Sprite):  #定义我的大敌机类,继承自动画精灵
    def __init__(self,bg_size,enemy3_n1_image,enemy3_n2_image):
        pygame.sprite.Sprite.__init__(self)
        self.enemy3_n1_image = enemy3_n1_image
        self.enemy3_n1 = pygame.image.load(enemy3_n1_image).convert_alpha()   #载入高级敌机照片
        self.enemy3_n2_image = enemy3_n1_image
        self.enemy3_n2 = pygame.image.load(enemy3_n2_image).convert_alpha()   #载入高级敌机照片
        #添加坠机图片
        self.destroy_images = []
        self.enemy3_down1_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy3_down1.png").convert_alpha()    #高级敌机撞到爆炸第1张图片
        self.enemy3_down2_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy3_down2.png").convert_alpha()    #高级敌机撞到爆炸第2张图片
        self.enemy3_down3_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy3_down3.png").convert_alpha()    #高级敌机撞到爆炸第3张图片
        self.enemy3_down4_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy3_down4.png").convert_alpha()    #高级敌机撞到爆炸第4张图片
        self.enemy3_down5_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy3_down5.png").convert_alpha()    #高级敌机撞到爆炸第5张图片
        self.enemy3_down6_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy3_down6.png").convert_alpha()    #高级敌机撞到爆炸第6张图片
        self.enemy3_hit_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy3_hit.png").convert_alpha()  #高级敌机被子弹打到后图片
        self.destroy_images.extend([self.enemy3_down1_image,self.enemy3_down1_image,self.enemy3_down3_image,self.enemy3_down4_image,\
                                                self.enemy3_down5_image,self.enemy3_down6_image ])
        
        
        self.width ,self.height = bg_size[0] , bg_size[1]   #获取生成低级敌机的活动范围
        self.rect = self.enemy3_n1.get_rect()   #获得低级敌机的尺寸
        self.rect.left , self.rect.top = [ randint(0 , self.width - self.rect.width ) ,\
                                                     randint(-15*self.height , 0) ]      #在窗口的5倍之上的距离中产生飞机
        
        self.mask = pygame.mask.from_surface(self.enemy3_n1)  #给类添加上mask属性,之后可以按照完美碰撞
        self.speed = 1  #设置大敌机移动的速度
        self.active = True#设置飞机是否生存的状态

    def move(self) :    #大敌机的移动函数
        if self.rect.top < self.height :    #如果大飞机的底超出窗口的下边框
            self.rect.top += self.speed  #大飞机向下移动他的速度
        else :  #如果大飞机已经超出窗口的下边界框
            self.reset() #重置大飞机

    def reset(self) :   #重置大飞机函数
        self.active = True#设置飞机是否生存的状态
        self.rect.left , self.rect.top = [ randint(0 , self.width - self.rect.width ) ,\
                                                     randint(-15*self.height , 0) ]      #在窗口的5倍之上的距离中产生飞机


def main() :
    pygame.init()   #初始化
    pygame.mixer.init()     #混音器初始化

    clock = pygame.time.Clock()     #设置一个计时器

    transform = False   #设置一个是否改变的变量
    delay = 100 #类似于单片机,设置一个延时变量,让飞机的改变看起来更加的顺滑
    switch_image = True     # 用于切换图片
    running = True
    """载入音乐文件"""
    #背景音乐
    pygame.mixer.music.load(r"D:\Code\Python\Pygame\pygame11:飞机大战\sound\game_music.ogg")  #设置背景音乐
    pygame.mixer.music.set_volume(1)  #设置音量

    #游戏音效
    bullet_sound = pygame.mixer.Sound( r"D:\Code\Python\Pygame\pygame11:飞机大战\sound\bullet.wav")  #子弹特效
    bullet_sound.set_volume(0.2)
    button_sound = pygame.mixer.Sound( r"D:\Code\Python\Pygame\pygame11:飞机大战\sound\button.wav")    #按键特效
    button_sound.set_volume(0.3)
    enemy1_down_sound = pygame.mixer.Sound( r"D:\Code\Python\Pygame\pygame11:飞机大战\sound\enemy1_down.wav")    #低等敌机被击毁音效
    enemy1_down_sound.set_volume(0.3)
    enemy2_down_sound = pygame.mixer.Sound( r"D:\Code\Python\Pygame\pygame11:飞机大战\sound\enemy2_down.wav")    #中等敌机被击毁音效
    enemy2_down_sound.set_volume(0.3)
    enemy3_down_sound = pygame.mixer.Sound( r"D:\Code\Python\Pygame\pygame11:飞机大战\sound\enemy3_down.wav")    #高等敌机被击毁音效
    enemy3_down_sound.set_volume(0.3)
    enemy3_fly_sound = pygame.mixer.Sound( r"D:\Code\Python\Pygame\pygame11:飞机大战\sound\enemy3_flying.wav")    #高等敌机出现音效
    enemy3_fly_sound.set_volume(0.3)
    get_bomb_sound = pygame.mixer.Sound(  r"D:\Code\Python\Pygame\pygame11:飞机大战\sound\get_bomb.wav")    #获得全屏炸弹补给音效
    get_bomb_sound.set_volume(0.4)
    get_bullet_sound = pygame.mixer.Sound(  r"D:\Code\Python\Pygame\pygame11:飞机大战\sound\get_bullet.wav")    #获得双倍子弹补给音效
    get_bullet_sound.set_volume(0.4)
    myself_down_sound = pygame.mixer.Sound(  r"D:\Code\Python\Pygame\pygame11:飞机大战\sound\me_down.wav")    #自身飞机被击毁(死亡)音效
    myself_down_sound.set_volume(0.3)
    supply_sound = pygame.mixer.Sound(  r"D:\Code\Python\Pygame\pygame11:飞机大战\sound\supply.wav")    #补给产生音效
    supply_sound.set_volume(0.4)
    upgrade_sound = pygame.mixer.Sound(  r"D:\Code\Python\Pygame\pygame11:飞机大战\sound\upgrade.wav")    #升级音效
    upgrade_sound.set_volume(0.3)
    use_bomb_sound = pygame.mixer.Sound(  r"D:\Code\Python\Pygame\pygame11:飞机大战\sound\use_bomb.wav")    #使用全屏炸弹音效
    use_bomb_sound.set_volume(0.4)

    """图片文件路径"""
    again_image = r"D:\Code\Python\Pygame\pygame11:飞机大战\images\again.png"   #重新开始图片路径
    background_image = r"D:\Code\Python\Pygame\pygame11:飞机大战\images\background.png"  #背景图的路径
    bomb_supply_image = r"D:\Code\Python\Pygame\pygame11:飞机大战\images\bomb_supply.png" #全屏炸弹补给图
    bomb_image = r"D:\Code\Python\Pygame\pygame11:飞机大战\images\bomb.png"   #全屏炸弹图
    bullet_supply_image = r"D:\Code\Python\Pygame\pygame11:飞机大战\images\bullet_supply.png" #补给子弹图
    bullet1_image = r"D:\Code\Python\Pygame\pygame11:飞机大战\images\bullet1.png" #第一种子弹图
    bullet2_image = r"D:\Code\Python\Pygame\pygame11:飞机大战\images\bullet2.png" #第二种子弹图
    
    enemy1_image = r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy1.png"  #低级敌机图片
    enemy2_image = r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy2.png"  #中级敌机图片
    enemy3_n1_image = r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy3_n1.png"   #高级敌机图片1
    enemy3_n2_image = r"D:\Code\Python\Pygame\pygame11:飞机大战\images\enemy3_n2.png"   #高级敌机图片2
    gameover_image = r"D:\Code\Python\Pygame\pygame11:飞机大战\images\gameover.png"    #游戏结束图片
    life_image = r"D:\Code\Python\Pygame\pygame11:飞机大战\images\life.png"    #表示生命值的图片
    
    me1_image = r"D:\Code\Python\Pygame\pygame11:飞机大战\images\me1.png" #自身飞机第一张图片
    me2_image = r"D:\Code\Python\Pygame\pygame11:飞机大战\images\me2.png" #自身飞机第二张图片
    pause_nor_image = r"D:\Code\Python\Pygame\pygame11:飞机大战\images\pause_nor.png"   #暂停的时候图片
    pause_pressed_image = r"D:\Code\Python\Pygame\pygame11:飞机大战\images\pause_pressed.png"   #暂停到继续的中间变换图
    resume_nor_image = r"D:\Code\Python\Pygame\pygame11:飞机大战\images\resume_nor.png"  #继续的时候的图片
    resume_pressed_image =r"D:\Code\Python\Pygame\pygame11:飞机大战\images\resume_pressed.png"   #继续到暂停的中间变换图

    #设置背景
    bg_size = width , height = 430 , 700       #背景大小
    screen = pygame.display.set_mode(bg_size)   #这是背景大小
    background = pygame.image.load(background_image).convert_alpha()       #画背景
    pygame.mixer.music.set_volume(1)    #设置音量
    pygame.mixer.music.play(-1)     #播放背景音乐,-1 表示无限循环播放

    #中弹图片索引
    e1_destroy_index = 0
    e2_destroy_index = 0
    e3_destroy_index = 0
    myself_destroy_index = 0

    #生成自己飞机
    myself = MyPlane(bg_size,me1_image,me2_image)    

    #生成敌机组
    def add_smallenemies(group1, group2, num):
        for i in range(num):
            e1 = SmallEnemy(bg_size,enemy1_image)
            group1.add(e1)
            group2.add(e1)

    def add_middleenemies(group1, group2, num):
        for i in range(num):
            e2 = MiddleEnemy(bg_size,enemy2_image)
            group1.add(e2)
            group2.add(e2)

    def add_bigenemies(group1, group2, num):
        for i in range(num):
            e3 = BigEnemy(bg_size,enemy3_n1_image,enemy3_n2_image)
            group1.add(e3)
            group2.add(e3)

    #生成敌机组
    enemies = pygame.sprite.Group() #生成整个飞机组
    smallenemies = pygame.sprite.Group()    #生成小飞机组
    add_smallenemies(smallenemies,enemies,20)     #生成小型飞机
    middleenemies = pygame.sprite.Group()   #生成中飞机组
    add_middleenemies(middleenemies,enemies,10)     #生成中型飞机
    bigenemies = pygame.sprite.Group() #生成大飞机组
    add_bigenemies(bigenemies,enemies,4)     #生成大型飞机


    while running :
        for event in pygame.event.get() :
            if event.type == QUIT :
                sys.exit()

        screen.blit(background,(0,0))   #绘制背景

        # 绘制大型敌机
        for each in bigenemies:
            if each.active: #如果大型敌机为生存状态
                each.move()
                if switch_image:    #判断是否交换图片
                    screen.blit(each.enemy3_n1, each.rect)
                else:
                    screen.blit(each.enemy3_n2, each.rect)
                # 即将出现在画面中,播放音效
                if each.rect.bottom == -50:
                    enemy3_fly_sound.play()
            else:   
                #如果大型敌机为撞毁状态
                if not (delay % 3) :
                    if e3_destroy_index == 0:   #当索引再次变为0时
                        enemy3_down_sound.play()    #播放声音
                    screen.blit(each.destroy_images[e3_destroy_index], each.rect)
                    e3_destroy_index = (e3_destroy_index + 1) % 6
                    if e3_destroy_index == 0:   #当索引再次变为0时
                        enemy3_down_sound.stop()
                        each.reset()    #重置飞机
        # 绘制中型敌机:
        for each in middleenemies:
            if each.active :    #如果此敌机的为存活状态
                each.move()
                screen.blit(each.enemy2, each.rect)
            else :
                #敌机撞毁
                if not (delay % 3) :
                    if e2_destroy_index == 0:   #当索引再次变为0时
                        enemy2_down_sound.play()    #播放声音
                    screen.blit(each.destroy_images[e2_destroy_index], each.rect)
                    e2_destroy_index = (e2_destroy_index + 1) % 4
                    if e2_destroy_index == 0:   #当索引再次变为0时
                        each.reset()    #重置飞机
        # 绘制小型敌机:
        for each in smallenemies:
            if each.active :    #如果此敌机的为存活状态
                each.move()
                screen.blit(each.enemy1, each.rect)
            else :
                #敌机撞毁
                if not (delay % 3) :
                    if e1_destroy_index == 0:   #当索引再次变为0时
                        enemy1_down_sound.play()    #播放声音
                    screen.blit(each.destroy_images[e1_destroy_index], each.rect)
                    e1_destroy_index = (e1_destroy_index + 1) % 4
                    if e1_destroy_index == 0:   #当索引再次变为0时
                        each.reset()    #重置飞机

        #绘制我方飞机
        if myself.active :  #如果我方飞机active的属性为真,即未和其他飞机发生碰撞
            if transform :  #如果改变属性为真  画第一张飞机图
                screen.blit(myself.image1,myself.rect)  #绘制第一张飞机图         
            else :  #如果改变属性为假  画第二张飞机图
                screen.blit(myself.image2,myself.rect)  #绘制第二张飞机图
        else:
            #我方飞机撞毁
            myself_down_sound.play()
            if not (delay % 3) :
                screen.blit(myself.destroy_images[myself_destroy_index], myself.rect)
                myself_destroy_index = (myself_destroy_index + 1) % 4
                if myself_destroy_index == 0:
                    print("Game Over!")
                    running = False

        if not (delay % 5) :
            transform = not transform    #改变transform的属性来打到改变图片的效果
            switch_image = not switch_image #改变switch_image 来改变大型飞机的图片

        if not delay :  #如果delay值变为0那么重新调回100
            delay = 100
        delay -= 1

        #检查双方飞机是否发生碰撞
        enemies_down = pygame.sprite.spritecollide(myself, enemies, False,collided=pygame.sprite.collide_mask)  #将所有发生碰撞的敌方飞机都加入到组中
        if enemies_down:       #如果组中有成员,说明又发生碰撞
            #myself.active = False   #我放飞机存活状态变成False
            for e in enemies_down:  
                e.active = False    #对于与我相撞的飞机的也设为False


        #检查是否有上下左右或wsad按下,如有则对飞机进行相应的控制
        key_press = pygame.key.get_pressed()
        if key_press[K_w] or key_press[K_UP] :    #如果按下上键或者↑
            myself.moveup()   #飞船向上飞行
        elif key_press[K_s] or key_press[K_DOWN] :    #如果按下下键或者↓
            myself.movedown()   #飞船向下飞行
        elif key_press[K_a] or key_press[K_LEFT] :    #如果按下左键或者←
            myself.moveleft()   #飞船向左飞行
        elif key_press[K_d] or key_press[K_RIGHT] :    #如果按下右键或者→
            myself.moveright()   #飞船向右飞行





        pygame.display.flip()   #不停的刷新画面,不停的绘画

        clock.tick(60)     #设置帧率


if __name__ == "__main__":
    # 这样做的好处是双击打开时如果出现异常可以报告异常,而不是一闪而过!
    try:
        main()
    except SystemExit: #这是按下 × 的异常,直接忽略
        pass
    except:
        traceback.print_exc()
        pygame.quit()
        input()

(to be continued……)

你可能感兴趣的:(第092讲: Pygame:飞机大战3 | 学习记录(小甲鱼零基础入门学习Python))