Pygame:飞机大战7(详细解读)


🚴大家好!我是近视的脚踏实地,虽然近视,但是脚踏实地。这一篇继续要完善飞机大战的游戏,这篇主要完成的内容是给添加给玩家三条生命值的功能,以及游戏结束画面的处理

(一)完成给玩家三次游戏机会的功能

在这里插入图片描述
很多游戏都会给玩家提供多次尝试的机会,因此我们也添加这么一个功能,玩家总共会有三次机会,在游戏界面的右下角通过显示多少个小飞机来提示玩家还剩下多少次机会,那这张图片其实就是玩家飞机的缩小版

1️⃣在屏幕右下角绘制玩家生命数

myplane.py 👇

#myplane.py
import pygame

class MyPlane(pygame.sprite.Sprite):
     def __init__(self,bg_size):
          pygame.sprite.Sprite.__init__(self)

          self.image1 = pygame.image.load("images/me1.png").convert_alpha()
          self.image2 = pygame.image.load("images/me2.png").convert_alpha()
          self.destroy_images = []
          self.destroy_images.extend([\
            pygame.image.load("images/me_destroy_1.png").convert_alpha(), \
            pygame.image.load("images/me_destroy_2.png").convert_alpha(), \
            pygame.image.load("images/me_destroy_3.png").convert_alpha(), \
            pygame.image.load("images/me_destroy_4.png").convert_alpha() \
            ])
          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
          self.active = True
          self.invincible = False
          self.mask = pygame.mask.from_surface(self.image1)

     #向上走
     def moveUp(self):
          if self.rect.top > 0:
               self.rect.top -= self.speed
          else:
               self.rect.top = 0
               
     #向下走
     def moveDown(self):
          if self.rect.bottom < self.height - 60:
               self.rect.top += self.speed
          else:
               self.rect.bottom = self.height - 60
               
     #向左走
     def moveLeft(self):
          if self.rect.left > 0:
               self.rect.left -= self.speed
          else:
               self.rect.left = 0

     #向右走
     def moveRight(self):
          if self.rect.right < self.width:
               self.rect.right += self.speed
          else:
               self.rect.right = self.width
               
     # 玩家复活
     def reset(self):
          self.rect.left, self.rect.top = \
                        (self.width - self.rect.width) // 2, \
                        self.height - self.rect.height - 60
          self.active = True
          self.invincible = True

在这里插入图片描述
那么想要玩家的飞机可以reset,要可以复活,所以我们在myplane.py中来添加一个reset方法, 那么放里边就首先是将飞机仍然初始化在页面底部的中央,然后self.active = True 将玩家的active属性设置为True,我们是通过active属性来判断他是是否还活着,self.invincible = True 这个属性就标致玩家的飞机是否无敌,具体用到后边再来

在main.py中进行修改 👇
在这里插入图片描述

那首先之前我们的逻辑就是,先检测玩家的飞机是否被撞,如果被撞,就把active属性设置为False,接着下边检测到玩家的对象的active属性为False时,就来绘制他毁灭的画面,绘制完后就打印了Game Over,那么现在我们想让玩家有三次尝试的机会,所以我们要在main模块中加多一个lief_num的变量来表示还有多少次机会,那么首先也是先把小飞机的图片加载进去,然后获取它的矩形对象,设置他的生命数量为3
在这里插入图片描述
接着就是如果检测到玩家的active属性为False,绘制毁灭画面后就life_num -= 1 ,生命数量减1,接着me.reset() 再来重生

那么接下来就是在屏幕右下角绘制生命数量的图片,在绘制左下角的全屏炸弹后绘制就好了,for i in range(life_num): 那么就是来迭代life_num这个变量,如果还有剩的话就screen.blit(life_image, (width-10-(i+1)*life_rect.width, height-10-life_rect.height)) 在右下角绘制出来,参数就是要来计算偏移了 x方向就是屏幕的宽度width -10 再减去剩下多少条命乘以一张小飞机的宽度,y方向就是屏幕的高度减10再减小飞机矩形对象的height

在这里插入图片描述
因为游戏的主流层,在我们没有限制生命的时候,我们是按下恢复了他才执行的,按下暂停的时候这个主流层里边的循环就不执行,所以这里需要多加一个条件,就是说只有当玩家还有剩余声明的时候且游戏不暂停的时候,才执行这个主流层,这个主循环的内容

