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