淺析游戲中的音頻GameObject管理

一、前言
GameObject是使用Wwise進(jìn)行音頻設(shè)計(jì)的基礎(chǔ)理念,從Wwise基礎(chǔ)功能的使用(尤其是Profiling),到使用WwiseSDK開(kāi)發(fā)都會(huì)涉及這一概念。筆者在日常工作中遇到過(guò)不少因?yàn)橐纛lGameObject管理不善而引發(fā)的問(wèn)題。故而嘗試對(duì)音頻GameObject的注冊(cè)與管理方法進(jìn)行一定的梳理。
首先我們來(lái)介紹一下關(guān)于本問(wèn)題的相關(guān)背景和一些基礎(chǔ)概念。
1、什么是GameObject?
首先我們來(lái)統(tǒng)一一下概念。
為了避免大家和廣義上游戲引擎中的GameObject以及Object-Based Mixing等概念相混淆,此處的定義以Wwise引擎中關(guān)于GameObject的定義為準(zhǔn):

同時(shí)為了與Wwise的界面及文檔統(tǒng)一,后文中也統(tǒng)一使用(Wwise中的)GameObject這一名稱(chēng)。
2、為什么要進(jìn)行音頻對(duì)象管理?
由上文可知,GameObject是Wwise中的核心概念——Wwise對(duì)音頻的管理都是基于管理GameObject來(lái)實(shí)現(xiàn)的。
可以說(shuō),在使用Wwise的情況下,游戲中任何聲音的播放都離不開(kāi)GameObject(無(wú)論聲音是2D/3D)。
若音頻GameObject管理不善,會(huì)導(dǎo)致各種問(wèn)題——輕則Wwise內(nèi)的功能異常或失效,重則在游戲引擎開(kāi)發(fā)層面留下隱患。
3、以GameObject為單位管理音頻是Wwise獨(dú)有的么?有什么特別之處?
以GameObject為單位(Object-based)管理音頻并非Wwise獨(dú)有,也存在其他音頻引擎(或音頻管理器實(shí)現(xiàn))支持這一管理方式。
不使用GameObject為單位管理音頻的音頻管理器實(shí)現(xiàn),通常是以事件為單位(Event-based)來(lái)管理音頻——這種情況下,音頻設(shè)計(jì)師只能從Event維度進(jìn)行控制。
而Wwise引擎則提供了GameObject和Event兩個(gè)維度供設(shè)計(jì)師使用(盡管這里Event也并非絕對(duì)的脫離GameObject),這使得聲音設(shè)計(jì)的靈活性與表現(xiàn)力都得到了提升。
Wwise Event、GameObject與UE Component的關(guān)系梳理
為了方便理解GameObject的重要性及其工作原理,我們以一個(gè)最簡(jiǎn)單的藍(lán)圖接口實(shí)現(xiàn)為例,看一下GameObject是如何在代碼層面被創(chuàng)建和引用的。
由于Audiokinetic(Wwise的開(kāi)發(fā)公司)官方維護(hù)有一套功能豐富的Unreal引擎集成,因此Unreal引擎集成當(dāng)中對(duì)于Wwise的SDK的用法具有一定的代表性,故而筆者在此處以PostEvent藍(lán)圖接口為例來(lái)論述。
以下截圖為Unreal中PostEvent藍(lán)圖節(jié)點(diǎn)的C++代碼:
Unreal中Postevent藍(lán)圖節(jié)點(diǎn)定義:

該段代碼對(duì)應(yīng)該藍(lán)圖界面:

該藍(lán)圖節(jié)點(diǎn)上AkEvent和Actor兩個(gè)輸入點(diǎn)必須連入變量或具體的值,否則將會(huì)在編譯時(shí)報(bào)錯(cuò)。
代碼邏輯簡(jiǎn)單講就是:運(yùn)行PostEvent節(jié)點(diǎn)時(shí)會(huì)給指定Actor對(duì)象的RootComponent層級(jí)附加AkComponent

