草庐IT

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

程序猿知秋 2023-04-22 原文

最终程序图

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

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

有关Python —— 给女儿写个雷霆战机的更多相关文章

  1. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  2. Python 相当于 Perl/Ruby ||= - 2

    这个问题在这里已经有了答案:关闭10年前。PossibleDuplicate:Pythonconditionalassignmentoperator对于这样一个简单的问题表示歉意,但是谷歌搜索||=并不是很有帮助;)Python中是否有与Ruby和Perl中的||=语句等效的语句?例如:foo="hey"foo||="what"#assignfooifit'sundefined#fooisstill"hey"bar||="yeah"#baris"yeah"另外,类似这样的东西的通用术语是什么?条件分配是我的第一个猜测,但Wikipediapage跟我想的不太一样。

  3. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

    什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

  4. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  5. python - 如何读取 MIDI 文件、更改其乐器并将其写回? - 2

    我想解析一个已经存在的.mid文件,改变它的乐器,例如从“acousticgrandpiano”到“violin”,然后将它保存回去或作为另一个.mid文件。根据我在文档中看到的内容,该乐器通过program_change或patch_change指令进行了更改,但我找不到任何在已经存在的MIDI文件中执行此操作的库.他们似乎都只支持从头开始创建的MIDI文件。 最佳答案 MIDIpackage会为您完成此操作,但具体方法取决于midi文件的原始内容。一个MIDI文件由一个或多个音轨组成,每个音轨是十六个channel中任何一个上的

  6. 「Python|Selenium|场景案例」如何定位iframe中的元素? - 2

    本文主要介绍在使用Selenium进行自动化测试或者任务时,对于使用了iframe的页面,如何定位iframe中的元素文章目录场景描述解决方案具体代码场景描述当我们在使用Selenium进行自动化测试的时候,可能会遇到一些界面或者窗体是使用HTML的iframe标签进行承载的。对于iframe中的标签,如果直接查找是无法找到的,会抛出没有找到元素的异常。比如近在咫尺的例子就是,CSDN的登录窗体就是使用的iframe,大家可以尝试通过F12开发者模式查看到的tag_name,class_name,id或者xpath来定位中的页面元素,会抛出NoSuchElementException异常。解决

  7. python ffmpeg 使用 pyav 转换 一组图像 到 视频 - 2

    2022/8/4更新支持加入水印水印必须包含透明图像,并且水印图像大小要等于原图像的大小pythonconvert_image_to_video.py-f30-mwatermark.pngim_dirout.mkv2022/6/21更新让命令行参数更加易用新的命令行使用方法pythonconvert_image_to_video.py-f30im_dirout.mkvFFMPEG命令行转换一组JPG图像到视频时,是将这组图像视为MJPG流。我需要转换一组PNG图像到视频,FFMPEG就不认了。pyav内置了ffmpeg库,不需要系统带有ffmpeg工具因此我使用ffmpeg的python包装p

  8. Python 刷Leetcode题库,顺带学英语单词(31) - 2

    ValidPalindromeGivenastring,determineifitisapalindrome,consideringonlyalphanumericcharactersandignoringcases. [#125]Example:"Aman,aplan,acanal:Panama"isapalindrome."raceacar"isnotapalindrome.Haveyouconsiderthatthestringmightbeempty?Thisisagoodquestiontoaskduringaninterview.Forthepurposeofthisproblem

  9. python - 是否可以使用 Ruby 或 Python 禁用 anchor /引用来发出有效的 YAML? - 2

    是否可以在PyYAML或Ruby的Psych引擎中禁用创建anchor和引用(并有效地显式列出冗余数据)?也许我在网上搜索时遗漏了一些东西,但在Psych中似乎没有太多可用的选项,而且我也无法确定PyYAML是否允许这样做.基本原理是我必须序列化一些数据并将其以可读的形式传递给一个不是真正的技术同事进行手动验证。有些数据是多余的,但我需要以最明确的方式列出它们以提高可读性(anchor和引用是提高效率的好概念,但不是人类可读性)。Ruby和Python是我选择的工具,但如果有其他一些相当简单的方法来“展开”YAML文档,它可能就可以了。 最佳答案

  10. .net - .NET 将如何影响 Python 和 Ruby 应用程序? - 2

    我很好奇.NET将如何影响Python和Ruby应用程序。用IronPython/IronRuby编写的应用程序是否会非常特定于.NET环境,以至于它们实际上将变得特定于平台?如果他们不使用任何.NET功能,那么IronPython/IronRuby相对于非.NET同类产品的优势是什么? 最佳答案 我不能说任何关于IronRuby的东西,但是大多数Python实现(如IronPython、Jython和PyPy)都试图尽可能忠实于CPython实现。不过,IronPython正在迅速成为这方面的佼佼者之一,并且在PlanetPyth

随机推荐