第一步 - 用 Wwise 來制作聲音
在這個 Unity 工程的根目錄(里面有 Assets 目錄)下,新建並保存一個 Wwise 工程,在這個過程中要保證選中了合適的平台,本例中用的是 Mac 平台。工程新打開的時候會啟動 Designer 布局( F5 鍵)。
前往 Project Explorer (工程瀏覽器)視圖的 Audio 選項卡,它默認位於 Wwise Authoring Editor (譯注:即 Wwise Authoring Tool, Wwise 設計工具)的左上區域。在 Actor-Mixer Hierarchy (角色混音器層級結構) 中右擊叫作 Default Work Unit (默認工作單元) 的對象並選擇 Import Audio Files ... 。這個 Actor-Mixer Hierarchy 是 Wwise 中的一個結構,它包含了游戲項目中所有的音效。從示例素材中選中並添加 rifle_shot.wav 這個文件,在 Object Type/Action 列選中 Sound SFX (音效對象)。小技巧:一個很有用的導入操作快捷鍵是 Shift+I 。
這是我們要用在槍聲上的唯一文件。選中 rifle_shot SFX 對象后,你可以按下空格鍵試聽一下效果。編輯器底部 Transport Control (播放控制)的標題欄中會顯示選中對象的名稱。你還會注意到選中 SFX 對象后,界面會自動更新,顯示出這個對象的 Sound Property Editor (屬性編輯器)和 Contents Editor (內容編輯器)視圖。讓我們運用 Sound Property Editor 來給步槍槍聲一點隨機變化吧!
讓 rifle_shot 對象保持選中狀態,雙擊 Pitch 屬性下的小圓圈圖標,接着 Randomizer (隨機化器)視圖打開了。啟用 Randomizer,並將 Min 和 Max 值分別設為-150和150,單位是音分,半音音階中相鄰半音間的音程是100音分,所以我們相當於給 rifle_shot 對象的 Pitch 屬性增加了正負1.5個半音的隨機化范圍。這樣做能讓聲音更多樣一些,避免產生令人心煩的重復音效。
再試聽幾次 rifle_shot SFX 對象,你會聽到音高有了隨機變化。恭喜,你已經學會使用 Wwise 中的第一塊積木 - SFX 對象了!
事件:連接 Unity 和 Wwise
我們現在已經能在 Wwise 中播放聲音了,但如何才能在 Survival Shooter 游戲中當玩家開槍時播放 rifle_shot 這個 SFX 對象呢?答案是事件(Event),這是讓游戲觸發 Wwise 對象來做事的一種方法。我們現在要添加一個名為 PlayerShoot 的事件,每當玩家的武器打響時,游戲代碼就會調用這個事件。
切換到 Project Explorer 視圖中的 Events 選項卡。右擊 Events 文件夾中的 Default Work Unit 並選擇 New Child > Play 。給這個新創建的 Event 取名為 PlayerShoot 。注意:Event 名稱的字符串必須要和文中所述吻合 - 我們要從游戲代碼里依據名稱來調用這個 Event。
你可能已經注意到了 PlayerShoot 事件已經自動加到 Event Viewer 里去了,這個視圖就在 Project Explorer 視圖下方。你可以在兩個視圖中的任何一個里點擊 PlayerShoot 事件。上一步中已經創建了一個 Play Event,Wwise 自動給這個事件的 Event Action (事件動作)列表中添加了一個 Play 動作。然而它還少了一個對 Object 的引用 - Wwise 現在還不知道當這個 Event 被調用時應該播放什么聲音。你可以右擊該列,選擇 Browse… ,再從 Actor-Mixer Hierarchy 中選中 rifle_shot 這個 SFX 對象。
你可以按照試聽 rifle_shot 對象的同樣方法來試聽 PlayShoot 事件,方法是在 Event Viewer 中選中這個 Event 並按下空格鍵。現在我們知道如何新建 Events 並把 Actions 添加到對象上去了。我們已經差不多准備好從游戲代碼中調用這個 Event 了,只需要做最后一件在 Wwise 中非常重要的事情 …
生成 SoundBanks
SoundBanks 是來裝載 Wwise 中你要創建的所有內容的容器,包括 SFX 對象,Events 等等。游戲要加載的不是別人正是 SoundBank,所以每當 Wwise 工程有了變化和更新的時候,你都必須要重新生成一個新版本的 SoundBank。同一個游戲可以有多個 SoundBanks(通常每個游戲關卡會有自己的 SoundBank),但因為 Survival Shooter 只有一個場景,所以我們只會用到一個 SoundBank。
在 Project Explorer 中切換到 SoundBanks 選項卡。右擊 SoundBanks 文件夾中的 Default Work Unit 之后選擇 New Child > SoundBank ,接着給它取名叫 MainSoundBank 。
下面把 Wwise 界面布局切換為 SoundBank,方法是從 Layouts 菜單中選擇這個布局,或者按下快捷鍵 F7 。Wwise 會更新各個視圖以反映新的工作環境。
你現在應該能在 SoundBank Manager 視圖中看到 MainSoundBank 對象了,它是 Default Work Unit 的子對象。從 Events 選項卡中把 Events 文件夾拖動到 SoundBank Manager 視圖中的 MainSoundBank 對象上去。保證勾選了這個 SoundBank 跟它的目標平台 Platform 和語言 Languages ,然后點擊 Generate 。
把 Wwise 添加到我們的 Unity 工程
切換到 Unity 並打開 Level 01 這個場景。我們要導入 Unity Wwise Integration( WwiseUnityIntegration_v2015.1.1_Mac.unitypackage ),這是一個 Unity 的 Custom Package(用戶包)。
如果一切進行順利,你會看到 Wwise Setup 屏幕出現了,注意到它已經定位到了我們的 Wwise 工程路徑,工程保存在游戲根目錄下。我們要保留默認設定,意味着 SoundBank 路徑應該維持 Audio/GeneratedSoundBanks 且保證勾選了底下的兩個復選框 - Wwise 會自動幫我們把一些游戲對象添加到 Survival Shooter 場景中去。
點擊 Start Installation 。
安裝結束后,你會看到 Unity 中出現了一個新選項卡叫做 Wwise Picker 。這給了我們一個高層次的視圖來看清 Wwise 工程的結構以及目前我們已經創建了的所有內容。
如果你看向場景的 Hierarchy 視圖,會注意到一個新對象叫做 WwiseGlobal ,它包括了兩個腳本組件,用來處理場景中 Wwise 的初始化和關閉。我們會維持這個對象的默認設定不變。
如果你查看一下 Main Camera 對象,也會發現 Wwise 已經將 Unity 的 Audio Listener 對象換成了自己的變體 - Ak Audio Listener - 並給了它一個 Listener ID 屬性,值為 0 。如果現在就播放這個 Unity 場景,那么你只會聽到一片空白。我們需要加載 SoundBank 並在游戲代碼中調用 PlayerShoot Event,這樣才能聽到 rifle_shot SFX 對象。
在 Unity 中加載 SoundBank
在場景中創建一個空游戲對象並給它取名為 LoadSoundBank 。從 Add Component 的 Wwise 子目錄中將 AkBank 腳本加到這個對象上去,並在 Bank Name 屬性中選擇 MainSoundBank ,保持其它屬性不變。
在 MonoDevelop 中打開 PlayerShooting.cs 腳本。在這個腳本中我們會添加對 PlayerShoot Event 的調用。看到第49行的 Shoot() 調用,這是每次武器打響時被調用的函數,所以這里是調用 PlayerShoot Event 的一個合適的位置。
將下列代碼添加到 Shoot() 下方:
1
|
AkSoundEngine.PostEvent(
"PlayerShoot"
,
this
.gameObject);
|
PostEvent 方法需要兩個輸入參數:被調用 Event 的名稱字符串和相關的游戲對象。
現在再播放一次場景,你應該可以聽到 rifle_shot SFX 對象了,並且每次槍聲響起時還有微妙的音高變化。
Wwise 的美妙之處
讓我們回頭仔細想想剛剛都做了些什么。我們只用一行游戲代碼就調用了 PlayerShoot 事件,於是就成功地解除了音頻團隊和游戲程序員的工作之間的耦合。這意味着只要約定了 Event 的名字,游戲程序員就可以繼續編寫游戲邏輯,而音頻團隊則可以集中注意力並行開發最佳的音頻體驗。假設我們想要改變步槍槍聲的音頻文件,或者用幾個聲音來層疊發聲,或者甚至給 PlayerShoot Event 添加別的 Play 動作,比如播放每發槍聲后(短暫延遲)的換彈聲。這些工作現在都可以不用游戲程序員干預了。音頻程序員只需重新生成包含新變動的 SoundBanks,隨后游戲音頻內容會自動更新。
再回想一下,剛才為 rifle_shot 聲音的音高做隨機化的過程是相對簡單的,想想如果要用 C# 游戲代碼來實現相似的效果會需要付出怎樣的努力,那樣至少需要好幾行代碼來生成隨機數並賦值給 AudioSource 的音高,而且如果我們要嘗試不同的音頻文件,那么就需要麻煩游戲程序員,發給他們新文件,讓他們把新文件加到工程素材中並加到確切的 AudioSource 里面去。相比之下,希望你能體會到使用 Wwise 能為整個團隊省下的開發時間。
制作更多的聲音 ...
創建玩家受創聲
讓我們切回我們的 Wwise 工程,並按 F5 來將界面布局換為 Designer 。切換到 Project Explorer 的 Audio 選項卡。
我們現在要添加一些玩家受創時要播放的聲音。為了做到這點,我們要使用一種 Wwise 對象叫做 Random/Sequence Container(隨機/序列容器)。這個對象會裝上多個 SFX 對象,分別代表各種嘟噥聲,並在玩家每次受創時隨機選擇並播放其中之一。導入( Shift I )叫做 damage sounds 的文件夾,當導入裝有多個音頻文件的文件夾時,Wwise 導入器會自動創建一個 Random/Sequence Container 作為父對象。
選中 damage sounds 對象,讓我們關注一下它的 Property Editor 。我們可以再次用 Wwise Randomizer 為整個容器添加一些隨機音高變化。這一次,讓我們將音高多樣化設成嚴格為正值 - 我們要設這個值為300音分,這樣嘟噥聲會有種活潑的花栗鼠叫聲的質感。我們還要保證 Play Type 設為 Random Shuffle ,並且因為我們不想連續聽到同樣的嘟噥聲,所以我們可以告訴 Wwise 要 Avoid repeating the last 2 played (避免重復前面2次播放的內容)。我們還要仔細確保 Play Mode 屬性設置為 Step (步進)而不是 Continuous (連續)。
我們要采用和 PlayerShoot Event 同樣的做法,來新建一個 Event 叫做 PlayerDamage ,它包含一個 Action 來在 damage sounds 這個容器對象上調用播放。
創建敵人音效
下面,我們要給三種敵人類型配上各自的聲音。再次選中 Actor-Mixer Hierarchy 中的 Default Work Unit ,導入( Shift I ) bunny.wav 、 bear.wav 、 hellephant.wav 這幾個音頻文件。這些會作為三種敵人類型的主要聲音。
如果試聽一下這三個新 SFX 對象,你會注意到它們其實都是一種怪物嚎叫的變體,並且各自反映了敵人的個頭 - 兔子(bunny)聲基本上是微喘了一口氣,而地獄象(hellephant)的聲音更長,更深沉響亮,而且感覺像是大得多的動物。熊(bear)的聲音介於上面兩者之間。
我們要讓這些聲音無限循環播放。每個 SFX 對象在 Property Editor 中都有一個 Loop 設定可以啟用,但與其對聲音一個個地做啟用操作,我們不如使用 Wwise 的 Multi-Editor 工具來節約時間。在 Actor-Mixer Hierarchy 中選擇全部三個敵人 SFX 對象並點擊 Ctrl M 來打開 Multi-Editor 窗口。在 Audio > General Settings > Loop 下啟用 Is Looping Enabled 並點擊 OK 。默認設定是無限循環,這符合我們的需求。
既然要循環播放這些聲音,那么我們應該保證循環是無縫的,這是指循環的開始和結束應該有同樣的音量電平。如果不這么做,那么一次循環播放會結束於某個音量電平而再次起播於不同的電平,造成聽得到的噼啪聲。針對每個敵人的聲音,打開其 Source Editor (源編輯器),方法是在其 Contents Editor 視圖中雙擊對象名字旁邊的波形圖標。
Source Editor 用來查看音頻文件的波形並調整裁剪點和淡變值。我們感興趣的是調整淡變值,這對應上圖中波形視圖頂端的藍色三角。拖動這些三角將調整淡入到滿度音量和淡出到無聲所需的時間。你會注意到波形的幅度會隨這些淡變值自動調整。
ShareSets 介紹 - 衰減
(2015年12月6日編輯)本教程早期版本中采用在每幀中計算玩家和敵人間的距離,再把距離值作為 RTPC 發給 Wwise 的方法來實現衰減,后來 Audiokinetic 團隊很慷慨地教會了我一個更簡潔的做法:我們不如讓 Wwise 自己為我們來處理距離計算!
第一步是新建三個新 Play Events,每個敵人的 Sound SFX 對象配備一個 Event。我已經給它們取名為 PlayZombear 、 PlayZombunny 、 PlayHellephant 。
然后,我們要前往 Project Explorer 的 ShareSets 選項卡並新建三個新的 Attnuations(衰減),每個敵人一個。 Max distance 這個參數可能需要縮短 - 我發現設為15比較合適。你大可以按自己的喜好調節曲線形狀。例如,如果讓“地獄象”的聲音在遠處也能聽見也許是個好的設計,因為這種野獸體積很大而且聲音也很吵鬧。而如果你希望熊和兔子能潛行突襲玩家,則可以把它們的衰減曲線設為只有在相當近的距離內音量才會攀升至滿度。
接下來,我們需要把這些 Attenuation 曲線關聯到對應的 Sound SFX 對象上去。對每個敵人聲音的 Sound SFX 對象,前往其 Sound Property Editor 的 Positioning 選項卡。確保 3D 選項已啟用,並在 3D 設定區塊中設置相關的 Attenuation 。將 Mode 項更新為 Use ShareSets 。
一旦為三種敵人的 Sound SFX 對象指派了 Attenuation,我們就要重新生成 SoundBank 然后切回 Unity。
前往 Unity 工程的 Prefabs 文件夾,對每個敵人的 prefabs 添加一個 Wwise 的 Ak Event 的腳本組件,把腳本加到 prefab 游戲對象上去,並在 Event Name 屬性下從我們之前創建的三個 Events 中選取一個合適的。
我們也需要把先前創建的 PlayerDamage 事件加入。打開 PlayerHealth.cs ,在 TakeDamage() 中添加以下代碼:
1
|
AkSoundEngine.PostEvent(
"PlayerDamage"
,
this
.gemaeObject);
|
保存並播放場景。你現在應該能夠聽到三個敵人的聲音會隨着敵人接近玩家而變得更響( Ak Listener 組件附着在玩家上,這個對象是 Wwse 內部用來計算發送事件的 Game Object 和玩家之間距離的),而且玩家角色每次受創時會發出不同的嘟噥聲!
互動音樂
如果我們能給 Survival Shooter 游戲添加些音樂,讓音樂能隨着玩家的動作而改變,是不是會很棒?我們可以利用一種 Game Sync 叫做 State(狀態)。
簡便起見,我們只會用到兩個玩家狀態來在兩個不同的音樂段落之間過渡 - 把一段簡單的弦樂合奏樂句用於玩家靜止不動的時候,而一段更復雜的弦樂合奏樂句用於玩家移動的時候。音樂節奏為4/4拍、124 BPM、降B小調。Wwise 可以讓你以音樂性的方式來做到在這些離散的狀態間過渡,也即可以在音樂樂句的節拍點或小節線上觸發狀態改變,而不是簡單的交叉淡變。
切回 Wwise 並按 F10 將界面布局換到 Interactive Music 。然后切換到 Project Explorer 的 Game Syncs 選項卡。
我們要新建兩個 States( Idle , Movement )。首先,新建一個 State Group,方法是右擊 States 文件夾中的 Default Work Unit ,並給它取名為 MainStateGroup 。然后在這個組里面新建兩個 State 子對象並給它們取名為 Idle 和 Movement 。
切換到 Project Explorer 的 Audio 選項卡,並右擊 Interactive Music Hierarchy (互動音樂層級結構)文件夾中的 Default Work Unit 。新建一個 Music Switch Container,並給它取名為 SwitchContainer 。然后右擊 SwitchContainer 並新建兩個 Music Playlst 類型的子對象,給它們取名為 Music Playlist Idle 和 Music Playlist Movement 。
我們現在要把創建好的 States 和 SwitchContainer 對象關聯起來。點擊 SwitchContainer 對象並在 Music Switch Container Property Editor 中選中 States 選項卡。點擊標記為 Add >> 的按鈕然后添加我們創建好的 MainStateGroup 。
接下來,切換到 Transitions 選項卡。那里應該已經有了一個默認的過渡(Transition),那么選中它。你會注意到被選中的過渡下方有兩片區域叫作 Source (源)和 Destination (目標)。我們要的是干凈利落的音樂性過渡,所以在 Source 下面,我們要選擇 Exit source at Next Beat 。我們還可以添加一個 Fade-out ,淡出時間為1秒,並給0.5秒的偏置,采用對數曲線。做完后你應該會得到這樣的結果:
我們也可以給 Destination 區添加 Fade-in ,采用與上述類似的數值。我們還要把 Destination 設定為 Sync to Same time as Playing Segment 。下面,我們要把 MainStateGroup 添加到 Music Switch Container Association Editor (關聯編輯器)中去,方法是點擊標記為 >> 的按鈕。最后,我們需要將兩個 Music Playlist 子對象從 Interactive Music Hierarchy 中拖到 Music Switch Container Association Editor 中它們對應的 States 上面去。這是至關重要的一步,而且我發現很容易忘記這一步。
現在,切換到 Music Swtich Container Property Editor 的 General Settings 選項卡,將 Time Settings 中的 Tempo 更新為124。 SwitchContainer 的 Music Playlist 子對象會自動更新它們的 Tempo 屬性來反映出父對象的變化。
現在我們可以把音樂文件添加到 Wwise 工程中去了。最簡單的方法是把正確的文件拖放到 Music Playlist 對象上去,這樣會自動新建 Music Segment(音樂段落),它里面裝有代表音樂文件的 Music Track(音樂軌)。然而,你還是可以手動添加這些子對象的,方法是右擊 SwitchContainer 並新建 Music Segment 子對象然后再創建 Music Segment 的 Music Track 子對象。
將 StringPhraseIdle.wav 拖放到 Music Playlist Idle 上去 ,然后將 StringPhraseMovement.wav 拖放到 Music Playlist Movement 上去。
創建了 Music Segment 子對象之后,你還需要把它們添加到 Music Playlist Editor 中去。選中 Music Playlist Idle 對象,將 StringPhraseIdle Music Segment 拖放到 Music Playlist Editor 中去。將這個 Group 的 Loop Count (循環次數)設為 Infinite (無限),方法是單擊下箭頭。要對 Movement 這個 Music Playlist 也重復一次這個過程。
到了這里,你可以試聽一下互動音樂了,方法是選中 SwitchContainer 對象,點擊播放,並在 Transport Control 視圖中的兩個狀態之間切換。你會注意到音樂是在節拍點上切換的!
還差一步 ...
我們想讓音樂在游戲開始時開始播放,所以我們要給 StartGame 事件添加一個 Play 動作,這會讓 SwitchContainer 開始播放。按 F5 鍵切回 Designer 布局,並把合適的 Action 加到 StartGame Event 中去。結果看起來應該是這樣的:
譯注:原文和上圖中的 StartGame 事件里有幾個問題需要澄清
- 這個事件需要單獨在 Wwise 中創建,原文遺漏了這個說明;
- 為了在游戲場景啟動時發送該事件,應該在 Unity 工程中某個 MonoBehaviour 對象的 Start() 方法中調用 PostEvent() 方法,或者利用 Wwise Unity 集成自帶的 AkAmbient 腳本組件,將其封裝的事件屬性 Trigger On 設為 Start ;
- 建議在手動創建 StartGame 事件時不要包括上圖中的前面三個 Actions,而只保留音樂播放的 Action,原因是這三個 Actions 對應的三個 SFX 對象已在 創建敵人音效 一節中被設為無限循環播放,所以一旦播放 StartGame 事件,怪物嚎叫聲會伴隨音樂不斷播放,而且前文 ShareSets 介紹 - 衰減 一節中已提到為三種敵人分別創建了各自的事件來播放此處用到的三個 SFX 對象,所以上圖中的情況不像是正常的設計。
你需要將 Interactive Music Hierarchy 文件夾添加到 SoundBank 中去。重新生成 SoundBank 並切回 Unity。
我們需要添加一點代碼,代碼要檢查玩家是否移動了。一個簡單的辦法是把下列代碼加到 PlayerMovement.cs 的 Move() 方法中去:
1
2
3
4
5
6
7
8
|
if
(h != 0 || v !=0)
{
AkSoundEngine.SetState(
"MainStateGroup"
,
"Movement"
);
}
else
{
AkSoundEngine.SetState(
"MainStateGroup"
,
"Idle"
);
}
|
變量 h 、 v 代表輸入坐標的數值。如果其中任何一個變量的數值不為零,則表示玩家在移動,我們於是對 State 做相應的更新。
如果你現在播放這個場景,應該就可以聽到音樂隨玩家移動而變化了!
音頻總線和混音:旁鏈/閃避
既然我們給場景添加了音樂,你可能會發現當玩家開槍時聽覺上會開始有點嘈雜。對此,一個可能的解決方案是當玩家開槍時讓音樂音量衰減一點。這叫作旁鏈或者閃避(Ducking)。Wwise 讓這種操作變得很簡單,方法是創造性地運用音頻總線。譯注:原文中作者將 Side-chaining 一詞與 Ducking 並用,但實際上只介紹了后者的功能,Wwise 的旁鏈比閃避功能更豐富,詳情請見官方文檔。
在 Master-Mixer Hierarchy (主混音器層級結構)的 Master Audio Bus 下面新建兩條子音頻總線,取名為 Music Bus 和 Weapon Bus 。
我們要更新 rifle_shot SFX 對象和 SwitchContainer 對象各自的音頻總線。你可以修改兩者的 Property Editor 中的 Output Bus 屬性。
最后,我們要添加閃避,方法是在 Master-Mixer Hierarchy 中選中 Weapon Bus 並將 Music Bus 插入到它的 Auto-ducking 屬性中去。我們也可以設置 Auto-ducking 的音量:我選擇-9 dB,這意味着當有信號通過 Weapon Bus 的時候 Music Bus 的音量會被衰減9 dB。你也可以設置閃避到達目標音量所需的時間,還可以設計衰減曲線的形狀。你應該會得到這樣的結果:
重新生成你的 SoundBank 並在 Unity 中播放場景。你現在應該可以聽到當玩家開槍時音樂音量會被壓低,隨后當停止開槍后又會回升到滿度音量。
結論
本文權當一篇基於 Unity 工程的 Wwise 簡介,只觸及了 Wwise 的一小部分功能。前文只是介紹了在 Unity Editor 中的操作步驟,當要把最終工程部署到選定的目標平台上去時,還需要把我們生成的 SoundBanks 挪到 Unity 的 StreamingAssets 文件夾下面去(譯注:參見前文 把 Wwise 添加到我們的 Unity 工程 一節配圖中的 SoundBank Path 設定)。感謝閱讀!