附加AkComponent的過(guò)程,首先遍歷當(dāng)前Actor的子級(jí):

如果已有AkComponent則直接啟動(dòng),
如果沒(méi)有,那就新建一個(gè)AkComponent:

然后AkComponent會(huì)通過(guò)類(lèi)別轉(zhuǎn)換和Wwise內(nèi)的GameObject進(jìn)行強(qiáng)綁定。
所以,每個(gè)Actor播放Wwise的聲音時(shí),AkComponent必須存在。
AkComponent的作用:

AkComponent就是存在與UE中的Wwise專(zhuān)用的GameObject(的容器),其可以調(diào)用到以下函數(shù):


純看代碼可能不方便部分沒(méi)有代碼基礎(chǔ)的同學(xué)理解相關(guān)概念。
接下來(lái)我們換一個(gè)更加通俗易懂的方法來(lái)描述下這個(gè)邏輯:游戲引擎使用音頻中間件播放聲音的邏輯是這樣的:

總結(jié)(第一節(jié))
(Wwise中的)GameObject的數(shù)據(jù)來(lái)源是游戲引擎中的AkComponent,且該來(lái)源不可或缺。(數(shù)據(jù)可能包括方位、轉(zhuǎn)向、GameSync等等信息。)
可以說(shuō)每個(gè)聲音的播放都指向了一個(gè)目標(biāo)對(duì)象——AkComponent。
Wwise會(huì)為目標(biāo)對(duì)象注冊(cè)一個(gè)與之匹配的(Wwise中的)GameObject來(lái)承載聲音,其基礎(chǔ)屬性全部從AkComponent繼承。
所有的以AkComponent為目標(biāo)對(duì)象Post的Event都是以該(Wwise中的)GameObject為目標(biāo)的。
如果使用一個(gè)感性的說(shuō)法:
(Wwise中的)GameObject就是某個(gè)人(AkComponent)在聲音世界的分身,負(fù)責(zé)承載所有指向這個(gè)人的事件。
另外,(Wwise中的)GameObject除可依據(jù)AkComponent被注冊(cè),也可被解注冊(cè)。
明白了這些以后我們可以做什么呢?
二、Object-based音頻管理與Event-based音頻管理之間的潛在矛盾
1、什么是Event-based音頻管理
早期的游戲音頻中間件或者小體量游戲的音頻管理器實(shí)現(xiàn)基本都是以Event-based的理念來(lái)管理游戲中的音頻的。而現(xiàn)如今,通常游戲中的每一個(gè)聲音都是一個(gè)獨(dú)立的對(duì)象。
偶爾會(huì)有一些接手音頻開(kāi)發(fā)的同學(xué)在缺乏考慮的情況下選擇簡(jiǎn)單粗暴的方式來(lái)實(shí)現(xiàn)音頻播放的管理與控制:
為每次聲音播放都注冊(cè)GameObject,在聲音播放完成后便銷(xiāo)毀。
創(chuàng)建少數(shù)負(fù)責(zé)聲音播放的游戲?qū)ο?,將所有將要播放的聲音都指派至該?duì)象進(jìn)行播放。
我在本文中將他們統(tǒng)稱(chēng)為基于事件的(Event-based)音頻管理。
之所以這樣劃分,是因?yàn)樗麄冊(cè)趯?shí)現(xiàn)時(shí)通常只考慮了聲音的觸發(fā),并沒(méi)有考慮到聲音在觸發(fā)后進(jìn)一步進(jìn)行控制的必要和復(fù)雜性。
盡管上述對(duì)象的創(chuàng)建和管理模式存在一定的合理性,但它們肯定是不能滿足所有開(kāi)發(fā)需求的。
先來(lái)看一下第一種方式(一個(gè)事件伴隨一個(gè)隨機(jī)對(duì)象):
稍有編程功底的同學(xué)們都知道,游戲中對(duì)象的創(chuàng)建和銷(xiāo)毀是需要占用系統(tǒng)資源的,低頻率事件可以使用該模式,如果是高頻觸發(fā)事件,那么將會(huì)在短時(shí)間內(nèi)大量的創(chuàng)建和銷(xiāo)毀對(duì)象,會(huì)導(dǎo)致不必要的系統(tǒng)消耗——這是Event-based模式在性能消耗方面可能存在的隱患。
當(dāng)然某些場(chǎng)合可以采取創(chuàng)建對(duì)象池的思路來(lái)優(yōu)化,但并不是所有聲音的播放都適合指派到隨機(jī)的對(duì)象上。
如果我們的游戲中存在多個(gè)對(duì)象同時(shí)觸發(fā)相同聲音的情況,那么Event-based模式將很難精細(xì)的管理這些聲音,因?yàn)樗械腅vent都是相互獨(dú)立的,音頻引擎無(wú)法區(qū)分哪些Event屬于游戲里的哪個(gè)對(duì)象。這導(dǎo)致在進(jìn)行發(fā)聲數(shù)上限等音頻管理時(shí)就只能進(jìn)行全局管理,也就是只能使用Wwise中的Global模式進(jìn)行管理。
再來(lái)看一下第二種方式(大量事件指向同一個(gè)對(duì)象):
通常在游戲?qū)ωS富的聲音設(shè)計(jì)提出需求的情況下,設(shè)計(jì)師本身也要將性能的控制考量進(jìn)去。
而將所有聲音分配至同一個(gè)游戲?qū)ο蟮淖龇?,?huì)導(dǎo)致以實(shí)際游戲內(nèi)對(duì)象為單位的更細(xì)致的聲音并發(fā)控制無(wú)法實(shí)現(xiàn)(與第一種方式一樣會(huì)遇到這個(gè)問(wèn)題)。
若此時(shí)還對(duì)此同一個(gè)音頻對(duì)象的聲音并發(fā)量設(shè)置了上限(Global),則可能導(dǎo)致聲音被隨機(jī)誤傷,進(jìn)而產(chǎn)生不合理的聽(tīng)感。
而若要解決聲音被誤傷的問(wèn)題,就需要解除聲音并發(fā)量上的限制,而這又會(huì)導(dǎo)致較大的性能消耗。
總結(jié)(第二節(jié))
Event-based音頻管理是將GameObject視為Event的附屬物,為每個(gè)聲音事件匹配一個(gè)GameObject的做法。
而Object-based音頻管理則是將Event視為Gameobject的附屬物,先注冊(cè)和游戲中對(duì)象對(duì)應(yīng)的(Wwise中的)GameObject,然后Event作為子項(xiàng)繼承Gameobject的相關(guān)信息的做法。
三、如果GameObject管理不善可能造成的問(wèn)題
為了更方便大家理解,我們此處直接使用一些工作中可能會(huì)接觸到的案例來(lái)進(jìn)行分析:
假定我們當(dāng)前正在開(kāi)發(fā)的游戲中并沒(méi)有加入Object-based的理念,依然基于Event-based的理念來(lái)觸發(fā)和管理聲音,那么Wwise將不會(huì)正確接收和游戲內(nèi)對(duì)象對(duì)應(yīng)的GameObject信息,而是盲目的為每個(gè)音頻事件獨(dú)立生成GameObject。
那么我們?cè)谑褂肳wise時(shí)可能會(huì)遇到以下問(wèn)題:
1、Wwise內(nèi)所有和GameObject相關(guān)的功能全部失效或表現(xiàn)異常
所有選擇了Game Object模式的Random和Sequence容器在被觸發(fā)播放時(shí)不會(huì)刷新自己的列表。尤其值得注意的是,若每次播放都注冊(cè)新的Game Object,被播放的Sequence容器會(huì)永遠(yuǎn)都只播放第一個(gè)子項(xiàng)。
遇到類(lèi)似情況,設(shè)計(jì)師若不通過(guò)Profiler仔細(xì)檢查不同聲音的目標(biāo)對(duì)象指派情況,將會(huì)在Debug上花費(fèi)大量的時(shí)間。
?

