本節書摘來自異步社群《android 3d遊戲開發技術寶典——opengl es 2.0》一書中的第2章,第2.1節遊戲中的音效,作者 吳亞峰,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視
2.1 遊戲中的音效
android 3d遊戲開發技術寶典——opengl es 2.0
一款好遊戲,除了具備優質的畫面和較高的可玩性之外,還應該有出色的音效。音效一般指的是遊戲中發生特定行為或進行特定操作時播放的效果音樂或為了渲染整體氣氛播放的背景音,如遠處隆隆的炮聲、怪物死亡的慘叫聲、由遠而近的腳步聲等。
通過開發人員精心準備的聲音特效,結合遊戲的場景,可以渲染出一種緊張刺激的氛圍,使玩家産生身臨其境的感覺。這就像電影中的聲音特效一樣,假如沒有了合适的音效,那麼遊戲和電影一樣,真實感會大打折扣。
提示 按照作用的不同,可以将音效劃分為即時音效和背景音樂。兩種音效在android中的實作技術是不同的,本節将向讀者詳細介紹兩種音效在android中的具體實作。
2.1.1 遊戲中的即時音效
遊戲中有時需要根據情況播放即時音效,如槍炮聲、碰撞聲等。即時音效的特點是短暫、可以重複、可以同時播放。由于android提供的mediaplayer(媒體播放器)會占用大量的系統資源,而且播放時還需要進行緩沖,有較大的時延,是以使用mediaplayer無法實作即時音效。
android系統的設計者也考慮到了這個問題,為即時音效的實作提供了一個專門的類——soundpool。soundpool類用于管理和播放應用程式中的聲音資源,使用該類時首先需要通過該類将聲音資源加載到記憶體中,然後在需要即時音效的地方播放即可,其幾乎沒有時延,可以滿足遊戲實時性的需要。
提示 由于soundpool設計的初衷是用于無時延地播放遊戲中的短促音效,是以實際開發中應該隻将長度小于7s的聲音資源放進soundpool,否則可能加載失敗或記憶體占用過大。
soundpool類的構造器及常用方法如表2-1所列。

