#!/usr/bin/env python3#-*- coding: utf-8 -*-#author:albert time:2020/9/23
importrandomimportpygame#常量
WIN_SIZE= (480, 700) #游戏窗口宽高
FPS = 60 #游戏帧数
HERO_PATH = "images/me1.png" #我方飞机图的路径
ENEMY_PATH = "images/enemy1.png" #敌机图的路径
BULLET_PATH = "images/bullet1.png" #子弹图的路径
BG_PATH = "images/background.png" #背景图的路径
CREATE_ENEMY_EVENT = pygame.USEREVENT #配合定时器定时生成敌机
CREATE_BULLET_EVENT = pygame.USEREVENT + 1 #配合定时器定时生成子弹
classHeroPlaneClass(pygame.sprite.Sprite):"""定义一个英雄飞机类 继承自pygame的精灵类
属性 image rect 定义飞机的图片来源和初始坐标和宽高
方法 update __init__(需要调用父类的init) fire() 发射子弹的方法
支持 需要pygame和 常量 WIN_SIZE"""
def __init__(self, image, **kwargs):
pygame.sprite.Sprite.__init__(self)
self.image= pygame.image.load(image) #image获取图像surface
self.rect = self.image.get_rect() #rect获取宽高
#英雄飞机初始位置位于最下面的中间
self.rect.x = int((WIN_SIZE[0] - self.rect.w)/2)
self.rect.y= WIN_SIZE[1] -self.rect.hdefupdate(self):#英雄飞机的行为:是用键盘事件上下左右键盘控制的 所以这里就不写具体行为了
pass
#负责生成子弹实例 然后子弹按照自己的行为模式运动即可
#第二个参数是用于暂时存放子弹实例的子弹精灵组
#经过测试 发现类方法传递精灵组也是地址传递 在类方法内修改一样会提现到精灵组本身
deffire(self, tmp_bullet_group):#生成一个子弹
tmp_bullet =BulletClass(BULLET_PATH, self.rect)
tmp_bullet_group.add(tmp_bullet)#添加到子弹精灵组里面
#简单的移动我机 和按键触发事件配合使用
def move(self, offset_x=0, offset_y=0):
self.rect.x+=offset_x
self.rect.y+=offset_y#避免越界
if self.rect.x <0:
self.rect.x=0elif self.rect.x > WIN_SIZE[0] -self.rect.w:
self.rect.x= WIN_SIZE[0] -self.rect.wif self.rect.y <0:
self.rect.y=0elif self.rect.y > WIN_SIZE[1] -self.rect.h:
self.rect.y= WIN_SIZE[1] -self.rect.hclassEnemyPlaneClass(pygame.sprite.Sprite):"""定义一个敌人飞机类 继承自pygame的精灵类
属性 image rect 定义飞机的图片来源和初始坐标和宽高
方法 update __init__(需要调用父类的init)
支持 需要pygame和 常量 WIN_SIZE"""
def __init__(self, image, **kwargs):
pygame.sprite.Sprite.__init__(self)
self.image= pygame.image.load(image) #image获取图像surface
self.rect = self.image.get_rect() #rect获取宽高
#敌机要求在最上一行随机位置刷新
self.rect.x = random.randint(1, WIN_SIZE[0] -self.rect.x)
self.rect.y=0def update(self, *args):#敌机的行为:是在间隔一定时间自动在最上一行随机位置产生 然后无脑向下直走 超出最下边界 就自动销毁他
#后面也可以考虑远距离开始瞄准英雄飞机抛物线撞击 后面再说
self.rect.y += 10
if self.rect.y > WIN_SIZE[1]:
self.kill()#敌机超出最下面边界了 自然是自动销毁
def __del__(self):print("<敌机销毁>")classBulletClass(pygame.sprite.Sprite):"""定义一个子弹类 继承自pygame的精灵类
属性 image rect 定义子弹的图片来源和初始坐标和宽高
方法 update __init__(需要调用父类的init)
支持 需要pygame和 常量 WIN_SIZE"""
def __init__(self, image, hero_rect):
pygame.sprite.Sprite.__init__(self)
self.image= pygame.image.load(image) #image获取图像surface
self.rect = self.image.get_rect() #rect获取宽高
#子弹的初始位置要求在英雄飞机的上部中间位置
self.rect.x = hero_rect.x + int(hero_rect.w/2)
self.rect.y=hero_rect.ydef update(self, *args):#子弹的行为:是在间隔一定时间自动在英雄飞机最上一行中间位置产生 然后无脑向上直走 超出最上边界 就自动销毁他
self.rect.y -= 10
if self.rect.y < 0 -self.rect.h:
self.kill()#子弹超出最上边界 就自动销毁他
def __del__(self):print("(子弹销毁)")classBgClass(pygame.sprite.Sprite):"""定义一个背景类 继承自pygame的精灵类
属性 image rect 定义背景的图片来源和初始坐标和宽高 初始位置是超出了游戏窗口 开始是左下和左下对齐 到头了是左上和左上对齐
方法 update __init__(需要调用父类的init)
支持 需要pygame和 常量 WIN_SIZE"""
def __init__(self, image):
pygame.sprite.Sprite.__init__(self)
self.image= pygame.image.load(image) #image获取图像surface
self.rect = self.image.get_rect() #rect获取宽高
self.rect.y = WIN_SIZE[1] - self.rect.h #设置背景的初始高度
def update(self, *args):#背景的行为:图左下和窗口左下对齐 然后慢慢的向下移动 当然了 我这里背景图片是1400高度 是2个背景图拼接在一起的
#背景图的左上对齐窗口左上或者超出 就自动回到初始位置重新来过
self.rect.y += 10
if self.rect.y >0:
self.rect.y= WIN_SIZE[1] - self.rect.h #让背景重新回到上面重新滚动
classWinClass(object):"""经过测试用get_window返回一个窗口对象可以正常的使用
这类其实就是生成一个游戏窗口对象并返回它
需要pygame库的支持"""
def __init__(self, pos):
self.x=pos[0]
self.y= pos[1]defget_window(self):returnpygame.display.set_mode((self.x, self.y))classGameGod(object):def __init__(self):#整体代码初始化 常用属性的声明
pygame.init()#窗口建立
tmp_win =WinClass(WIN_SIZE)
self.win1=tmp_win.get_window()#我机 敌机 的精灵实例和精灵组的创建 背景 子弹精灵组
self.bg1 =BgClass(BG_PATH)
self.bg_group=pygame.sprite.Group(self.bg1)
self.hero1=HeroPlaneClass(HERO_PATH)
self.hero_group=pygame.sprite.Group(self.hero1)
self.enemy_group=pygame.sprite.Group()
self.bullet_group=pygame.sprite.Group()#clock的建立和定时器定时生成敌机和生成子弹
self.clock =pygame.time.Clock()
pygame.time.set_timer(CREATE_ENEMY_EVENT,1000)
pygame.time.set_timer(CREATE_BULLET_EVENT, 100)defgame_start(self):#正式循环的开始
#循环fps 事件触发 判断碰撞 绘图 刷新
whileTrue:#1. 设置刷新帧率
self.clock.tick(FPS)#2. 事件监听
#开始遍历事件做针对处理 同时避免卡顿
for event inpygame.event.get():if event.type ==pygame.QUIT:
pygame.quit()if event.type ==CREATE_ENEMY_EVENT:#定时创建敌机
self.enemy_group.add(EnemyPlaneClass(ENEMY_PATH))if event.type ==CREATE_BULLET_EVENT:#定时开火 其实本质是定时生成子弹 子弹被生成后按照子弹类定义的行为移动
self.hero1.fire(self.bullet_group)#配合按键事件来移动我机
keys_pressed =pygame.key.get_pressed()ifkeys_pressed[pygame.K_UP]:print("向上")#hero1.rect.y -= 4
self.hero1.move(0, -4)elifkeys_pressed[pygame.K_DOWN]:print("向下")#hero1.rect.y += 4
self.hero1.move(0, 4)elifkeys_pressed[pygame.K_LEFT]:print("向左")#hero1.rect.x -= 4
self.hero1.move(-4, 0)elifkeys_pressed[pygame.K_RIGHT]:print("向右")#hero1.rect.x += 4
self.hero1.move(4, 0)#3. 碰撞检测
#子弹摧毁敌机(子弹组和敌机组的碰撞测试)
pygame.sprite.groupcollide(self.bullet_group, self.enemy_group, True, True)#敌机撞毁英雄(敌机组和英雄精英的碰撞测试)
enemies =pygame.sprite.spritecollide(self.hero1, self.enemy_group, True)#判断列表时候有内容 就是说我机被撞上了
if len(enemies) >0:#让英雄牺牲
self.hero1.kill()#结束游戏
pygame.quit()
exit()#4. 更新/绘制精灵组
self.bg_group.update() #肯定是先更新背景
self.bg_group.draw(self.win1)
self.hero_group.update()
self.hero_group.draw(self.win1)
self.enemy_group.update()
self.enemy_group.draw(self.win1)
self.bullet_group.update()
self.bullet_group.draw(self.win1)#5. 更新显示
pygame.display.update()defgame_over(self):
pygame.quit()
exit()if __name__ == '__main__':#没事可以外面加个异常处理 懒得动弹了
god1 =GameGod()
god1.game_start()
god1.game_over()