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上。

飛機大戰源碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM