讀古今文學網 > 父與子的編程之旅:與小卡特一起學Python > 19.7 更多聲音 >

19.7 更多聲音

球碰到球拍時會發出 hit 聲音,下面再增加另外一些聲音。我們將為下面這些情況添加聲音:

  • 球碰到兩邊的牆時;

  • 球碰到頂邊而且玩家得分時;

  • 玩家漏球,球碰到底邊時;

  • 新的一條命開始時;

  • 遊戲結束時。

首先需要為所有這些情況創建聲音對象。可以把相應代碼放在 pygame.mixer.init 之後但在 while 循環之前的任何位置上。

hit_wall = pygame.mixer.Sound(\"hit_wall.wav\")hit_wall.set_volume(0.4)get_point = pygame.mixer.Sound(\"get_point.wav\")get_point.set_volume(0.2)splat = pygame.mixer.Sound(\"splat.wav\")splat.set_volume(0.6)new_life = pygame.mixer.Sound(\"new_life.wav\")new_life.set_volume(0.5)bye = pygame.mixer.Sound(\"game_over.wav\")bye.set_volume(0.6)  

這裡我選擇了不同的音量,這只是為了看看哪種音量聽起來最合適。可以按你的喜好來設置音量。另外要記住,所有聲音文件都要複製到保存代碼的位置上。這些聲音都可以在 examplessounds 文件夾或者本書網站上找到。

現在需要在發生這些事件時的相應代碼中增加 play 方法。只要碰到窗口左右兩邊就應當發出 hit_wall 聲音。這個事件在球的 move 法中檢測,我們還要讓球的 x 速度反向(使球在兩邊「反彈」)。在原來的代碼清單 18-5 中,這是第 14 行 (if self.rect.left < 0 or self.rect.right > screen.get_width:)。

所以,在反向時還可以播放聲音。代碼如下:

可以對 get_point 聲音做同樣的處理。在球的 move 方法下面一點點的地方,我們檢測了球是否碰到窗口頂邊。在這裡要讓球反彈,並為玩家加 1 分。現在還要播放一個聲音。新代碼如下:

增加這些代碼後,試著運行程序,看看有什麼效果。

接下來繼續增加代碼,當玩家漏球而丟掉一條命時會播放一個聲音。這個事件在主 while 循環中檢測,也就是原來的代碼清單 18-5 中的第 63 行(if myBall.rect.top >= screen.get_rect.bottom:)。只需再增加這樣一行代碼:

還可以在新的一條命開始時增加一個聲音。這種情況在代碼清單 18-5 的最後 3 行代碼發生(也就是 else 塊中)。這一次我們會在開始新的一條命之前留出一點時間來播放音效:

else:pygame.time.delay(1000)new_life.playmyBall.rect.topleft = [50, 50]screen.blit(myBall.image, myBall.rect)pygame.display.flippygame.time.delay(1000)  

與原來的程序不同,現在不是等待 2 秒,我們會等待 1 秒(1000 毫秒),再播放聲音,然後在開始下一輪之前再等待 1 秒。可以試試看,聽聽效果如何。

這裡還要增加一個音效:遊戲結束時需要播放一個聲音。這種情況在代碼清單 18-5 的第 65 行發生(if lives == 0:)。在這裡增加下面這行代碼,就會播放 bye 聲音:

if lives == 0:bye.play  

試試看效果如何。

唉呀!我們忘了一件事。播放 bye 聲音和 splat 聲音的代碼放在主 while 循環中,Pygame 窗口關閉前它們是不會停止的,所以只要 while 循環在運行,聲音就會反覆播放!需要增加一些代碼來確保它只會播放一次。

這裡就要使用前面定義的 done 變量了,它會告訴我們遊戲何時結束。可以把代碼修改成下面這樣:

試試看,確認程序能正常工作。

嗯……這個問題可能需要稍稍考慮一下。這裡通過 done 變量來告訴我們遊戲何時結束,利用這一點,我們能夠知道什麼時候播放 bye 聲音,以及什麼時候顯示最後的分數消息。不過這球在幹嘛?

儘管球已經到達窗口底邊,但它仍然在不停地移動!球在繼續向下走,越走越遠,沒有什麼來阻止它,所以它的 y 值會越來越大。雖然它在屏幕底邊的「下面」,我們看不到,但是仍然能夠聽到它的聲音!球仍在移動,所以當球的 x 值變得足夠大或者足夠小時,它還會在「左右兩邊」上反彈。這種情況發生在 move 方法中,只要 while 循環在運行,這個方法就會一直運行。

怎麼解決這個問題呢?我們可以採用以下幾種辦法。

  • 遊戲結束時把球的速度設置為 [0,0] 來阻止球繼續移動。

  • 查看球是否在窗口底邊以下,如果是,就不再播放 hit_wall 聲音。

  • 檢查 done 變量,如果遊戲已經結束就不再播放 hit_wall 聲音。

我選擇了第二種方法,不過上面這幾種方法都能奏效。你可以選擇以上的任意一種方法,修改你的代碼來解決這個問題。