選擇了Per Game Object的playback limit將失效,完全無(wú)法限制游戲內(nèi)的實(shí)際聲部數(shù)量,針對(duì)并發(fā)控制的聲部管理功能將不得不在引擎層面重新被實(shí)現(xiàn),然而這也要看程序員同學(xué)是否樂(lè)意。
如我們?cè)诘诙糠之?dāng)中已經(jīng)提到的,以游戲?qū)ο鬄閱挝坏穆曇舨l(fā)控制將無(wú)法實(shí)現(xiàn)。無(wú)論是一聲一對(duì)象還是多聲一對(duì)象,設(shè)計(jì)師都無(wú)法在創(chuàng)作工具層面實(shí)現(xiàn)性能上的控制。這相當(dāng)于程序同學(xué)把已經(jīng)分出去的工作又自己搶了回來(lái)。
?

插件參數(shù)的自動(dòng)化(LFO)作用域的不同選項(xiàng)表現(xiàn)將會(huì)變得混亂。
?

?具體會(huì)如何混亂,參見(jiàn)下圖:

在一聲一對(duì)象的情況下,Stop和Break等音頻事件將無(wú)法針對(duì)直觀存在的Game Object使用(這些行為都不得不交由程序管理),只能使用Stop all等針對(duì)全局性的功能。
這與前面提到的播放限制問(wèn)題一樣,相當(dāng)于程序同學(xué)把已經(jīng)分出去的工作又自己搶了回來(lái)。無(wú)法使用舊版本W(wǎng)wise中的Game Object Profiler,因?yàn)镚ame Object都是順序生成的,你很難知道即將播放的聲音的Game Object ID是啥,除非你需要監(jiān)視的是一個(gè)持續(xù)的Loop聲音。
這給音頻設(shè)計(jì)師測(cè)試與Debug帶來(lái)了進(jìn)一步的困難。
2、產(chǎn)生不必要的系統(tǒng)消耗
如果有高頻次觸發(fā)的One-Shot聲音,會(huì)導(dǎo)致聲音對(duì)象高頻次的反復(fù)創(chuàng)建和銷(xiāo)毀,這個(gè)情況我們前文已經(jīng)講述了,這會(huì)產(chǎn)生不必要的系統(tǒng)消耗;
因?yàn)槊總€(gè)聲音都獨(dú)立注冊(cè)GameObject,那么每個(gè)聲音也都會(huì)獨(dú)立計(jì)算方位與轉(zhuǎn)向、讀取自己的GameSyncs,這會(huì)進(jìn)一步造成無(wú)意義的系統(tǒng)消耗。
以玩家的腳步為例:
如果玩家身上有一個(gè)常駐的Game Object,那么Wwise只會(huì)在玩家腳下的地面材質(zhì)發(fā)生改變時(shí)接到SetSwitch API的調(diào)用,然后播放正確的音頻內(nèi)容;
如果不這么做,那么玩家的腳每踩一下,都會(huì)檢測(cè)一次地面材質(zhì)、注冊(cè)一個(gè)隨機(jī)ID的Game Object,然后為此Game Object調(diào)用SetSwitch API確保播放正確的內(nèi)容,再進(jìn)行音頻內(nèi)容的播放,在音頻播放完畢后解注冊(cè)掉Game Object,走每一步都會(huì)重復(fù)以上過(guò)程。
再以一輛車(chē)為例:
若車(chē)的引擎、胎噪、風(fēng)噪彼此是獨(dú)立事件觸發(fā)的,它們都需要讀取車(chē)輛的車(chē)速。
如果車(chē)輛上有一個(gè)常駐的Game Object,那么該Game Object對(duì)應(yīng)的三個(gè)事件的車(chē)速參數(shù)可通過(guò)同一個(gè)RTPC進(jìn)行控制(調(diào)用SetRTPCValue一次);
反之,若采取一聲一對(duì)象的方式實(shí)現(xiàn),則會(huì)生成三個(gè)Game Object,并且需要為每個(gè)Game Object傳遞車(chē)速RTPC(調(diào)用SetRTPCValue三次),哪怕這幾個(gè)Game Object需要的RTPC數(shù)值是一樣的。
這便會(huì)導(dǎo)致計(jì)算車(chē)速這個(gè)RTPC的導(dǎo)致的內(nèi)存/CPU消耗上升到合理做法的三倍。
雖然這個(gè)消耗可能依然不算大,但是這也是一個(gè)不健康的狀態(tài)。
3、針對(duì)GameObject設(shè)置的插件反復(fù)實(shí)例化
這個(gè)前文已經(jīng)提到過(guò)一次,因?yàn)槊總€(gè)聲音都是一個(gè)獨(dú)立的object,所以每有一個(gè)聲音都會(huì)實(shí)例化一個(gè)新的插件,每個(gè)插件都有自己獨(dú)立的運(yùn)行參數(shù),而不是從屬于同一個(gè)GameObject的插件擁有相同的參數(shù)。
4、限制了虛實(shí)例技術(shù)的使用
因?yàn)椴荒塥?dú)立注冊(cè)和銷(xiāo)毀GameObject,也沒(méi)有辦法通過(guò)讓多個(gè)發(fā)出相同聲音的發(fā)聲體只實(shí)例化其中一個(gè)的方式來(lái)減少系統(tǒng)消耗,所有的發(fā)聲體都將是真實(shí)實(shí)例化的。
總結(jié)(第三節(jié))
如果大家在工作中遇到了類(lèi)似以上所述的情況,可以嘗試查詢一下游戲內(nèi)針對(duì)音頻的GameObject的創(chuàng)建和注銷(xiāo)機(jī)制是否正常。有可能有助于大家定位問(wèn)題產(chǎn)生的原因。
四、良好的GameObject管理理念
1、游戲開(kāi)發(fā)初期應(yīng)當(dāng)規(guī)劃音頻對(duì)象池,對(duì)于可能高頻重復(fù)觸發(fā)的聲音事件采用對(duì)象池技術(shù)管理,甚至所有的音頻對(duì)象都采用對(duì)象池技術(shù)進(jìn)行管理。
2、游戲開(kāi)發(fā)時(shí)除了規(guī)劃聲音的觸發(fā)和停止機(jī)制以外,還要注意規(guī)劃聲音所屬GameObject的創(chuàng)建和銷(xiāo)毀(引用和解引用)時(shí)機(jī)。
3、游戲引擎的音頻接口開(kāi)發(fā)時(shí)應(yīng)注意盡量保持和Wwise官方SDK的參數(shù)設(shè)置保持統(tǒng)一,必要的時(shí)候給Wwise開(kāi)發(fā)獨(dú)立的音頻接口。
4、進(jìn)行音頻中間件的更換或升級(jí)時(shí)應(yīng)注意新舊中間件SDK的兼容度。如果兼容度低,升級(jí)過(guò)程中可能需要開(kāi)發(fā)新的音頻接口和同時(shí)修改聲音的掛接邏輯。
結(jié)語(yǔ)與致謝
以上就是截止目前我們對(duì)于音頻GameObject管理的相關(guān)研究,目前的結(jié)論也可能存在一定的疏漏,我們會(huì)在此基礎(chǔ)上不斷完善。
最后鳴謝協(xié)助我一起完成該文章的伙伴——張正昱陽(yáng)、溫哲奇。
同時(shí),鄭重鳴謝Audiokinetic大中華區(qū)產(chǎn)品專(zhuān)家侯晨鐘老師在文章的修訂過(guò)程中提供了豐富的相關(guān)資料與寶貴意見(jiàn),個(gè)人獲益匪淺。

徐巍
資深音效設(shè)計(jì)師
網(wǎng)易互娛
立志成為全品類(lèi)音頻制作人,曾參與制作過(guò)電影、電視、動(dòng)畫(huà)、球幕影院、沉浸式互動(dòng)場(chǎng)館等音頻設(shè)計(jì)與制作,現(xiàn)在開(kāi)始搞游戲了。