在这里插入图片描述
那么如果玩家没有剩余声明了的话,就来绘制结束画面,elif life_num == 0: 这里不能只是else,因为你else的话玩家按下暂停也会直接跑到游戏结束的界面,如果玩家剩余生命为0了,我们现在先打印,一步一步来,我们后边再来绘制出来,那么先来运行测试一下

测试结果如下: 👇
在这里插入图片描述

那么其实这里有个小细节,就当玩家打到后边很多飞机的时候就会发现,每次我方飞机牺牲后,如果说你你诞生的这个位置,就是底部中央的位置,恰好有一辆敌机,那么会导致玩家飞机一诞生就牺牲的惨状发生,因此我们要设定每次牺牲会有3秒钟的安全期,也就是无敌状态

2️⃣完成玩家复活后三秒真男人状态

myplane.py

在这里插入图片描述
在这里插入图片描述

那么就是首先在myplane模块多加一个self.invincible = False,然后在检测碰撞的时候,检测到这个属性是True还是False就可以了,那么之前我们已经在myplane模块里添加好了,然后在reset方法中也添加好了,重生时,就把它的属性值设置为True,表示开启无敌状态

main.py
在这里插入图片描述
那么无敌的时间只会保持3秒,太久的话对敌机不公平,所以这里多加一个计时器INVINCIBLE_TIME = USEREVENT + 2 这里表示我们的第三个自定义事件
在这里插入图片描述
在这里插入图片描述
然后当玩家飞机发生重生的时候,那我们就来调用这个计时器,然后在检测让他们是否相撞的时候,就not me.invincible: 不是无敌状态下才把active属性设置为False。也就说当me.invincible: 玩家飞机的这个属性为True的时候,那么not True就是Fasle,那么这个条件就不会成立,敌方就撞不到我们,就不会产生后面的事件。

在这里插入图片描述

那么接着3秒钟到后,我们就来响应INVINCIBLE_TIME 这个事件来解除无敌状态,使得me.invincible = False ,然后pygame.time.set_timer(INVINCIBLE_TIME, 0) 取消这个计时器就可以了,下面来测试一下

测试结果如下: 👇
在这里插入图片描述

(二)绘制游戏结束画面

在这里插入图片描述在这里插入图片描述
也就是说当life_num的值为0的时候,说明玩家已经输掉了游戏,但是玩家没有输掉人生,所以这个游戏结束的画面就要求上门最历史最高的分数,然后历史最高分是存在record.txt文档里边,然后中间显示本次游戏的分数,如果这个分数比历史最高分还高就要进行一个存档,下面有重新开始和结束游戏的按钮可以进行选择。

在这里插入图片描述
结束的时候我们应该做一些收尾的工作,比如说,背景音乐要去掉,音效也应该去掉,如果没有去掉,大飞机出来就会噜噜噜的响,永远不会停,所以应该把音效给去掉,然后把补给的定时器也应该给取消,停止,不然隔30秒还能听到补给的音效

在这里插入图片描述
那么首先因为整个一个while循环就是一帧,如果当游戏life_num等于0的时候,事实上就是不断地,像刚刚第一个测试截图,会看到很多个GAME OVER,那就是不断在刷除了主流层里被暂定的代码的以外的代码,那么不断地刷,整个文件就会不会不断地打开,不断关闭,那就非常消耗资源,也很有可能出错,所以这里加多了变量来限制他来限制打开一次就可以了
在这里插入图片描述

那么就是recorded 刚开始的默认值是Fasle,那么就是if not recorded: ,判断如果没有打开,就执行里面的内容,进到后就把recorded = True,设置为True,那以后他都不会进来了,就只执行一次,接着读取里面的最高分啊,然后玩家的分数比最高分还高就存档啊,这里就是文件读取的一些基本内容。然后这里要注意的就是这里并没有进行这个文件存不存在的判断,最省事的办法就是自己去这些源文件所在的当前文件夹创建出一个,比较专业的就是先来判断,是否有这个文件,没有就先创建一个,然后给出分数是0,文章最后我会给出自己改进的方法

在这里插入图片描述
接着就是来绘制结束的界面,这里就是加载图片,获取他们的矩形对象等,设置字体,这些都已经熟悉得不能再熟悉了,就不赘述了
在这里插入图片描述
接着就是来把结束画面个画出来,就是一些打印字体啊,先渲染成surface对象,然后调整好贴上去的位置好了,都是之前的知识了,就不一一啰嗦了

