import pygame
import time
from Sprite_class import *
class PlaneGame(object):
"""飞机大战主游戏"""
def __init__(self):
print("游戏初始化")
#1. 创建游戏的窗口
self.screen=pygame.display.set_mode(SCREEN_RECT.size)# 这里面要求传递一个元组,SCREEN_RECT.size 刚好就是得到元组
#2. 创建游戏的时钟
self.clock =pygame.time.Clock()
#3. 调用私有方法,精灵和精灵组的创建
self.__creat_sprites()#这个方法不能在while True 里面调用
#::设置定时器事件--创建敌机 1s出一架飞机
pygame.time.set_timer(CREATE_ENEMY_EVENT, 500)
pygame.time.set_timer(HERO_FIRE_EVENT,500)
def __creat_sprites(self):
#私有方法 以__开头
# 创建背景精灵 和精灵组
bg1 = Background()
bg2 = Background(is_alt=True)
bg2.rect.y=-bg2.rect.height
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)
# 在这个方法中 创建精灵和精灵组
# 让精灵组调用update 和draw()方法
def start_game(self):
print("游戏开始.....")
while True:
# 1.设置刷新帧率
self.clock.tick(FRAME_PER_SEC)
# 2.事件监听
self.__event_handler()
# 3.碰撞检测
self.__check_collide()
# 4.更新/绘制精灵组
self.__update_sprites()
# 5.更新显示
pygame.display.update()
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_sprites =Enemy()
self.enemy_group.add(enemy_sprites)
#通过 监听事件的方式 这种方式不可以 按键盘 一直触发
# elif event.type==pygame.KEYDOWN and event.key ==pygame.K_RIGHT:
# print("向右移动...")
#使用键盘提供的方法 获取 键盘按键: 可以按一次,一直触发
# 这个时要被遍历的开火 事件!!!
if event.type == HERO_FIRE_EVENT:
self.hero.fire()
# 返回一个 按键元组
keys_pressed = pygame.key.get_pressed()
#怎么使用这个元组? 判断元组中 对应按键的索引值
if keys_pressed[pygame.K_RIGHT]:
# print("向右移动")
self.hero.rect.x+=5
elif keys_pressed[pygame.K_LEFT]:
# print("向左移动")
self.hero.rect.x-=5
elif keys_pressed[pygame.K_UP]:
# print("向前移动")
self.hero.rect.y-=5
elif keys_pressed[pygame.K_DOWN]:
# print("向后移动")
self.hero.rect.y+=5
def __check_collide(self):
#1 子弹摧毁敌机
pygame.sprite.groupcollide(self.hero.bullets,self.enemy_group,True,True)
#2 敌机撞毁英雄
enemies =pygame.sprite.spritecollide(self.hero,self.enemy_group,True)
#判断 enemies 列表是否有内容,有内容的话就 把英雄干掉
if len(enemies):
time.sleep(2)
print("英雄牺牲")
self.hero.kill()
time.sleep(1)
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.bullets.update()
self.hero.bullets.draw(self.screen)
def __update_screen(self):
pass
@staticmethod #静态方法 不需要传参
def __game_over():
print("游戏结束")
pygame.quit()
exit()
if __name__ == '__main__':
#编写在主程序中直接执行的
pygame.init()
#创建游戏对象
game =PlaneGame()
#开始游戏
game.start_game()
"""
使用常量 都代替固定的数值
1 常量--- 不变化的量
2 变量--- 可以变化的量
应用场景:
1--在开发时,可能会需要使用固定的数值,例如屏幕的高度是700
2--在这个时候,简易不要直接使用固定数值,而应该使用常量
3--在开发时,为了保证代码的可维护性,尽量不要使用魔法数字
常量的定义:
1-- 定义常量和定义变量的语法完全一样,都是使用赋值语句
2-- 常量的命名应该所有字母都是用大写,单词与单词之间使用下划线连接
提示:PYthon中并没有真正意义的常量,只是通告命名的约定---所有字母都大写的就是常量。开发时不要轻易修改
"""
#:2 游戏精灵库代码:
#导入随机模块
import random
import pygame
"""
导入模块的顺序:
1 官方标准模块导入
2 第三方模块导入
3 应用程序模块导入
"""
pygame.init()
SCREEN_RECT=pygame.Rect(0,0,480,700)
FRAME_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__() # 子类的父类不是 object 不主动调用,就没有办法享用到父类的 初始化代码
#定义对象的属性
self.image=pygame.image.load(image_name)
self.rect = self.image.get_rect() # 返回图像大小,且x,y
self.speed = speed
def update(self):
# 在屏幕的垂直方向上移动 改变其y值
self.rect.y += self.speed
class Background(GameSprite):
"""游戏背景精灵"""
def __init__(self,is_alt=False):
# #1.调用父类方法实现精灵的创建(image/rect/speed)
super().__init__("./images/background.png")
# #2.判断是否交替图像,如果是,需要设置初始位置
if is_alt:
self.rect.y=-self.rect.height
def update(self):
# 1. 调用父类的方法实现
super().update()
# 2. 判断是否移出屏幕,(背景轮动)如果移除屏幕,将图像设置到屏幕的上方
if self.rect.y>=SCREEN_RECT.height:
self.rect.y=-self.rect.height
class Enemy(GameSprite):
"""敌机精灵"""
def __init__(self):
#1.调用父类方法,创建敌机精灵,同时指定敌机图片
super().__init__("./images/enemy1.png")
#2.指定敌机的初始随机速度 1-3
self.speed= random.randint(3,6) # 随机数的最小值,随机数的最大值
#3.指定敌机的初始随机位置
self.rect.bottom= 0
self.rect.x=random.randint(0,480-self.rect.width)
pass
"""
使用pygame.Rect 提供的bottom属性,在指定敌机初始位置时,会比较方便
bottom =y +height
y=bottom-height
"""
def update(self):
#1.调用父类方法,(游戏精灵父类中实现了垂直飞行) 保持垂直方向的飞行
super().update()
#2.判断是否飞出屏幕,如果废除,需要从精灵组中删除敌机
if self.rect.y >=SCREEN_RECT.height:
# print("飞出屏幕,需要从精灵组删除....")
#kill 方法可以将精灵从所有精灵组中移出,精灵就会被自动销毁
self.kill()
"""
移出屏幕销毁敌机:
从敌机组删除,否则会造成内存浪费
检测敌机被销毁
__del__ 内置方法 会在对象被 销毁 前调用, 在开发中,可以用于 判断对象是否被销毁
def __del__(self):
销毁敌机的方法
kill() 方法, 删除某个精灵 从所有组,内存
"""
def __del__(self):
# print("敌机挂了 %s "% self.rect)
pass
class Hero(GameSprite):
"""
英雄需求:
1 英雄初始位置 屏幕 水平中间位置, 距离屏幕底部 120px
2 英雄每隔0.5s 发射一次子弹,每次连发三枚子弹
3 英雄默认不会移动,需要通过左/右 方向键,控制英雄在水平方向移动
子弹需求:
1:子弹从英雄的正上方发射 沿直线 向上方飞行
2:飞出屏幕后,需要从 精灵组中 删除
"""
def __init__(self):
super().__init__("./images/me1.png")
self.speed = 0
self.rect.centerx = SCREEN_RECT.centerx
self.rect.bottom = SCREEN_RECT.bottom - 120
#创建子弹精灵组
self.bullets =pygame.sprite.Group()
def update(self):
#英雄在水平方向移动:
self.rect.x +=self.speed
self.rect.y +=self.speed
#控制英雄不能离开屏幕
if self.rect.x<=0 :
self.rect.x=0
if self.rect.x>SCREEN_RECT.width-self.rect.width:
self.rect.x=SCREEN_RECT.width-self.rect.width
if self.rect.y<=0:
self.rect.y=0
if self.rect.y>=SCREEN_RECT.height-self.rect.height:
self.rect.y=SCREEN_RECT.height-self.rect.height
def fire(self):
# print("发射子弹...")
#一次发射三枚子弹
for i in (0,1,2):
#1.创建子弹精灵
bullet = Bullets()
#2 设置精灵的位置
bullet.rect.bottom=self.rect.y-i*bullet.rect.height
bullet.rect.centerx=self.rect.centerx
#3 将精灵添加到精灵组
self.bullets.add(bullet)
class Bullets(GameSprite):
"""子弹精灵"""
def __init__(self):
super().__init__("./images/bullet1.png",-3)
#调用父类方法,设置子弹图片,设置初始速度
def update(self):
super().update()
#判断子弹是否飞出屏幕
if self.rect.bottom<0:
#表示子弹已经飞出了屏幕
self.kill()
def __del__(self):
pass
# print("子弹被销毁")