Python —— 给女儿写个雷霆战机

时间:2023-04-07 17:15:37

最终程序图

Python —— 给女儿写个雷霆战机

程序分析——精灵对象与功能分析

  • 基本精灵对象,继承 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同款,欢迎关注!