在这里插入图片描述
然后就是要响应玩家点下的按钮对应的操作了,也很容易理解,认真看一遍就好了
在这里插入图片描述

最后我们需要把绘制得分的部分放到主流层中,否则结束画面也会把左上角的得分显示出来的

1️⃣附上自己修改第一次游戏结束没有record.text文件报的文件不存在异常的解决方案

那么如果你的当前文件夹里没有record.text 这个文件,游戏结束后就会闪退,报错的,所以还不够完善,将来给别人用户,别人可没有意识要先去创建个文本来保存最高分。下面给出自己想的办法,路过的大佬有别的办法也可以指点指点,必须很多事情都有各种不同的解决方案
在这里插入图片描述
那我就是先去上面定义了一个record_score 变量,然后他的默认值就给0

if not recorded:
                recorded = True
                # 读取历史最高得分
                try:
                    with open("record.txt", "r") as f:
                        record_score = int(f.read())
                except FileNotFoundError:
                    with open("record.txt", "w") as f:
                        f.write(str(record_score))


                # 如果玩家得分高于历史最高得分,则存档
                if score > record_score:
                    with open("record.txt", "w") as f:
                        f.write(str(score))

在这里插入图片描述

接着我就是用了处理异常的方法,如果第一次打开,不存在record.text 这个文件,就跳到处理异常的地方,以**"w"的形式open**这个文件,那么如果没有,他就会在源代码默认的文件夹里自动帮你创建出这个文件夹,然后 f.write(str(record_score)) 往里面写入刚刚定义好的默认历史最高分0,因为写入要以字符串的形式写入,所以这里要强转一下,那么创建好之后,以后就存在这个文件了,用户分数大于历史分数时就会写入覆盖越来的分数,直接读就好了,就不会再有文件不存在的异常了

测试结果如下: 👇
在这里插入图片描述
在这里插入图片描述

2️⃣附上这个飞机大战项目的所有源码,加油!

1️⃣子弹模块----bullet.py

bullet.py

# bullet.py
import pygame

#普通子弹
class Bullet1(pygame.sprite.Sprite):
     def __init__(self,position):
          pygame.sprite.Sprite.__init__(self)
          self.image = pygame.image.load("images/bullet1.png").convert_alpha()
          self.rect = self.image.get_rect()
          self.rect.left,self.rect.top = position
          self.speed = 12
          self.active = False
          self.mask = pygame.mask.from_surface(self.image)


     def move(self):
          self.rect.top -= self.speed

          if self.rect.top < 0:
               self.active = False

     def reset(self,position):
          self.rect.left,self.rect.top = position
          self.active = True
          

#超级子弹
class Bullet2(pygame.sprite.Sprite):
     def __init__(self,position):
          pygame.sprite.Sprite.__init__(self)
          self.image = pygame.image.load("images/bullet2.png").convert_alpha()
          self.rect = self.image.get_rect()
          self.rect.left,self.rect.top = position
          self.speed = 14
          self.active = False
          self.mask = pygame.mask.from_surface(self.image)


     def move(self):
          self.rect.top -= self.speed

          if self.rect.top < 0:
               self.active = False

     def reset(self,position):
          self.rect.left,self.rect.top = position
          self.active = True

2️⃣敌机模块----enemy.py

enemy.py

#enemy.py
import pygame
from random import *

#小型敌机
class SmallEnemy(pygame.sprite.Sprite):
     def __init__(self,bg_size):
          pygame.sprite.Sprite.__init__(self)

          self.image = pygame.image.load("images/enemy1.png").convert_alpha()
          self.destroy_images = []
          self.destroy_images.extend([\
            pygame.image.load("images/enemy1_down1.png").convert_alpha(), \
            pygame.image.load("images/enemy1_down2.png").convert_alpha(), \
            pygame.image.load("images/enemy1_down3.png").convert_alpha(), \
            pygame.image.load("images/enemy1_down4.png").convert_alpha() \
            ])
          self.rect = self.image.get_rect()
          self.width,self.height = bg_size[0],bg_size[1]
          self.speed = 2
          self.active = True
          self.rect.left,self.rect.top = \
                         randint(0, self.width - self.rect.width),\
                         randint(-5 * self.height, 0)
          self.mask = pygame
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值