• Python飞机大战实例有感——pygame如何实现“切歌”以及多曲重奏?


    pygame如何实现“切歌”以及多曲重奏?

    昨天晚上研究了好久pygame的音乐混合器mixer,出了很多问题后最终成功,不过学习本来也不可能一帆风顺的吗,下面我就来讲一讲我遇到的问题。

    一、pygame实现切歌

    初始化路径

    # 导库,需安装
    import pygame
    # 把路径赋值分别给三个变量,以便之后加载。
    music_file_path1 = "./sound/background.mp3"
    music_file_path2 = "./sound/background1.mp3"
    music_file_path3 = "./sound/dead.mp3"
    # 初始化混合器
    pygame.mixer.init()
    

    尝试一

    开始尝试直接加载新的音乐,想着循环里有调用play方法,是不是直接调用load方法修改路径,就能播放其他音乐了呢?

    # 加载初始背景音乐
    pygame.mixer.music.load(music_file_path1)
    while True:
      pygame.mixer.music.play()
      if 死亡:
        # 切换死亡音乐
        pygame.mixer.music.load(music_file_path3)
        for 检测按键
        	if 按键:
            	#重开游戏,并切换成初始背景音乐
            	pygame.mixer.music.load(music_file_path1)
      if 达成条件进入第二关:
        # 切换为第二关背景音乐
      	pygame.mixer.music.load(music_file_path2)
      # 延时50ms之后进入下层循环
      pygame.time.delay(50)
    

    失败、、、没有完成切换音乐,只有播放初始音乐,切换的部分是静音的。

    尝试二

    是不是可以考虑多开几个线程呢?之前java我就这么捣鼓过,这个算是写的比较乱的,主要还是不懂的太多。

    # 导库,系统自带的。
    import threading
    # 定义一个函数以便线程来执行。
    def bgm(music_file_path):
      pygame.mixer.music.load(music_file_path)
      pygame.mixer.music.play()
    
    ...
    
    # 新建3个子线程
    thread1 = threading.Thread(bgm(music_file_path1))
    thread2 = threading.Thread(bgm(music_file_path2))
    thread3 = threading.Thread(bgm(music_file_path3))
    # 启动线程1
    thread1.strat()
    while True:
      if 死亡:
        # 切换死亡音乐
        thread3.strat()
        for 检测按键
        	if 按键:
            	#重开游戏,并切换成初始背景音乐
            	thread1.strat()
      if 达成条件进入第二关:
        # 切换为第二关背景音乐
      	thread2.strat()
      # 延时50ms之后进入下层循环
      pygame.time.delay(50)
    

    同样失败了,刚开始,运行的就是死亡时候的背景音乐,也就是说,只有最后加载的那个起作用了,在具体点说,此时的thread1, thread2, thread3已经是完全相同的了。

    尝试三

    加了许多改变,bgm函数里加了初始化mixer,线程改为了在循环里运行匿名线程。(因为直接在循环里thread1.start()的话,会报错,说线程只能启动一次。)

    # 导库,系统自带的。
    import threading
    # 定义一个函数以便线程来执行。
    def bgm(music_file_path):
      pygame.mi
      pygame.mixer.music.load(music_file_path)
      pygame.mixer.music.play()
    
    ...
    
    while True:
      # 默认音乐
      threading.Thread(bgm(music_file_path1)).start()
      if 死亡:
        # 切换死亡音乐
        threading.Thread(bgm(music_file_path3)).start()
        for 检测按键
        	if 按键:
            	#重开游戏,并切换成初始背景音乐
            	threading.Thread(bgm(music_file_path1)).start()
      if 达成条件进入第二关:
        # 切换为第二关背景音乐
      	threading.Thread(bgm(music_file_path2)).start()
      # 延时50ms之后进入下层循环
      pygame.time.delay(50)
    

    现在看也觉得怎么看怎么错的,不过这倒是给我提供了一个思路,只要每次切换音乐的时候重新初始化一下mixer就能播放新的了。

    成功

    尝试不止三次,我只是找了3个可能比较有代表性的例子,希望大家能从中吸取经验,下面,我将展示成功的代码。

    # 定义3个变量来表示是否在播放哪首音乐。
    sound1, sound2, sound3 = True, True, True
    # 加载初始背景音乐
    pygame.mixer.music.load(music_file_path1)
    pygame.mixer.music.play()
    while True:
      if 死亡:
        # 切换死亡音乐
        # 通过sound的True, False的值的改变,控制只有第一次进入这个判断条件的时候才会初始化混合器。防止出现每50ms加载一次音乐的开头50ms的情况。
        if sound3:
        	pygame.mixer.init()
        	pygame.mixer.music.load(music_file_path3)
            sound3 = False
            sound1, sound2 = True, True
        if pygame.mixer.get_busy != 1:
      		pygame.mixer.music.play()
        for 检测按键
        	if 按键:
            	#重开游戏,并切换成初始背景音乐
            	if sound1:
                  pygame.mixer.init()
                  pygame.mixer.music.load(music_file_path1)
                  sound1 = False
                  sound2, sound3 = True, True
                if pygame.mixer.get_busy != 1:
                  pygame.mixer.music.play()
      if 达成条件进入第二关:
        # 切换为第二关背景音乐
      	if sound2:
          pygame.mixer.init()
          pygame.mixer.music.load(music_file_path1)
          sound2 = False
          sound1, sound3 = True, True
        if pygame.mixer.get_busy != 1:
          pygame.mixer.music.play()
      # 延时50ms之后进入下层循环
      pygame.time.delay(50)
    

    最终成功!

    总结

    二、如何在python多线程顺序执行的情况下实现音乐和音效同时播放?

    这个其实挺简单的,就是我开始的时候被坑了,被坑的原因现在也不太清楚。。

    尝试一

    # 飞机的发射子弹类
    def launch_bullet:
      sound = pygame.mixer.Sound("./sound/bullet.wav")
      sound.play()
    # 敌机的被击毁判断
    if 敌机被击毁:
      sound = pygame.mixer.Sound("./sound/boom.wav")
      sound.play()
    

    真的很简单的啊,就这样就应该可以了啊,结果它报错了,说unable to open file "./sound/bullet.wav",无奈,只能换方法。。

    尝试二

    经过查阅发现了winsound这个模块,然后,testing...

    # 导入模块,系统自带的
    import winsound
    # 飞机的发射子弹类
    def launch_bullet:
      winsound.PlaySound("./sound/bullet.wav", SND_NOSTOP)
    # 敌机的被击毁判断
    if 敌机被击毁:
      winsound.PlaySound("./sound/boom.wav", SND_NOSTOP)
    

    然后成功感受到了单线程的恶意。。。

    尝试三

    于是就用多线程吧,结合java的经验,一定手到擒来的吧!

    # 再次尝试使用threading
    import threading
    import winsound
    # 飞机的发射子弹类
    def launch_bullet:
      # 直接匿名函数先测试走起!
      threading.Thread(winsound.PlaySound("./sound/bullet.wav", SND_NOSTOP)).start()
    # 敌机的被击毁判断
    if 敌机被击毁:
      threading.Thread(winsound.PlaySound("./sound/boom.wav", SND_NOSTOP)).start()
    

    有点错愕地发现失败了,跟之前一次的尝试结果一样,然后才知道原来python的多线程因为什么原因我忘了,还是顺序执行的。

    尝试四

    在网上了解到了多进程可以实现并发访问,于是

    # 系统自带
    import multiprocessing
    import winsound
    # 飞机的发射子弹类
    def launch_bullet:
      multiprocessing.freeze__support()
      p = multiprocessing.Process(winsound.PlaySound("./sound/bullet.wav", SND_NOSTOP))
      p.start()
    # 敌机的被击毁判断
    if 敌机被击毁:
      multiprocessing.freeze__support()
      p = multiprocessing.Process(winsound.PlaySound("./sound/boom.wav", SND_NOSTOP))
      p.start()
    

    然后每射一发子弹,就给我打开一个新窗口,我。。。。

    成功

    最后决定还是再给Sound一个机会,他文档上不是说只能加载wav和ogg吗?wav失败了,我再重新找一下ogg的素材吧。然后就成功了。就成功了。。。我捣鼓半天,结果是素材的原因。

    # 飞机的__init__方法里
    	self.sound = pygame.mixer.Sound("./sound/bullet.ogg")
    # 飞机的发射子弹类
    def launch_bullet:
      self.sound.play()
    # 敌机的__init__方法里
    	self.sound = pygame.mixer.Sound("./sound/get_score.ogg")
    # 敌机的被击毁判断
    if 敌机被击毁:
      self.sound.play()
    

    具体第一次尝试为何失败我们仍未可知,也许是文件太大了?

    总结

    真的是一次印象挺深刻的经历,深刻到我这篇全文都是没看之前的代码敲出来的,甚至学了个新单词mixer是混合器的意思。程序源码我会放在我的github上。

    飞机大战源码

  • 相关阅读:
    mysql官网下载yum
    zookeeper和kafka的leader和follower
    查看目标端口是否被占用
    scala中的val,var和lazy
    scala的异常处理try catch
    Navicat总是提示主键不存在问题
    idea常用快捷键
    wiremock技术入门
    Liunx常用操作(11)-VI编辑器-末行模式命令
    Liunx常用操作(十)-VI编辑器-命令模式命令
  • 原文地址:https://www.cnblogs.com/zhangA/p/10954714.html
Copyright © 2020-2023  润新知