大家好!我是近视的脚踏实地,虽然近视,但是脚踏实地。这一篇继续要完善飞机大战的游戏,这篇主要完成的内容是完成玩家得分的显示以及暂停功能的实现以及随着等级提升,增加游戏的难度,还有一个就是完成全屏炸弹的功能
那么这一篇接下来就要在游戏的左上角设置一个得分显示区域,实时显示玩家的得分,那么我们设点的规则就是击中小、中、大型敌机分别可以获得1000分,6000分,以及10000分
main.py
那么这个很容易实现,直接在main函数里边添加一个叫做score 的变量来统计玩家的得分,如上4张图
接下来就来指定显示分数的字体,那么这里就是调用了pygame的font模块的Font类,然后实例化就好了,第一参数传入一个路径,第二参数就指定他的尺寸,大小就可以了
那么就下来在绘制完玩家飞机后把分数给画上去,那就是 score_text = score_font.render(“Score : %s” % str(score), True, WHITE) ,这里调用render函数,render函数就是将一个字体,字符串渲染成一个Surface对象,之前说了pygame事实上就是操纵各种各样的Surface对象在贴来贴去,Surface对象可以认为是图片在pygame里边的描述,这里的True就表示开启抗锯齿,然后就是画白色的字体
接着screen.blit(score_text, (10, 5)) 给这字体图片刷一帧就显示上去,显示到10,5这个位置就OK了
测试结果:
首先可以看到有4中样式,前面两个表示要暂停,后边两个表示要开始,就是恢复游戏,那么一个颜色和一个颜色浅的区别就是当玩家的鼠标悬浮在这个按钮的上方的时候,并没做任何的操作,这个按钮的颜色就会加深,这样的效果就相当于是在暗示玩家,你这个鼠标是有效的,你点一下,他就会触发相应的事件,一些功能。
那么我们可以通过响应 MOUSEBUTTONDOWN 的事件可以知道用户是否点下鼠标,在点下鼠标那一刹那,我们来获得鼠标的位置,就可以知道玩家是否点下了暂停按钮。从而使得游戏得到暂停。
然后通过响应 MOUSEMOTION 事件,我们可以改变按钮的样式。就是当我们mousemotion,鼠标的位置刚好在这个按钮上方的话,就会对应变成颜色比较深的图片按钮,下面来改写代码
main.py
那么同样也是现在main函数中添加一个pause变量,来表示暂停的标志,接着把几个待会需要用的图片依次用load()方法给加载进来,然后由于四个图片他们的尺寸都是一样,所以我们只要获得其中一个尺寸的这个矩形对象的位置就可以了。
接着paused_rect.left, paused_rect.top = width - paused_rect.width - 10, 10 ,这里就是来修改暂停按钮矩形对象的位置,让他在右上角,那么x轴的就是游戏窗口的宽度减去这个暂停按钮矩形对象的宽度再减去10,右边留一点空间,然后y轴就和前面的得分一样高,那么这里在啰嗦下,其实前面也已经讲了,这个矩形对象的left,和top,组成的其实就是这个矩形对象的左上角的顶点,我们只需要给这两个顶点赋好值,其他的点pygame会自动算好的
接着paused_image = pause_nor_image 设置默认显示的图标,为上面图片的第一张,然后当鼠标悬浮在上面的时候,就显示上面的第二张图片,按下的时候再显示第四张,当鼠标不悬浮在上面时就显示第三张图片
那么现在就是来响应MOUSEBUTTONDOWN的事件,那么就是elif event.type == MOUSEBUTTONDOWN: ,然后if event.button == 1 就表示如果按下鼠标按钮的左键时,事实上这里还需要去检测鼠标的pos,位置,这个位置是否位于矩形之内,然后我们的这个rect矩形对象有一个叫collidepoint的方法,他就会检测一个点的坐标,这个方法就是检测一个点的坐标是否在他这个矩形之内,所以and paused_rect.collidepoint(event.pos): 直接调用这个方法就好了, 参数就传入一个鼠标的实时位置,他就会自动检测这个pos是否在这个矩形之内,如果是的话,返回True,否的话就返回False
接着paused = not paused 就是直接取反,那么就是说,如果现在是暂停,那么点一下就变成了,重新开始,再点一下又变成暂停
接着就是来修改暂停按钮的图标样式了,那么就是elif event.type == MOUSEMOTION: MOUSEMOTION就是当玩家的鼠标在这个界面上有任何的移动的时候,他就会出现这消息,那么就来获得他的event.pos,然后检测他是否在这个范围之内,如果是就视情况来咯
接着if paused: 如果是暂停的情况下,就paused_image = resume_pressed_image ,否则就是方暂停的深颜色的图片
接着就是来显示他们浅色时的方案,拷贝一下,把pressed改为nor就可以了,
然后我们将游戏的主流层,只有在paused为False的情况下就是不暂停才得以执行,我们还需要将绘制背景把它拉到外边去,主流层的代码就是从检测用户的键盘操作开始到绘制得分的上部分都是,全部选中加一个tab键,缩进完,加个判断 if not paused: 就是paused变量为False的时候才会继续游戏,为True 的时候就暂停游戏
接着 因为上面我们已经把所有的状态都放在了pause_image中了,所以我们直接把它绘制就可以了
接着运行测试一下:
可以看到这里有个问题就是玩家本来要挂了,马上点暂停,然后找好个位置,一点开始就往安全地方跑,所以我们在暂停的时候应该只显示背景,不能看到玩家飞机或者敌机,所以在暂停的时候,我们应该将背景图片放到外边去如下:把它剪切出来
那这样子,背景图片在外面先画,那么暂停的时候,这个循环是不会进去的,这样子暂停的话,就会被背景图片盖住,这样子玩家就要看不到战局,就不能通过恢复暂停来达到作弊的效果
因为敌机一成不变地慢悠悠地出现,对于玩家来说,是无法忍受的,玩家希望得到的游戏体是刺激,是心跳,所以我们要让游戏的难度随着玩家的得分增加而增加,我们这里将游戏难度划分为 5 个级别,那么每提升一个级别就会增加一些敌机,或者说会提升一些敌机的移动速度,比如说中型敌机和小型敌机就应该像雨一样地落下来,下面开始设置
同样的,我们先在main模块里多加一个level 属性来表示难度级别,默认先是1级,那当他执行的时候,我们可以让他根据玩家的得分实时地增加游戏的难度
接着if level == 1 and score > 5000: 如果玩家分数大于5000,level = 2 就把等级调为2,接着upgrade_sound.play() 就是表示提升难度时播放的音效,之前已经都加载进来了。
接着就是相应地增加敌机,就直接调用add_small…、add_mid…、add_big…然后参数就是给他们自己的group添加,同时也给enemies列表添加,这个列表是存着所有类型的敌机,检测碰撞就是从这个列表拿出来检测的,第三个参数就是增加的数量,接着inc_speed(small_enemies, 1) 来提升小型敌机的速度,事实上就是取修改他这个类的speed属性就 。
那么因为我们后边还要持续提升速度,所以我们这里把它定义为一个方法,首先就是第一个参数,你要往哪里添加列表,第二个就是添加的速度大小,里边用个循环来给里边的飞机逐个提升速度,也就是修改他们的speed属性,然后下面就是调用就好了
接着后面的级别copy下来,依次来修改分数,和添加的飞机数量和速度就可以了
初始化的时候我们有15个小型敌机,如果到1000000分,那么就有33个小型敌机了,然后中型敌机和大型的敌机也依次地增加,就越来越多,那么它的密集度就很高,就像下飞机雨一样,敌机的速度也越来越快,那难度当然就是相应增加
那么其实到5这个级别的时候,你就会看到下飞机雨的情况了,这时候玩家就很容易陷入不利的局面,因此我们会提供一个大杀招,全屏炸弹,此招要出,界面上的所有的敌机将会在一瞬间灰飞烟灭,那么这个大杀招我们就通过空格键来触发的,初始化的时候先让玩家带着3颗全屏炸弹,我们也可以通过每过30秒的补给来获得,但是玩家的飞机最多可以装载3颗,然后由于触发全屏炸弹是偶然事件,因此我们通过响应KEYDOWN事件,再检测event.KEY是否为space就可以了
那么首先就是定义一下这个全屏炸弹的样子,就是把图片给加载进来,同样的,然后获取这张图片的rect矩形对象,接着bomb_font = pygame.font.Font(“font/font.ttf”, 48) 字体仍然用前面显示分数的那个字体,48号字体,然后bomb_num = 3 刚开始初始化给3个全屏炸弹,当我们获得补给的时候,这个bomb就检测是否小于3,如果是的话那么就加等于1
接着elif event.type == KEYDOWN: 先看看是不是按键按下事件,是的话,再来if event.key == K_SPACE: 再来检测是不是按下了空格键,是的话if bomb_num: 就检测是否有库存,有的话就bomb_num -= 1 先把炸弹数量减1,然后bomb_sound.play() 播放炸弹爆炸的声音。
接着for each in enemies: ,在所有敌机中检测,如果if each.rect.bottom > 0: 如果敌机bottom是大于0的,大于0就表示他们已经处于屏幕上了,那么each.active = False 他们都挂了,active属性设置为False。
接着来绘制全屏炸弹剩余的数量,绘制在界面的左下角的位置,那么也绘制在主流层里边把,暂停的时候我们也不需要知道有还有多少炸弹
那么bomb_text = bomb_font.render(“× %d” % bomb_num, True, WHITE) 用render渲染成一个surface对象,才能画到屏幕上,显示样式就是炸弹图片X 炸弹数量,然后渲染成surface对象,就可以text_rect = bomb_text.get_rect() 来获取它的矩形对象了。
接着screen.blit(bomb_image, (10, height - 10 - bomb_rect.height)) 先画炸弹的图片,10就是x轴向右10个像素,y轴是整个屏幕的高度减去炸弹矩形的高度,再减去10像素,让他和下面有些空隙。 接着 screen.blit(bomb_text, (20 + bomb_rect.width, height - 5 - text_rect.height)) 来画这个文本,就是剩余多少炸弹,然后这些位置可以去调整吧,自己看得舒服就可以.
效果如下:
import pygame
import sys
import traceback
import myplane
import enemy
import bullet
from pygame.locals import *
pygame.init()
pygame.mixer.init()
bg_size = width,height = 480,700
screen = pygame.display.set_mode(bg_size)
pygame.display.set_caption("飞机大战 -- Monster ZF")
background = pygame.image.load("images/background.png").convert()
BLACK = (0,0,0)
GREEN = (0,255,0)
RED = (255,0,0,0)
WHITE = (255,255,255)
# 载入游戏音乐
pygame.mixer.music.load("sound/game_music.ogg")
pygame.mixer.music.set_volume(0.2)
bullet_sound = pygame.mixer.Sound("sound/bullet.wav")
bullet_sound.set_volume(0.2)
bomb_sound = pygame.mixer.Sound("sound/use_bomb.wav")
bomb_sound.set_volume(0.2)
supply_sound = pygame.mixer.Sound("sound/supply.wav")
supply_sound.set_volume(0.2)
get_bomb_sound = pygame.mixer.Sound("sound/get_bomb.wav")
get_bomb_sound.set_volume(0.2)
get_bullet_sound = pygame.mixer.Sound("sound/get_bullet.wav")
get_bullet_sound.set_volume(0.2)
upgrade_sound = pygame.mixer.Sound("sound/upgrade.wav")
upgrade_sound.set_volume(0.2)
enemy3_fly_sound = pygame.mixer.Sound("sound/enemy3_flying.wav")
enemy3_fly_sound.set_volume(0.2)
enemy1_down_sound = pygame.mixer.Sound("sound/enemy1_down.wav")
enemy1_down_sound.set_volume(0.1)
enemy2_down_sound = pygame.mixer.Sound("sound/enemy2_down.wav")
enemy2_down_sound.set_volume(0.2)
enemy3_down_sound = pygame.mixer.Sound("sound/enemy3_down.wav")
enemy3_down_sound.set_volume(0.5)
me_down_sound = pygame.mixer.Sound("sound/me_down.wav")
me_down_sound.set_volume(0.2)
def add_small_enemies(group1,group2,num):
for i in range(num):
e1 = enemy.SmallEnemy(bg_size)
group1.add(e1)
group2.add(e1)
def add_mid_enemies(group1,group2,num):
for i in range(num):
e2 = enemy.MidEnemy(bg_size)
group1.add(e2)
group2.add(e2)
def add_big_enemies(group1,group2,num):
for i in range(num):
e3 = enemy.BigEnemy(bg_size)
group1.add(e3)
group2.add(e3)
def inc_speed(target, inc):
for each in target:
each.speed += inc
def main():
pygame.mixer.music.play(-1)
#生成玩家飞机
me = myplane.MyPlane(bg_size)
#生成所有敌机汇总
enemies = pygame.sprite.Group()
#生成小型敌机
small_enemies = pygame.sprite.Group()
add_small_enemies(small_enemies,enemies,15)
#生成中型敌机
mid_enemies = pygame.sprite.Group()
add_mid_enemies(mid_enemies,enemies,4)
#生成大型敌机
big_enemies = pygame.sprite.Group()
add_big_enemies(big_enemies,enemies,2)
#生成普通子弹
bullet1 = []
bullet1_index = 0
BULLET1_NUM = 4
for i in range(BULLET1_NUM):
bullet1.append(bullet.Bullet1(me.rect.midtop))
clock = pygame.time.Clock()
# 中弹图片索引
e1_destroy_index = 0
e2_destroy_index = 0
e3_destroy_index = 0
me_destroy_index = 0
# 统计得分
score = 0
#字体的定义
score_font = pygame.font.Font("font/font.ttf",36)
#标志是否暂停游戏
paused = False
pause_nor_image = pygame.image.load("images/pause_nor.png").convert_alpha()
pause_pressed_image = pygame.image.load("images/pause_pressed.png").convert_alpha()
resume_nor_image = pygame.image.load("images/resume_nor.png").convert_alpha()
resume_pressed_image = pygame.image.load("images/resume_pressed.png").convert_alpha()
paused_rect = pause_nor_image.get_rect()
paused_rect.left, paused_rect.top = width - paused_rect.width - 10, 10
paused_image = pause_nor_image
#设置难度级别
level = 1
# 全屏炸弹
bomb_image = pygame.image.load("images/bomb.png").convert_alpha()
bomb_rect = bomb_image.get_rect()
bomb_font = pygame.font.Font("font/font.ttf", 48)
bomb_num = 3
#用于切换图片
switch_image = True
#用于延迟
delay = 100
running = True
while running:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == MOUSEBUTTONDOWN:
if event.button == 1 and paused_rect.collidepoint(event.pos):
paused = not paused
elif event.type == MOUSEMOTION:
if paused_rect.collidepoint(event.pos):
if paused:
paused_image = resume_pressed_image
else:
paused_image = pause_pressed_image
else:
if paused:
paused_image = resume_nor_image
else:
paused_image = pause_nor_image
elif event.type == KEYDOWN:
if event.key == K_SPACE:
if bomb_num:
bomb_num -= 1
bomb_sound.play()
for each in enemies:
if each.rect.bottom > 0:
each.active = False
# 根据用户的得分增加游戏难度
if level == 1 and score > 50000:
level = 2
upgrade_sound.play()
# 增加3架小型敌机,2架中型敌机和1架大型敌机
add_small_enemies(small_enemies,enemies,3)
add_mid_enemies(mid_enemies, enemies, 2)
add_big_enemies(big_enemies, enemies, 1)
# 提升小型敌机的速度
inc_speed(small_enemies, 1)
elif level == 2 and score > 300000:
level = 3
upgrade_sound.play()
# 增加5架小型敌机、3架中型敌机和2架大型敌机
add_small_enemies(small_enemies, enemies, 5)
add_mid_enemies(mid_enemies, enemies, 3)
add_big_enemies(big_enemies, enemies, 2)
# 提升小型敌机的速度
inc_speed(small_enemies, 1)
inc_speed(mid_enemies, 1)
elif level == 3 and score > 600000:
level = 4
upgrade_sound.play()
# 增加5架小型敌机、3架中型敌机和2架大型敌机
add_small_enemies(small_enemies, enemies, 5)
add_mid_enemies(mid_enemies, enemies, 3)
add_big_enemies(big_enemies, enemies, 2)
# 提升小型敌机的速度
inc_speed(small_enemies, 1)
inc_speed(mid_enemies, 1)
elif level == 4 and score > 1000000:
level = 5
upgrade_sound.play()
# 增加5架小型敌机、3架中型敌机和2架大型敌机
add_small_enemies(small_enemies, enemies, 5)
add_mid_enemies(mid_enemies, enemies, 3)
add_big_enemies(big_enemies, enemies, 2)
# 提升小型敌机的速度
inc_speed(small_enemies, 1)
inc_speed(mid_enemies, 1)
screen.blit(background,(0,0))
if not paused:
#检测用户的键盘操作
key_pressed = pygame.key.get_pressed()
if key_pressed[K_w] or key_pressed[K_UP]:
me.moveUp()
if key_pressed[K_s] or key_pressed[K_DOWN]:
me.moveDown()
if key_pressed[K_a] or key_pressed[K_LEFT]:
me.moveLeft()
if key_pressed[K_d] or key_pressed[K_RIGHT]:
me.moveRight()
# 发射子弹
if not(delay % 10):
bullet1[bullet1_index].reset(me.rect.midtop)
bullet1_index =(bullet1_index + 1) % BULLET1_NUM
# 检测子弹是否击中敌机
for b in bullet1:
if b.active:
b.move()
screen.blit(b.image,b.rect)
enemy_hit = pygame.sprite.spritecollide(b,enemies,False,pygame.sprite.collide_mask)
if enemy_hit:
b.active = False
for e in enemy_hit:
if e in mid_enemies or e in big_enemies:
e.hit = True
e.energy -= 1
if e.energy == 0:
e.active = False
else:
e.active = False
# 绘制大型敌机
for each in big_enemies:
if each.active:
each.move()
if each.hit:
#绘制被打到的特效
screen.blit(each.image_hit,each.rect)
each.hit = False
else:
if switch_image:
screen.blit(each.image1,each.rect)
else:
screen.blit(each.image2,each.rect)
# 绘制血槽
pygame.draw.line(screen, BLACK, \
(each.rect.left, each.rect.top - 5), \
(each.rect.right, each.rect.top - 5), \
2)
# 当生命大于20%显示绿色,否则显示红色
energy_remain = each.energy / enemy.BigEnemy.energy
if energy_remain > 0.2:
energy_color = GREEN
else:
energy_color = RED
pygame.draw.line(screen, energy_color, \
(each.rect.left, each.rect.top - 5), \
(each.rect.left + int(each.rect.width * energy_remain), \
each.rect.top - 5), 2)
#即将出现在画面中,播放音效
if each.rect.bottom == -50:
enemy3_fly_sound.play(-1)
else:
# 毁灭
if not(delay % 3):
if e3_destroy_index == 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:
enemy3_fly_sound.stop()
score += 10000
each.reset()
# 绘制中型敌机:
for each in mid_enemies:
if each.active:
each.move()
if each.hit:
screen.blit(each.image_hit, each.rect)
each.hit = False
else:
screen.blit(each.image,each.rect)
# 绘制血槽
pygame.draw.line(screen, BLACK, \
(each.rect.left, each.rect.top - 5), \
(each.rect.right, each.rect.top - 5), \
2)
# 当生命大于20%显示绿色,否则显示红色
energy_remain = each.energy / enemy.MidEnemy.energy
if energy_remain > 0.2:
energy_color = GREEN
else:
energy_color = RED
pygame.draw.line(screen, energy_color, \
(each.rect.left, each.rect.top - 5), \
(each.rect.left + int(each.rect.width * energy_remain), \
each.rect.top - 5), 2)
else:
# 毁灭
if not(delay % 3):
if e2_destroy_index == 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:
score += 6000
each.reset()
# 绘制小型敌机:
for each in small_enemies:
if each.active:
each.move()
screen.blit(each.image,each.rect)
else:
# 毁灭
if not(delay % 3):
if e1_destroy_index == 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:
score += 1000
each.reset()
#检测玩家飞机是否被撞
enemies_down = pygame.sprite.spritecollide(me,enemies,False,pygame.sprite.collide_mask)
if enemies_down:
#me.active = False
for e in enemies_down:
e.active = False
# 绘制玩家飞机
if me.active:
if switch_image:
screen.blit(me.image1,me.rect)
else:
screen.blit(me.image2, me.rect)
else:
# 毁灭
if not(delay % 3):
if me_destroy_index == 0:
me_down_sound.play()
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
# 绘制剩余全屏炸弹数量
bomb_text = bomb_font.render("× %d" % bomb_num, True, WHITE)
text_rect = bomb_text.get_rect()
screen.blit(bomb_image, (10, height - 10 - bomb_rect.height))
screen.blit(bomb_text, (20 + bomb_rect.width, height - 5 - text_rect.height))
# 绘制得分
score_text = score_font.render("Score : %s" % str(score), True, WHITE)
screen.blit(score_text, (10, 5))
#绘制暂停按钮
screen.blit(paused_image,paused_rect)
#切换图片
if not (delay % 5):
switch_image = not switch_image
delay -= 1
if not delay:
delay = 100
pygame.display.flip()
clock.tick(60)
if __name__ == "__main__":
try:
main()
except SystemExit:
pass
except:
traceback.print_exc()
pygame.quit()
input()
本篇博客到这就完啦,非常感谢您的阅读,下一篇继续来完成飞机大战游戏,如果对您有帮助,可以帮忙点个赞或者来波关注鼓励一下喔 ,嘿嘿