飞机大战
前言
近来,受到朋友嘱托,做一款简单的小游戏,本小白很久没有碰过这方面的东西了,想知新还需温故,便书写此篇博客,本人只是利用博客记录自己的学习经历,水平较低。
很久之前,曾在b站观看黑马程序员的python教学视频,其中有不小的篇幅细致讲解了利用pygame库做一款飞机大战的游戏。但是只是懵懵懂懂跟着码,有很多东西值得去总结,所以本篇博客由黑马源码、黑马分享的飞机素材和笔记组成。
谈及黑马,他那部视频非常基础、细致,正如他所说:听得懂中文都能学会。对python语言感兴趣却不知道如何下手的同学,可以考虑去看一两集。
第一步:程序运行顺序
def start_game(self):
print("游戏开始...")
while True:
#1.刷新频率
self.clock.tick(FRAME_PER_SEC)
#2.事件监听
self.__event_handler()
#3.碰撞检测
self.__check_collide()
#4.更新/绘制精灵
self.__updata_sprites()
#5.更新检测
pygame.display.update()
本次程序主要运用到的库:pygame(核心)
运行程序后游戏直接进行,设定刷新频率、事件监听(是否有摁键摁下)、碰撞检测(敌机与本机,子弹与敌机)、绘制图像(根据前几步骤,确定场上的子弹、子弹、本机的位置并画出)、全员更新(例如敌机逼近,子弹前行)。
第二步:游戏窗口
游戏窗口的创建:
SCREEN_RECT = pygame.Rect(0,0,480,720)
self.screen = pygame.display.set_mode(SCREEN_RECT.size) #常量 > 700
主屏上将会承载接下来所有的元素,且应该给玩家留有反应空间,长方形最佳。
这里运用到了pygame.Rect类,传入参数(left, top, width, height),随后在set_mode函数中传入隶属于Rect类、名叫SCREEN_RECT对象的size:(480,720)。于是一个宽480,长720的游戏窗口便建立完成。
第三步:敌机精灵
飞机大战类似雷电,本次游戏中,敌机出现的位置,数量,飞行速度和外观均为随机。
这里运用到了pygame库中精灵与精灵组(类)的概念:https://blog.csdn.net/houyanhua1/article/details/84193317。而在该类中,比较重要的有image,rect,最为关键亦是最为常用的是update成员函数,我们往往因自身需求改写该函数。
class GameSprite(pygame.sprite.Sprite): #(继承父类) 其中sprite是模块 Sprite是类名称
'''飞机大战游戏精灵'''
#构造函数/初始化
def __init__(self,image_name,speed = 1):
#调用父类初始化方法
super().__init__()
#定义对象的属性
self.image = pygame.image.load(image_name)
self.rect = self.image.get_rect() #图像的属性
self.speed = speed
def update(self, *args): #*args 一定保留
#屏幕上垂直移动
self.rect.y += self.speed
创建精灵类,继承自pygame.sprite.Sprite,增添初始化函数内容,写入装载img,设置rect内容。updata函数用于更新状态,例如敌机、子弹不断前进。
class Enemy(GameSprite):
def __init__(self):
#调用父类 指定图片
super().__init__("./image/enemy1.png")
#指定随机速度
self.speed = random.randint(1,3)
#敌机初始位置
#垂直方向
self.rect.bottom = 0
#水平方向 加以限定 只在游戏界面出现
max_x = SCREEN_RECT.width - self.rect.width
self.rect.x = random.randint(0,max_x)
def update(self, *args):
super().update()
#若敌机飞出游戏屏幕
if self.rect.y >= SCREEN_RECT.height:
#将精灵从所有的组移出 并被del调用
self.kill()
#print("飞出了屏幕")
敌机类继承精灵类,于初始化中定义了几种属性。此外,当敌机自上而下飞出游戏屏幕时,需要销毁,无限增加敌机会给电脑带来很大负担。
出现了一个新的问题:一个敌机出现后,在更新画图的时候调用enemy1.update()便有了数据上的位移,但是实际情况上,游戏中敌机数量往往很多,难道需要一个个调用成员函数?
利用pygame中精灵组,定义定时产生敌机事件并且监听,产生后将其放入一个敌机精灵组,在需要绘制时,对精灵组进行更新即可。
#创建敌机的定时器常量
CREATE_ENEMY_EVENT = pygame.USEREVENT #用户事件的常量
pygame.time.set_timer(CREATE_ENEMY_EVENT,1000) #时间一到,触发名叫CREAT_ENEMY_EVENT的事件
#事件监听
for event in pygame.event.get():
if event.type == CREATE_ENEMY_EVENT:
#创建敌机精灵
enemy = Enemy()
#添加到精灵组
self.enemy_group.add(enemy)
#更新并绘制敌机精灵组
def __updata_sprites(self):
#精灵组调用update
self.enemy_group.update()
self.enemy_group.draw(self.screen)
第四步:动态背景
在没有摁键摁下的时候,本机是静止的,为了营造飞翔的视觉效果,采用背景动态划动的做法。两张相同的图上下相连,持续向下位移,循环初始。
持续移动,装载图片,拥有长宽属性,这不恰恰和自己定义的GameSprite类很像似?同样的道理,本机,子弹都拥有着相似的属性,我们只需要继承并改写就好。
class Background(GameSprite):
"""游戏背景精灵"""
def __init__(self,is_alt=False):
#调用父类方法
super().__init__("./image/background.png") #传入背景图片
#判断图像
if is_alt:
self.rect.y = -self.rect.height
def update(self, *args):
#1.调用父类的更新,默认速度为1
super().update()
#2.判断是否移出屏幕
if self.rect.y >= SCREEN_RECT.height:
self.rect.y = -self.rect.height
def __create_sprites(self):
#背景精灵组
bg1 = Background()
bg2 = Background(1)
self.back_group = pygame.sprite.Group(bg1,bg2)
#更新并绘制
self.back_group.update()
self.back_group.draw(self.screen)