2.1.2 即時音效的一個案例
了解了soundpool類的基本操作方法之後,接下來就可以開發遊戲中用到的即時音效了。本小節将向讀者展示一個播放和停止即時音效的簡單案例,其主要功能為,通過soundpool聲音池技術來實作一個即時音效的播放和停止,運作效果如圖2-1和圖2-2所示。
了解了本案例的運作效果後,接下來對其具體開發步驟進行介紹,具體如下所列。
(1)首先在eclipse中建立名稱為sample2_1的項目,然後在項目目錄下的res檔案夾下建立raw檔案夾。接着将需要被播放的短促音效對應的音頻檔案musictest.ogg複制到raw檔案夾下,如圖2-3所示。
提示 一般在android手機平台上使用的即時音效檔案越小越好,這有助于提高遊戲的整體性能。對于同一個音效檔案,在不改變其時長的情況下,可以采用降低采樣率(如降低到16kbit/s)或由立體聲改為單聲道的方式來縮小體積。
(2)準備好聲音資源後,接下來進行本案例中sample2_1_activity類的開發。該類中使用了聲音池技術實作了即時音效的播放,其代碼如下。
第6-8行為聲明所用到的soundpool和hashmap對象的引用,其中soundpool用來加載、播放、停止音效;hashmap用來管理音效id。currstreamid為目前正播放的streamid,用于對正在播放的音效進行管理。
第14行調用initsoundpool方法來對聲音池進行初始化。
第15-37行為播放和停止按鈕添加監聽器,并在單擊操作時顯示toast進行提示。
第39-44行為初始化聲音池的方法,其首先建立soundpool對象,然後加載音效檔案到聲音池并将生成的音效id存儲進hashmap中。
第46-59行為播放聲音的方法,其中調用soundpool的play方法來實作即時音效的播放。
提示 通過以上的案例,讀者可以看出使用soundpool播放即時音效是非常簡單的。今後的遊戲開發中,隻要是遊戲中的即時音效都應該用此方式來實作。
2.1.3 背景音樂播放技術
背景音樂也可以采用前一小節的聲音池技術,在播放背景音樂的時候,隻需要把loop播放次數參數設定成-1進行無限循環即可。但由于soundpool隻适合播放不大于7秒的音效檔案,限制較大。而背景音樂對時延并不敏感,是以在實際的遊戲開發中,時長較長的背景音樂一般采用媒體播放器(mediaplayer)來進行播放。
要想很好地使用mediaplayer進行音/視訊檔案的播放,首先必須要熟悉mediaplayer的生命周期。這樣不僅有利于開發人員開發出更加合理的代碼,而且可以達到充分利用系統資源的目的。
1.mediaplayer的生命周期
mediaplayer的生命周期包括10種狀态,每種狀态下可以調用相應的方法來實作音/視訊檔案的管理或播放。其各個狀态及狀态間的關系可以用一個簡單的流程圖來表示,如圖2-4所示。
idle 狀态。
使用new方法建立一個mediaplayer對象或者調用了其reset方法時,該mediaplayer對象處于idle狀态。
但通過兩種不同方式進入的idle狀态還是有些差別的,主要展現為:如果在這個狀态下調用了getduration等方法,若是通過reset方法進入idle狀态的話會觸發onerrorlistener.onerror,并且mediaplayer會進入error狀态;如果是新建立的mediaplayer對象,則并不會觸發onerror,也不會進入error狀态。
end狀态。
通過release方法可以進入end狀态,隻要mediaplayer對象不再被使用,就應當盡快将其通過release方法釋放掉,以釋放其占用的軟、硬體資源,這其中有些資源是互斥的(相當于臨界資源)。如果mediaplayer對象進入了end狀态,則不會再進入其他任何狀态了。
initialized 狀态。
這個狀态比較簡單,mediaplayer調用setdatasource方法就進入了initialized狀态,表示此時要播放的檔案已經設定好了。
prepared 狀态。
初始化完成之後還需要通過調用prepare或prepareasync方法進行準備,這兩個方法一個是同步的,一個是異步的。隻有進入了prepared狀态,才表明mediaplayer到目前為止都工作正常,可以進行音樂檔案的播放。
preparing 狀态。
這個狀态比較容易了解,主要是與prepareasync異步準備方法配合,如果異步準備完成,會觸發onpreparedlistener.onprepared,進而進入prepared狀态。
started 狀态。
mediaplayer準備完成後,通過調用start方法,将進入started狀态。所謂started狀态,也就是播放中狀态,開發中可以使用isplaying方法測試mediaplayer是否處于started狀态。
如果播放完畢,而又設定了循環播放,則mediaplayer仍然會處于started狀态。類似的,如果在該狀态下mediaplayer調用了seekto或者start方法均可以讓mediaplayer停留在started狀态。
paused 狀态。
started狀态下調用pause方法可以暫停播放,進而進入paused狀态。mediaplayer暫停後再次調用start方法則可以繼續進行播放,并轉到started狀态。暫停狀态時可以調用seekto方法,這是不會改變狀态的。
stop 狀态。
started或paused狀态下均可調用stop方法停止播放并進入stop狀态,而處于stop狀态的mediaplayer要想重新播放,需要通過調用prepareasync或prepare方法傳回到先前的prepared狀态重新開始才可以。
playbackcompleted狀态。
檔案正常播放完畢,而又沒有設定循環播放的話就進入該狀态,并會觸發oncompletionlistener接口中的oncompletion方法。此時可以調用start方法重新從頭播放檔案,也可以調用stop方法停止播放,或者調用seekto方法來重新定位播放位置。
error狀态。
由于某種原因mediaplayer出現了錯誤,則會觸發onerrorlistener.onerror回調方法,此時mediaplayer即進入error狀态。及時捕捉并妥善處理這些錯誤是很重要的,這可以幫助應用程式及時釋放相關的軟、硬體資源,也可以改善使用者體驗。
如果mediaplayer進入了error狀态,可以通過調用reset方法來恢複,使得mediaplayer重新傳回到idle狀态。
提示 從上述對生命周期的介紹中可以看出,某些情況發生時mediaplayer會回調特定監聽接口中的事件處理方法。若讀者在開發中希望使用回調,則需要首先向mediaplayer注冊實作了指定監聽接口的監聽器。例如,可以使用setonerrorlistener方法注冊實作了onerrorlistener接口的監聽器,當mediaplayer進入error狀态時監聽器中的onerror方法就會被回調。
2.audiomanager類
audiomanager類在android系統中主要用來進行音/視訊播放時的音量控制,使用時的基本步驟如下所列。
首先可以調用activity對象的getsystemservice(context.audio_service)方法擷取audiomanager對象。
然後再調用audiomanager類中的相關方法進行音量控制。
audiomanager類中的常用方法如表2-2所列。
提示 mediaplayer類還可以對視訊檔案進行操作,由于本書隻介紹與遊戲音效相關的功能,是以不再對其進行介紹,有興趣的讀者可以自行查閱相關資料。
2.1.4 簡易音樂播放器的實作
了解了mediaplayer和audiomanager類的基本操作方法之後,就可以對遊戲的背景音樂功能進行開發了。本小節将通過這兩個類來實作一個簡易的音樂播放器,其主要功能為對手機sd卡中的音樂檔案進行播放,運作效果如圖2-5所示。
提示 圖2-5中從左到右分别為案例運作後,依次單擊“播放音樂”按鈕、“暫停播放”按鈕、“增大音量”按鈕後的效果圖。停止播放音樂和減小音量的效果與圖2-5中的已有效果類似,這裡沒有給出,請讀者自行運作本案例進行檢視。
了解了案例的具體運作效果後,接下來就介紹案例的具體開發步驟,具體如下所列。
(1)首先需要準備好要播放的音樂檔案,本案例中使用的是著名的高山流水古曲,檔案名為“gsls.mp3”。準備完音樂檔案後,将該音樂檔案通過ddms導入到模拟器或真機的sd卡中,如圖2-6所示。
提示 高山流水曲的音樂資源檔案見随書CD光牒中源代碼/第2章目錄下的gsls.mp3。導入時直接用滑鼠光标将檔案拖曳到ddms下“file explorer”面闆中的“sdcard”目錄下即可。
(2)音樂資源在sd卡中放置完成後,接下來進行本案例中sample2_2_activity類的開發。該類使用了mediaplayer實作了背景音樂的播放,具體代碼如下。
第6-10行為聲明需要用到的mediaplayer和audiomanager對象的引用、最大音量值、目前音量值及每次調整的音量幅度等。
第16-23行擷取了mediaplayer類對象的引用,設定了音樂資源的路徑,并調用prepare方法進入了準備狀态。
第24-29行擷取了audiomanager類對象的引用,同時擷取了音樂播放的最大音量值,并設定每次調整的音量幅度為最大值的1/6。
第30-98行分别擷取了開始按鈕、暫停按鈕、停止按鈕以及增大和減小音量按鈕對象的引用,并分别為這些按鈕添加了監聽器。這樣在使用者單擊這些按鈕時程式就能完成指定的功能了,例如,按下開始按鈕開始播放音樂、按下減小音量按鈕減小播放音量等。