Pygame库200行代码实现简易飞机大战
写在开头,因为这个小游戏的实验主要是帮助我熟悉pygame库的使用,所以游戏的一些地方可能存在不完善处,还望包涵。
安装使用库
pygame简介
pygame是跨平台python模块,专为电子游戏设计,包括图像、声音。建立在SDL基础上,允许实时电子游戏研发而无需被低级语言束缚, 开发者可以把精力放在游戏的架构上。
pgame中主要模块介绍
(1) pygame
pygame模块会自动导入其它的pygame相关模块。
pygame模块包括surface函数, 可以返回一个新的surface 对象。 init()函数是pygame游戏的核心,必须在进入游戏的主循环之前调用。init()会自动初始化其它所有模块。
(2) pygame.locals
包括在你自己的模块作用域内使用的名字(变量)。包括事件类型、键和视频模式等的名字。
(3) pygame.display
包括处理pygame显示方式的函数。包括普通窗口和全屏模式。 pygame.display中一些常用的方法如下:
- flip:更新显示。
- update:更新一部分时候使用update。
- set_mode:设定显示的类型和尺寸。
- set_caption:设定pygame程序的标题。
- get_surface:调用flip和blit前返回一个可用于画图的surface对象。
(4) pygame.font
包括font函数,用于表现不同的字体。
(5) pygame.sprite
游戏精灵,Group用做sprite对象的容器。调用group对象的update对象,会自动调用所有sprite对象的update方法。
(6) pygame.mouse
隐藏鼠标光标,获取鼠标位置。
(7) pygame.event
追踪鼠标单击、按键按下和释放等事件。
(8) pygame.image
用于处理保存在GIF、PNG或者JPEG文件内的图像。
注:程序中除了local模块和font模块没有使用,其他模块均有涉及。
pygame的安装
你可以选择使用pycharm中工具栏下方的python packages
搜索pygame进行安装,当然也可以使用cmd命令符中常用的pip install pygame
的方法。
安装超时网速慢,直接超时,可以指定国内源镜像。
pip install -i 国内镜像地址 包名
例如: pip install -i https://mirrors.aliyun.com/pypi/simple/ numpy
国内常用源镜像地址:
清华:https://pypi.tuna.tsinghua.edu.cn/simple
阿里云:http://mirrors.aliyun.com/pypi/simple/
中国科技大学: https://pypi.mirrors.ustc.edu.cn/simple/
豆瓣:http://pypi.douban.com/simple/
验证安装
验证pygame是否安装成功,在cmd命令符中使用 pip list
语句,如果安装成功,我们可以在下方结果中找到。
程序原理
首先创建游戏的主窗体,主窗体的大小就是你背景图片的大小。
import pygame #游戏初始化 pygame.init() #创建游戏主窗口 screen = pygame.display.set_mode((480,700)) #绘制背景图香 #1 加载图像数据 background = pygame.image.load('./image/background.png') #2 blit绘制图像 #从左上角绘制 screen.blit(background,(0,0)) pygame.display.update() #卸载所有的模块 pygame.quit()
绘制图像时的方向,后面我们主机、敌机、背景、子弹的方向都需要注意这一点。
背景图像绘制完之后就是我们主机、敌机的绘制,我们可以在所有的绘制工作完成后再统一调用update方法
#绘制飞机图片 hero = pygame.image.load('./image/hero2.png') screen.blit(hero,(150,300)) pygame.display.update()
其实飞机大战的原理和动画片的原理一致,我们需要对一个个按下的指令进行反应,从而形成一个连贯的画面,这里我们就需要使用到pygame库中的time模块
#创造时钟对象 屏幕绘制速率 clock = pygame.time.Clock() #游戏循环 while True: #指定循环体内部代码的执行频率 每秒重复60次 clock.tick(60) #将移动前飞机进行遮挡 达到动画效果 screen.blit(background, (0, 0)) screen.blit(hero,hero_rect)
如果你想停止程序,发现点击窗体右上角的关闭并无反应,因为你还未在循环体内进行事件监听,然后对时间进行判断
event_list = pygame.event.get() for event in event_list: #判断是否为退出事件 if event.type == pygame.QUIT: print("退出游戏...") #卸载所有模块 pygame.quit() exit()
关于敌机的生成我们需要使用到sprite精灵和精灵组模块,创建两个一样的敌机,但是敌机的飞行速度不一样,然后将敌机精灵添加至敌机精灵组中,然后在循环体中我们不断将敌机精灵组进行绘制即可
enemy = GameSprite("./image/enemy0.png") enemy1 = GameSprite("./image/enemy0.png",2) enemy_group = pygame.sprite.Group(enemy,enemy1) #精灵组调用方法 enemy_group.update() enemy_group.draw(screen)
对上述部分代码进行整理简化,最后我们可以得到这样的效果图,主机和敌机都可以移动
import pygame from plane_sprites import * #游戏初始化 pygame.init() #创建游戏主窗口 screen = pygame.display.set_mode((480,700)) #绘制背景图香 #1 加载图像数据 background = pygame.image.load('./image/background.png') #2 blit绘制图像 #从左上角绘制 screen.blit(background,(0,0)) #3 update更新屏幕显示 # pygame.display.update() #绘制飞机图片 hero = pygame.image.load('./image/hero2.png') screen.blit(hero,(150,300)) #可以在所有绘制工作完成后,统一调用update方法 pygame.display.update() #创造时钟对象 屏幕绘制速率 clock = pygame.time.Clock() #定义飞机初始位置 hero_rect = pygame.Rect(150,300,100,122) #敌机精灵创建 enemy = GameSprite("./image/enemy0.png") enemy1 = GameSprite("./image/enemy0.png",2) enemy_group = pygame.sprite.Group(enemy,enemy1) #游戏循环 while True: #指定循环体内部代码的执行频率 每秒重复60次 clock.tick(60) #监听时间 event_list = pygame.event.get() # if len(event_list)>0: # print(event_list) for event in event_list: #判断是否为退出事件 if event.type == pygame.QUIT: print("退出游戏...") #卸载所有模块 pygame.quit() exit() hero_rect.y -= 1 if hero_rect.y <=0: hero_rect.y = 700 #将移动前飞机进行遮挡 达到动画效果 screen.blit(background, (0, 0)) screen.blit(hero,hero_rect) #精灵组调用方法 enemy_group.update() enemy_group.draw(screen) pygame.display.update() pass pygame.quit()
程序升级
设置飞机大战的精灵类和常量
这里的update方法主要是在为了后面背景类、敌机类和子弹类中达到可以垂直飞行的效果
#屏幕大小 SCREEN_RECT = pygame.Rect(0,0,480,700) #刷新帧率 SCREEN_PER_SEC = 60 #创建敌机的定时器常量 CREATE_ENEMY_EVENT = pygame.USEREVENT #英雄发射子弹事件 HERO_FIRE_EVENT = pygame.USEREVENT + 1 class GameSprite(pygame.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): self.rect.y += self.speed
背景类和敌机类
因为想要背景会随着移动,所以在背景类中的init设置了is_alt参数,用来添加背景到原图像的上方,这样就可以达到背景滚动的效果。
在敌机类中对敌机的位置进行判断,如果敌机超过我们预设屏幕y的方位就会使用kill()方法从精灵组中删除,敌机生成的位置和速度采用了random随机数的randint()方法
class Background(GameSprite): #游戏背景滚动 def __init__(self,is_alt=False): #调用父类中精灵中的创建 image/rect/speed super().__init__("./image/background.png") if is_alt: self.rect.y = -self.rect.height def update(self): super().update() #如果移出屏幕,将图像移到屏幕上访 if self.rect.y >= SCREEN_RECT.height: self.rect.y = -self.rect.height class Enemy(GameSprite): #敌机精灵 def __init__(self): #1 调用父类方法 创建敌机精灵 super().__init__("./image/enemy0.png") #2 指定敌机初始随机速度 self.speed = random.randint(1,2) #3 指定敌机初始随机位置 self.rect.bottom = 0 max_x = SCREEN_RECT.width - self.rect.width self.rect.x = random.randint(0,max_x) def update(self): #!调用父类方法 垂直方向飞行 super().update() #2 判断是否飞出屏幕 如果是,需要从精灵组中删除 if self.rect.y >= SCREEN_RECT.height: print("飞出屏幕,需要从精灵组中删除...") #kill方法可以将精灵从精灵族中移出,精灵自动销毁 self.kill() def __del__(self): print("敌机死亡 %s" % self.rect)
主机类和子弹类
SCREEN_RECT.centerx是用来保证一开始主机初始在屏幕中央部分,关于主机的y因为在一开始画了一张绘制示意图所以这里应该为屏幕的SCREEN_RECT.bottom减去部分距离。
子弹类中和敌机类同理需要判断是否飞出屏幕范围,如果超过使用kill()方法从精灵组中删除,子弹的初始位置应该是主机的上方,既子弹的x等于主机的x
#主角飞机 class Hero(GameSprite): def __init__(self): # 调用父类 设置英雄图像和速度 super().__init__("./image/hero2.png",0) # 设置主机位置 self.rect.centerx = SCREEN_RECT.centerx self.rect.bottom = SCREEN_RECT.bottom - 50 # 子弹精灵组 self.bullet_group = pygame.sprite.Group() def update(self): #水平方向移动 self.rect.x += self.speed #控制英雄不能为0 if self.rect.x < 0: self.rect.x =0 elif self.rect.right > SCREEN_RECT.right: self.rect.right = SCREEN_RECT.right #子弹发射 def fire(self): print("发射子弹...") #创建子弹精灵 bullet = Bullet() #设置子弹位置 bullet.rect.bottom = self.rect.y-20 bullet.rect.centerx = self.rect.centerx #添加到子弹精灵组中 self.bullet_group.add(bullet) #子弹类 class Bullet(GameSprite): def __init__(self): #调用父类方法 设置子弹图片速度 super().__init__("./image/bullet1.png",-2) def update(self): #调用父类方法 让子弹垂直飞行 super().update() #判断子弹是否飞出 if self.rect.bottom < 0: self.kill() def __del__(self): print("子弹被销毁")
主游戏类
在主类中进行精灵组的创建,背景精灵组的创建是为了达到背景滚动的效果
#精灵组的创建 def __create_sprites(self): #创建背景精灵和精灵1组 bg1 = Background() bg2 = Background(True) self.back_group = pygame.sprite.Group(bg1,bg2) #创建敌机精灵和精灵组 self.enemy_group = pygame.sprite.Group( #创建英雄精灵和精灵组 self.hero = Hero() self.hero_group = pygame.sprite.Group(self.hero)
游戏的初始化
def __init__(self): print("游戏初始化") #创建游戏窗口 self.screen = pygame.display.set_mode(SCREEN_RECT.size) #创建游戏时钟 self.clock = pygame.time.Clock() self.__create_sprites() #敌机生成和主机开火的定时器 pygame.time.set_timer(CREATE_ENEMY_EVENT,1000)#单位是ms pygame.time.set_timer(HERO_FIRE_EVENT,500)
事件监听方法
关于事件的监听过程中,需要对敌机生成和子弹飞行、主机移动指令做出对应反应
def __event_handler(self): for event in pygame.event.get(): if event.type == pygame.QUIT: PlaneGame.__game_over() elif event.type == CREATE_ENEMY_EVENT: print("敌机生成...") #创建敌机精灵 enemy = Enemy() #敌机精灵加入精灵组 self.enemy_group.add(enemy) elif event.type == HERO_FIRE_EVENT: print("发射子弹...") self.hero.fire() #飞机移动 keyss_pressed = pygame.key.get_pressed() if keyss_pressed[pygame.K_RIGHT]: print("向右移动") self.hero.speed = 2 elif keyss_pressed[pygame.K_LEFT]: print("向左移动") self.hero.speed = -2 else: self.hero.speed = 0
碰撞检测
对于子弹碰撞敌机和敌机碰撞主机的情况进行判定
#碰撞检测 def __check_collide(self): #子弹摧毁敌机 pygame.sprite.groupcollide(self.hero.bullet_group,self.enemy_group,True,True) #敌机撞毁英雄 enemies = pygame.sprite.spritecollide(self.hero, self.enemy_group, True) if len(enemies) > 0: self.hero.kill() PlaneGame.__game_over()
精灵组更新显示
#精灵更新显示 def __update_sprites(self): #背景精灵组 self.back_group.update() self.back_group.draw(self.screen) #敌机精灵组 self.enemy_group.update() self.enemy_group.draw(self.screen) #主机精灵组 self.hero_group.update() self.hero_group.draw(self.screen) #子弹精灵组 self.hero.bullet_group.update() self.hero.bullet_group.draw(self.screen)
游戏的开始与结束
def start_game(self): print("游戏开始...") while True: #1 设置刷新帧率 self.clock.tick(SCREEN_PER_SEC) #2 时间监听 self.__event_handler() #3 碰撞检测 self.__check_collide() #4 更新敌机 self.__update_sprites() #5 更新显示 pygame.display.update() @staticmethod def __game_over(): print("游戏结束") pygame.quit() exit()
程序总结
程序中print语句是为了我在运行中进行功能的检测,可以直接注释掉,对于整个程序的运行并无影响。游戏只是简单的规则,主机可以左右移动,敌机会随机从上方垂直下来,敌机撞到主机之后就会游戏结束直接关闭游戏窗体,比如击毁敌机也没有得分这一设置,这些都是可以再对规则进行优化的部分,因为只是为了帮助我熟悉pygame库的功能,所以代码并未写的更加详细,感兴趣的话你可以对代码部分进行更改达到你想要的效果,程序升级部分并未进行太多的文字解释部分,在代码中我保留了我的注释部分方便你对代码进行解读。
程序中用到的素材以及未使用到的音乐都在链接中。