Python飛機大戰實例有感——pygame如何實現「切歌」以及多曲重奏?

pygame如何實現「切歌」以及多曲重奏?

昨天晚上研究了很久pygame的音樂混合器mixer,出了不少問題後最終成功,不過學習原本也不可能一路順風的嗎,下面我就來說一講我遇到的問題。python

1、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方法修改路徑,就能播放其餘音樂了呢?git

# 加載初始背景音樂
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)

失敗、、、沒有完成切換音樂,只有播放初始音樂,切換的部分是靜音的。github

嘗試二

是否是能夠考慮多開幾個線程呢?以前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)

最終成功!線程

總結

2、如何在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上。

飛機大戰源碼

相關文章
相關標籤/搜索