最终程序图
程序分析——精灵对象与功能分析
-
基本精灵对象,继承 pygame.sprite.Sprite
-
属性:加载图片、设置初始速度
-
功能:纵向更新速度
-
程序对象代码
import random
import pygame
SCREEN_PRO = pygame.Rect(0, 0, 512, 768)
# 自定义派生精灵子类,继承pygame.sprite.Sprite
class GameSprites(pygame.sprite.Sprite):
"""游戏精灵基类"""
def __init__(self, img_name, speed=1):
# 调用父类的初始化方法
super().__init__()
if img_name is None:
return
# 加载图像
self.image = pygame.image.load(img_name)
# 设置尺寸; get_rect()可以获取图像的原始大小
self.rect = self.image.get_rect()
# 设置速度
self.speed = speed
def update(self):
# 默认在屏幕上垂直方向移动
self.rect.y += self.speed
-
背景图对象(继承精灵基类)
-
属性:背景图片、背景音乐
-
位置:从屏幕0,0 开始绘制(屏幕大小与背景图一样大小)
-
功能:更新图片纵向移动,当移出屏幕后重置到屏幕上方(需要两张背景图,不断交替纵向下移动,表现出飞机不停飞的现象)
-
程序对象代码
class BackGround(GameSprites):
"""背景精灵"""
# 初始化背景图片
def __init__(self, is_alternate=False):
super().__init__("./img/bg2.jpg")
# 加载背景音乐
pygame.mixer.music.load("./music/bg.mp3")
# 设置音量
pygame.mixer.music.set_volume(0.1)
# 设置播放次数, -1 表示无限循环
pygame.mixer.music.play(-1)
# 如果是交替图,初始位置则在屏幕上方
if is_alternate:
self.rect.bottom = SCREEN_PRO.y
def update(self):
# 调用父类更新
super().update()
# 当图片移出屏幕外时,将图片设置到屏幕上方
if self.rect.top >= SCREEN_PRO.height:
self.rect.bottom = SCREEN_PRO.y
-
英雄战机对象
-
英雄图片
-
x轴在屏幕中间,y轴距离屏幕底部200个像素
-
横向移动,不能移出屏幕。发射子弹,发射子弹音乐
-
程序对象代码
class Hero(GameSprites):
"""英雄战机精灵"""
def __init__(self):
super().__init__("./img/hero.png")
# 初始化英雄的位置
self.rect.centerx = SCREEN_PRO.centerx
self.rect.y = SCREEN_PRO.height - 200
def update(self, speed=1):
self.rect.x += speed
# 控制边界
if self.rect.left <= 0:
self.rect.left = 0
elif self.rect.right >= SCREEN_PRO.right:
self.rect.right = SCREEN_PRO.right
def fire(self, bulletGroup):
# 英雄发射子弹
for i in range(1, 2):
bullet = Bullet(-2)
bullet.rect.centerx = self.rect.centerx
bullet.rect.y = self.rect.top - 30 * i
bulletGroup.add(bullet)
# 播放子弹音乐
bullet.bulletMusic.play()
-
两种类型的敌机对象
-
一群普通敌机
-
属性:敌机图片
-
位置:从屏幕上方纵向移动到屏幕中
-
功能:从上往下,纵向移动。移动屏幕后,销毁敌机对象
-
-
一群雷霆敌机
-
功能同上,敌机图片不同
-
-
程序对象代码
class EnemyPlane(GameSprites):
"""敌机精灵"""
def __init__(self):
# 初始化敌机图像
super().__init__("./img/enemy1.png")
# 初始化 敌机随机速度
self.speed = random.randint(2, 4)
# 初始化 敌机随机位置,x轴的 最小位置是0,最大位置是屏幕宽度-敌机宽度
maxSpeed = SCREEN_PRO.width - self.rect.width
self.rect.bottom = SCREEN_PRO.y
self.rect.x = random.randint(0, maxSpeed)
# 初始化爆炸图片
self.boomImg = pygame.image.load("./img/boom.png")
# 初始化敌机销毁音乐
self.boomMusic = pygame.mixer.Sound("./music/boom.mp3")
self.boomMusic.set_volume(0.3)
def update(self):
# 调用父类更新敌机速度
super().update()
# 如果敌机飞出屏幕需要从精灵组中删除
if self.rect.top >= SCREEN_PRO.height:
# kill 方法可以让精灵从所有精灵组中移除,精灵会自动销毁(会默认调用 __del__ 方法)
self.kill()
def plan_die(self):
# 播放音乐
self.boomMusic.play()
class EnemyPlane2(GameSprites):
"""敌机精灵2-雷霆敌机"""
def __init__(self):
# 初始化敌机图像
super().__init__("./img/enemy2.png")
# 初始化 敌机随机速度
self.speed = random.randint(2, 4)
# 初始化 敌机随机位置,x轴的 最小位置是0,最大位置是屏幕宽度-敌机宽度
maxSpeed = SCREEN_PRO.width - self.rect.width
self.rect.bottom = SCREEN_PRO.y
self.rect.x = random.randint(0, maxSpeed)
# 初始化爆炸图片
self.boomImg = pygame.image.load("./img/boom.png")
# 初始化敌机销毁音乐
self.boomMusic = pygame.mixer.Sound("./music/boom.mp3")
self.boomMusic.set_volume(0.3)
def update(self):
# 调用父类更新敌机速度
super().update()
# 如果敌机飞出屏幕需要从精灵组中删除
if self.rect.top >= SCREEN_PRO.height:
# kill 方法可以让精灵从所有精灵组中移除,精灵会自动销毁(会默认调用 __del__ 方法)
self.kill()
def plan_die(self):
# 播放音乐
self.boomMusic.play()
-
子弹图对象
-
属性:子弹图片
-
位置:在英雄敌机的正上方
-
功能:子弹纵向移动,从下往上。移出屏幕后,子弹对象销毁
-
程序对象代码
class Bullet(GameSprites):
"""子弹精灵"""
def __init__(self, speed):
super().__init__("./img/bullet.png", speed)
# 初始子弹发出音乐
self.bulletMusic = pygame.mixer.Sound("./music/bullet2.wav")
self.bulletMusic.set_volume(0.2)
def update(self):
super().update()
# 子弹超出屏幕就销毁
if self.rect.bottom <= 0:
self.kill()
-
计分器对象
-
属性:计分器文字对象
-
位置:初始位置在屏幕左上角,游戏结束时,在结束图片的中间
-
功能:子弹销毁一个敌机,分值+1
-
程序对象代码
class FontSprites(GameSprites):
"""字体精灵"""
def __init__(self, size=18):
super().__init__(None)
self.font = pygame.font.Font("./font/msyh.ttf", size)
# 初始分值
self.fontValue = 0
self.textShow = self.font.render("分值:%d" % self.fontValue, True, (0, 255, 0))
def update(self, color=(0, 255, 0)):
# 第一个参数是写的文字;第二个参数是个布尔值,以为这是否开启抗锯齿,就是说True的话字体会比较平滑,不过相应的速度有一点点影响;
# 第三个参数是字体的颜色;第四个是背景色,如果你想没有背景色(也就是透明),那么可以不加这第四个参数。
self.textShow = self.font.render("分值:%d" % self.fontValue, True, color)
程序所需材料与监听事件
材料
-
背景音乐材料
-
子弹发出音乐材料
-
敌机爆炸音乐材料
-
敌机爆炸图片材料
-
游戏结束图片材料、
-
游戏重新开始图片材料
监听事件
-
点击关闭窗口程序退出事件
-
敌机随机出现事件
-
鼠标点击重新开始游戏事件
-
子弹发出事件
程序分析——主方法分析
-
初始化
-
初始化pygame模块
-
初始化英雄生命
-
初始化开始菜单图片位置
-
初始化游戏窗口
-
初始化游戏时钟
-
初始化爆炸敌机对象
-
初始化各个精灵及精灵组对象
-
初始化 创建敌机定时器(每700毫秒创建一架敌机)
-
初始化 创建子弹定时器 (每500毫秒创建一颗子弹)
-
-
游戏循环
-
检测如果敌机爆炸了,让爆炸效果最多持续1秒
-
设置刷新频率,每秒刷新60次
-
监听事件
-
监听鼠标点击关闭窗口退出游戏事件
-
监听创建敌机事件
-
监听发射子弹事件
-
监听鼠标点击重新开始游戏事件
-
监听键盘按住向左或向右移动英雄战机事件
-
-
判断英雄战机,如果死亡则不再向下更新,直到点击重新开始,或者关闭游戏
-
碰撞检测
-
检测子弹精灵组与敌机精灵组碰撞,一旦有碰撞,则相应的敌机与子弹都销毁,并播放敌机爆炸声音,加载爆炸图片、分值加+1
-
检测英雄战机与敌机精灵组碰撞,一旦有碰撞,英雄战机阵亡,游戏结束,弹出游戏结束界面。停止全部音乐,加载最终分值
-
-
更新精灵组
-
更新与绘制背景图
-
更新与绘制敌机精灵组,如果有爆炸的敌机,需要绘制爆炸图
-
绘制英雄战机
-
更新与绘制子弹
-
绘制分值
-
如果英雄战机销毁,则调用结束方法
-
-
更新屏幕显示
-
程序代码
from plane_sprites import *
# 设置游戏时钟(刷新频率)
TICK_TIMES = 60
# 设置敌机事件、创建敌机所需时间
EVENT_ENEMY=pygame.USEREVENT
CREATE_ENEMY_TIME=700
# 设置子弹事件、创建子弹所需时间
EVENT_BULLET=pygame.USEREVENT+1
CREATE_BULLET_TIME=500
# 总分值
TOTAL_SCORE=0
class PlaneGame:
"""飞机大战主类"""
def __init__(self):
# 初始化pygame模块
pygame.init()
# 初始化英雄生命
self.life=True
# 初始化开始菜单图片位置
self.startImgRect=pygame.Rect(0,0,0,0)
# 初始化游戏窗口
self.screen = pygame.display.set_mode(SCREEN_PRO.size)
# 初始化游戏时钟
self.clock = pygame.time.Clock()
# 初始化爆炸敌机对象
self.boomEnemy= {"boomEnemy":None,"times":0}
# 初始化精灵及精灵组相关
self.__create_sprites()
# 初始化 创建敌机定时器
pygame.time.set_timer(EVENT_ENEMY,CREATE_ENEMY_TIME)
# 初始化 创建子弹定时器
pygame.time.set_timer(EVENT_BULLET,CREATE_BULLET_TIME)
def __create_sprites(self):
# 创建背景精灵
bg1 = BackGround()
bg2 = BackGround(True)
# 创建精灵组,并放入背景精灵
self.bgGroup = pygame.sprite.Group(bg1, bg2)
# 创建敌机精灵组
self.enemyGroup=pygame.sprite.Group()
# 创建英雄战机
self.hero=Hero()
self.heroGroup=pygame.sprite.Group(self.hero)
# 创建子弹精灵组
self.bulletGroup=pygame.sprite.Group()
# 创建分值
self.font=FontSprites()
self.fontGroup=pygame.sprite.Group(self.font)
def start_game(self):
# 游戏循环开始
while True:
# 爆炸图片持续1秒
self.__enemy_boom()
# 设置刷新频率
self.clock.tick(TICK_TIMES)
# 监听事件
self.__event_handler()
if not self.life:
continue
# 碰撞检测
self.__check_collide()
# 更新精灵组
self.__update_group()
# 更新屏幕
pygame.display.update()
def __enemy_boom(self):
# 爆炸图片持续1秒
if self.boomEnemy["boomEnemy"] is not None:
enemyTimes = self.boomEnemy["times"]
self.boomEnemy["times"] = enemyTimes + 1
if enemyTimes > TICK_TIMES:
self.boomEnemy["boomEnemy"] = None
self.boomEnemy["times"] = 0
def __event_handler(self):
global TOTAL_SCORE
for event in pygame.event.get():
# 判断退出游戏
if event.type==pygame.QUIT:
PlaneGame.game_over()
# 监听鼠标点击开始游戏
if event.type == pygame.MOUSEBUTTONDOWN:
x, y = event.pos
print("鼠标点击了...x:%d ,y:%d,原始图片位置,x:%d,y:%d"%(x,y,self.startImgRect.x,self.startImgRect.y))
if self.startImgRect.collidepoint(x, y):
self.life = True
TOTAL_SCORE=0
pygame.quit()
PlaneGame().start_game()
# 监听英雄死亡
if not self.life:
continue
# 监听到创建敌机
if event.type==EVENT_ENEMY:
enemy=EnemyPlane()
enemy2 = EnemyPlane2()
self.enemyGroup.add(enemy)
self.enemyGroup.add(enemy2)
# 监听子弹
if event.type == EVENT_BULLET:
self.hero.fire(self.bulletGroup)
keyPressed=pygame.key.get_pressed()
if keyPressed[pygame.K_RIGHT]:
self.hero.update(3)
if keyPressed[pygame.K_LEFT]:
self.hero.update(-3)
def __update_group(self):
# 绘制背景
self.bgGroup.update()
self.bgGroup.draw(self.screen)
# 绘制敌机
self.enemyGroup.update()
self.enemyGroup.draw(self.screen)
boomEnemy=self.boomEnemy["boomEnemy"]
if boomEnemy:
# 绘制爆炸图片
self.screen.blit(boomEnemy.boomImg,(boomEnemy.rect.x,boomEnemy.rect.y))
# 绘制英雄
self.heroGroup.draw(self.screen)
# 绘制子弹
self.bulletGroup.update()
self.bulletGroup.draw(self.screen)
# 绘制分值
self.fontGroup.update()
self.screen.blit(self.font.textShow,(0,0))
# 更新游戏结束
if not self.life:
self.load_game_over()
def __check_collide(self):
global TOTAL_SCORE
# 子弹碰撞敌机,都销毁
dictG=pygame.sprite.groupcollide(self.bulletGroup,self.enemyGroup,True,True)
if dictG:
self.font.kill()
# 分值加1
TOTAL_SCORE += 1
self.font=FontSprites()
self.font.fontValue=TOTAL_SCORE
self.fontGroup.add(self.font)
# 爆炸敌机对象
enemy=list(dictG.values())[0][0]
# 将爆炸的敌机记录
self.boomEnemy["boomEnemy"]=enemy
# 播放音乐
enemy.plan_die()
# # 敌机碰撞英雄战机,都销毁
spriteList=pygame.sprite.spritecollide(self.hero,self.enemyGroup,True)
# 碰撞的敌机列表
if len(spriteList)>0:
# self.hero.kill()
self.life=False
self.load_game_over()
def load_game_over(self):
# 背景音乐停止
pygame.mixer.music.stop()
# 停止全部音效
pygame.mixer.stop()
# 加载游戏结束
img = pygame.image.load("./img/gameover1.png")
self.screen.blit(img, (0, 30))
# 加载开始菜单
startImg = pygame.image.load("./img/restart.png")
self.startImgRect=self.screen.blit(startImg, (150,img.get_rect().height+30))
# 加载文字
gameOverFont=FontSprites(40)
gameOverFont.fontValue=TOTAL_SCORE
gameOverFont.update((255,0,0))
self.screen.blit(gameOverFont.textShow,(200,250))
@staticmethod
def game_over():
print("游戏结束!!!!")
# 游戏退出
pygame.quit()
exit()
if __name__ == '__main__':
planGame = PlaneGame()
planGame.start_game()
程序猿与投资生活实录已改名为 程序猿知秋,WX同款,欢迎关注!