【翻譯】音游開(kāi)發(fā)速成:概論(作者:5argon)
【文章原載于https://exceed7.com/native-audio/rhythm-game-crash-course/index.html,授權(quán)翻譯轉(zhuǎn)載,再次轉(zhuǎn)載請(qǐng)聯(lián)系原作者獲取授權(quán)?!?/span>
【譯注:
本文作者5argon是Native Audio的開(kāi)發(fā)者,這是Unity引擎中用于解決“移動(dòng)端聲音播放延遲較高”這一問(wèn)題的第三方解決方案,而本文就發(fā)布在Native Audio的官網(wǎng)上。作者5argon有豐富的音游游玩經(jīng)驗(yàn),并有一定的音游開(kāi)發(fā)實(shí)踐經(jīng)歷(作品Mel Cadence),同時(shí)具備音樂(lè)創(chuàng)作能力,曾為VOEZ、Dynamix和Pump It Up等音游供曲,對(duì)音游的游戲設(shè)計(jì)和制譜等問(wèn)題具有獨(dú)到見(jiàn)解。另外作者擁有計(jì)算機(jī)工程學(xué)士學(xué)位和互動(dòng)媒體設(shè)計(jì)碩士學(xué)位,對(duì)音游開(kāi)發(fā)中的許多技術(shù)問(wèn)題理解極為深刻,曾長(zhǎng)期更新Unity游戲開(kāi)發(fā)技術(shù)博客(許多內(nèi)容與音游開(kāi)發(fā)直接相關(guān))。
本系列文章中,作者主要討論了音游開(kāi)發(fā)中與音頻相關(guān)的技術(shù)問(wèn)題,以及游戲設(shè)計(jì)和制譜相關(guān)問(wèn)題,內(nèi)容見(jiàn)系列目錄。系列文章成文較早(約為2019-2020年間),參考時(shí)請(qǐng)注意時(shí)效問(wèn)題。】
【系列文章目錄】
音游開(kāi)發(fā)速成(概論)【本文】
背景音軌
游戲設(shè)計(jì)與制譜
四類音頻應(yīng)用程序
音頻導(dǎo)入設(shè)置
與DSP同步
來(lái)自Bemuse作者的提示
音游開(kāi)發(fā)速成(概論)

我猜許多對(duì)Native Audio感興趣的開(kāi)發(fā)者都正在開(kāi)發(fā)一款基于Unity的音游,因此我不妨針對(duì)音游開(kāi)發(fā)提供一些指南,從而幫您節(jié)約開(kāi)發(fā)時(shí)間。
其中部分內(nèi)容與Native Audio無(wú)關(guān),如果您不知道什么是Native Audio或Unity,或許也能從這篇文章中學(xué)到些什么!
祝您在這種小眾而美妙的游戲門類的創(chuàng)作之旅中一切順利。
您的游戲應(yīng)當(dāng)有趣
在運(yùn)用這些技術(shù)手段讓游戲正確運(yùn)行起來(lái)之前,應(yīng)該首先考慮用鍵型配置(pattern)和游戲設(shè)計(jì)使游戲變得有趣。我建議您先去看看《游戲設(shè)計(jì)與制譜》,然后再利用本文的信息解決技術(shù)方面問(wèn)題。
我認(rèn)為自己不會(huì)發(fā)布一款無(wú)趣的游戲。這個(gè)道理不言而喻,但無(wú)論您是否相信,當(dāng)真正做出一款游戲后,您就會(huì)感受到發(fā)布它的誘惑,即使您的內(nèi)心并不這么認(rèn)為。更不必說(shuō)您將面對(duì)來(lái)自親戚朋友們的“意見(jiàn)風(fēng)暴”,諸如“就先發(fā)布一個(gè)最小可用產(chǎn)品(然后由此繼續(xù)改進(jìn))”或“發(fā)布總比什么都不做要強(qiáng)”。找到您對(duì)作品質(zhì)量的“底線”標(biāo)準(zhǔn),然后告訴他們:“只是可以游玩是不夠的”。如果您有豐富的音游經(jīng)驗(yàn),您就可以清晰地聽(tīng)到自己的心聲:“我的游戲還遠(yuǎn)不如其他游戲有趣”。
播放背景音軌
除非您正在做一種從一無(wú)所有的狀態(tài)中創(chuàng)造出音樂(lè)的游戲,否則您的游戲中必然有某種背景音軌。您無(wú)需使用Native Audio處理背景音軌,因?yàn)樗?strong>不屬于反饋音,使用Unity的AudioSource + AudioClip方案就很好。
在此基礎(chǔ)上您需要使用一些技巧來(lái)盡可能讓背景音樂(lè)和Unity的時(shí)間值相同步,由此便可以判定玩家操作的準(zhǔn)度,這個(gè)話題稍后再討論。
比如在一個(gè)根據(jù)壓縮后的背景音樂(lè)敲鼓的游戲中,您可以使用Native Audio處理鼓聲,從而獲得最及時(shí)的反饋,同時(shí)這不會(huì)占用很多空間。Native Audio用于解決反饋音的延遲問(wèn)題,這些聲音長(zhǎng)度較短且無(wú)法事先預(yù)測(cè)是否會(huì)被播放,而且您希望在它需要被播放時(shí)能盡快被播放出來(lái)。
例如在敲鼓游戲中,如果您不點(diǎn)擊屏幕,鼓聲將不會(huì)被播放。而在收集金幣的游戲中,如果玩家錯(cuò)過(guò)了金幣,收集音效也不會(huì)被播放。
但如果您的游戲中只有必定會(huì)被播放的背景音軌,那么問(wèn)題可以使用固定偏移量或校準(zhǔn)來(lái)解決,而不是使用Native Audio。
背景音軌的延遲問(wèn)題
在音游中,您必須先讓背景音軌與第一個(gè)“Note”(這里的Note具體指什么取決于您的游戲)對(duì)齊,后續(xù)部分就會(huì)保持正確,除非游戲或音頻發(fā)生卡頓。90%的情況是游戲發(fā)生卡頓而音頻走在了前面,這是由于執(zhí)行播放指令后,音樂(lè)和游戲并不在同一個(gè)處理單元中運(yùn)行。這種卡頓需要在游戲和音頻中分別解決,本文不會(huì)立刻討論這個(gè)問(wèn)題。
Unity中的音頻是“即發(fā)即忘”的(fire and forget,發(fā)射后就不用再管)。當(dāng)您要求Unity的AudioSource開(kāi)始播放時(shí),AudioSource將為此花費(fèi)不定長(zhǎng)的時(shí)間,并在它覺(jué)得恰當(dāng)?shù)臅r(shí)候?qū)嶋H播放出來(lái)。實(shí)際播放行為最快也會(huì)發(fā)生在當(dāng)前幀的末尾,因?yàn)閁nity存在一種機(jī)制來(lái)實(shí)現(xiàn)混音并依優(yōu)先級(jí)決定所播放的音頻源,這意味著Unity必須先收集所有播放指令再實(shí)施播放,而不能在發(fā)出指令的那一行代碼處立刻開(kāi)始播放。
該幀結(jié)束后,獲取數(shù)據(jù)和驅(qū)動(dòng)揚(yáng)聲器的速度取決于設(shè)備自身。每個(gè)設(shè)備(特別是Android設(shè)備)的音頻延遲都不同。
我們難以在游戲中計(jì)算這種延遲,除非擁有一些合適的裝備(如環(huán)回線纜loopback cable)或一些復(fù)雜的技巧(如令設(shè)備錄制其自身?yè)P(yáng)聲器以自動(dòng)校準(zhǔn))。
所以Android設(shè)備上的背景音軌問(wèn)題通常通過(guò)玩家自行校準(zhǔn)來(lái)解決,因?yàn)椴煌珹ndroid設(shè)備間的音頻延遲各不相同。如果只有背景音軌而沒(méi)有反饋音,那么我認(rèn)為這是最好的做法。
當(dāng)玩家設(shè)置了本設(shè)備正確的偏移量后,您的任務(wù)是確保該偏移量始終不變,即在每次播放的開(kāi)頭都保持該值相同。一些音游在手工校準(zhǔn)后依然存在問(wèn)題,因?yàn)槊看沃貑⒂螒蚝筮@個(gè)偏移量都會(huì)改變。這是程序員的錯(cuò)誤,并且這樣的錯(cuò)誤會(huì)使玩家對(duì)他是否校準(zhǔn)正確感到懊惱,從而使玩家反復(fù)回到校準(zhǔn)界面,即使這并非他的過(guò)錯(cuò)。
精準(zhǔn)地啟動(dòng)音樂(lè)
如上一節(jié)所提到的:當(dāng)玩家?guī)湍鉀Q特定設(shè)備的延遲問(wèn)題后,您需要保證這個(gè)值每次都被正確執(zhí)行(包括每次重開(kāi),沖分玩家會(huì)重開(kāi)很多次)。
因地制宜地預(yù)加載音頻。怎么做到“因地制宜”?這進(jìn)一步取決于您的音頻導(dǎo)入類型!更多相關(guān)信息請(qǐng)參考《音頻導(dǎo)入設(shè)置》。
字面意義上的“立即播放”是不可能實(shí)現(xiàn)的,唯一的解決方案是使用AudioSource.PlayScheduled(https://docs.unity3d.com/ScriptReference/AudioSource.PlayScheduled.html),該方法可以指定一個(gè)未來(lái)的精確時(shí)間點(diǎn)。您的音頻將被故意延后播放,但會(huì)更精確地在所指定的時(shí)間播放出來(lái)。這比要求引擎立即播放但并不能真正實(shí)現(xiàn)“立即播放”的效果更好。該方法使用dspTime,請(qǐng)留意您請(qǐng)求AudioSettings.dspTime的位置,因?yàn)檫@個(gè)值可能在各行代碼間發(fā)生改變(也可能不變)。唯一需要確保的是這個(gè)時(shí)間點(diǎn)應(yīng)當(dāng)足夠遠(yuǎn),以供音頻“準(zhǔn)備完成”。如果您指示的時(shí)間點(diǎn)是“當(dāng)下”(即AudioSettings.dspTime本身或更早的時(shí)間,這是一個(gè)不可能完成的請(qǐng)求),這個(gè)方法將不會(huì)比audioSource.Play更好,它無(wú)法滿足您對(duì)“當(dāng)下”的請(qǐng)求。
請(qǐng)根據(jù)您在此處使用的未來(lái)時(shí)間點(diǎn),將您的游戲事件(Note或第一個(gè)小節(jié),或者和您游戲相關(guān)的某些事物)對(duì)齊到盡可能靠近該時(shí)間點(diǎn)的位置。由于Unity基于幀和游戲循環(huán),所以瞄準(zhǔn)未來(lái)的某一幀從而準(zhǔn)確地在所預(yù)定的時(shí)間點(diǎn)啟動(dòng)是不可能的。因此您的邏輯必須用某種方法在計(jì)算中涵蓋“超時(shí)”(即您所設(shè)置的預(yù)定時(shí)間和該時(shí)間實(shí)際落到的那一幀之間的時(shí)間差),從而表現(xiàn)得像預(yù)定時(shí)間準(zhǔn)確地落到您啟動(dòng)游戲的那一幀一樣。
關(guān)于處理背景音軌問(wèn)題的全面論述,請(qǐng)參閱《背景音軌》(這篇文章需要一定時(shí)間來(lái)消化)。
反饋音問(wèn)題
?(終于談到Native Audio的工作了?。?/p>
當(dāng)背景音軌正確播放后,唯一剩下的問(wèn)題是您游戲中可能存在的反饋音,如區(qū)分perfect/great/bad的音效,或收集金幣的音效。反饋音無(wú)法被校準(zhǔn)/補(bǔ)償,所以最好依賴于一種以盡可能低的延遲播放音效的方法。請(qǐng)參閱《四類音頻應(yīng)用程序》并理解為什么有反饋音的游戲(如音游)是最難解決的。
這最終成為Native Audio的一項(xiàng)任務(wù)。用這個(gè)庫(kù)實(shí)現(xiàn)盡可能及時(shí)地播放音效并不困難。
您需要理解:立即播放的準(zhǔn)確性總是不如正確校準(zhǔn),但校準(zhǔn)并不能用于反饋音,因?yàn)槟仨氉屢粜б苿?dòng)到更早的時(shí)刻播放從而補(bǔ)償延遲(而延遲導(dǎo)致音效更晚被播放出)。您無(wú)法將作為響應(yīng)的音效移動(dòng)到比造成該音效的輸入更早的位置,除非您會(huì)預(yù)知魔法或用神經(jīng)網(wǎng)絡(luò)之類的技術(shù)預(yù)測(cè)出玩家必然點(diǎn)擊屏幕并激活反饋音的時(shí)刻。
有一些有趣的情況實(shí)際上是可以預(yù)測(cè)或巧妙應(yīng)對(duì)的,這取決于您的游戲。例如如果您的游戲有一個(gè)用于長(zhǎng)條的音效,并且在擊中長(zhǎng)條頭部時(shí)播放該音效,那么或許可以稍晚一些播放。因?yàn)檫@個(gè)音效的播放是持續(xù)不斷的,并且稍晚一些不會(huì)大幅破壞游戲體驗(yàn)。(所以您可能并不需要Native Audio。)或者如果您的游戲中有一種Note,在Miss和擊中時(shí)分別播放兩種音效,那會(huì)發(fā)生什么呢?這種情況下您可以將擊中音效恰當(dāng)?shù)卦O(shè)計(jì)成融合了Miss音效的效果,這樣您就可以在確認(rèn)玩家輸入前預(yù)先播放Miss音效。如果玩家擊中了Note,那么播放的Miss音效將可以成為稍后播放的擊中音效的一部分。這種方案能正確工作的原因是該音效在無(wú)輸入(Miss)時(shí)也會(huì)播放。相信以您的游戲?yàn)榛A(chǔ)還可以做出其他巧妙設(shè)計(jì)。
一些判定時(shí)機(jī)的數(shù)值
如果您的背景音軌或反饋音只晚了10-20 ms,那問(wèn)題真的嚴(yán)重嗎?為了客觀地看待這個(gè)問(wèn)題,下面將展示一位音游大佬從諸多游戲中整理出的“最佳判定”時(shí)間窗口。
Dynamix? ? 59 ms
Deemo? ? 50 ms
Jubeat(ユビート)? ? 41 ms?
BanG Dream(ガルパ)? ? 40 ms
SOUND VOLTEX(ボルテ)? ? 33 ms
Arcaea? ? 25 ms
pop'n music(ポップン)? ? 25 ms
—?StaLight(@Sta_Light_)2018.7.21【譯注:原推文已無(wú)法查看】
VOEZ是30 ms
IIDX(弐寺)是20 ms
DDR?>>>15ms<<<
Cytus是70 ms
—?StaLight(@Sta_Light_)2018.7.22【譯注:原推文已無(wú)法查看】
請(qǐng)注意,這些數(shù)字并不是隨機(jī)選取的,而是大致為單幀時(shí)間(60 FPS下是16.666 ms,120 FPS下是8.3333 ms)的若干倍,因此檢查判定可以基于幀內(nèi)時(shí)間而非真實(shí)的“硬件輸入時(shí)間”。
無(wú)論如何讓我們先來(lái)看看DDR,其Marvelous判定的時(shí)機(jī)只有60 FPS下的1幀(不確定機(jī)臺(tái)在120FPS下運(yùn)行時(shí)會(huì)怎么樣,或許您可以在這個(gè)時(shí)間窗口內(nèi)“看到”箭頭移動(dòng)了2步),并且有玩家可以像這樣MFC(Marvelous Full Combo)一首難度離譜的歌曲:https://youtu.be/r2Z-qi9IPF8。
這意味著人類肯定可以連續(xù)2分鐘在16.67 ms的時(shí)間窗口下不出錯(cuò)。這個(gè)很小的窗口時(shí)間值實(shí)際上是相當(dāng)重要的。
音頻卡頓較為少見(jiàn)
這是一個(gè)或許不那么明顯的關(guān)鍵點(diǎn)。您的游戲中的所有內(nèi)容都是隨游戲循環(huán)一同更新的,這種更新可能基于每一幀的deltaTime。無(wú)論您如何運(yùn)用deltaTime,音頻總是向前播放而不會(huì)隨游戲發(fā)生卡頓。如果游戲發(fā)生了卡頓,那么游戲可能就會(huì)落到音樂(lè)的“后面”。這種情況下需要進(jìn)行“重新同步”。(否則玩家就會(huì)重開(kāi)……)
音頻卡頓源于緩存欠載(buffer underrun)
Unity會(huì)將音頻緩存的大小設(shè)置為256、512或1024,這取決于您選擇的設(shè)置是最佳延遲Best Latency/較優(yōu)延遲Good Latency/最佳性能Best Performance中的哪一個(gè)。(我認(rèn)為這將取決于設(shè)備,但就我做的測(cè)試而言,情況似乎并非如此。這個(gè)問(wèn)題的答案在未來(lái)也無(wú)法得到確認(rèn)。)
您可能希望讓緩存盡可能小,從而獲得最小延遲,但如果緩存過(guò)小,CPU就不能把揚(yáng)聲器正在播放的音頻數(shù)據(jù)及時(shí)地提取到內(nèi)存中。此時(shí)揚(yáng)聲器將不知道如何工作,從而產(chǎn)生人們所說(shuō)的卡頓lagging、干擾glitching、可怕的聲音scary、尖叫squealing、變慢slowdown、故障bugged、吱吱聲squeaking、僵尸聲zombie、結(jié)巴stuttering、混亂garbled(老實(shí)講,我收到過(guò)很多癥狀描述,里面的形容詞就沒(méi)有重復(fù)過(guò))。重要的是,此時(shí)您的游戲?qū)⒆叩揭纛l前面。這種情況難以通過(guò)程序檢測(cè)出來(lái),最好的設(shè)置是以緩存足夠大為前提,盡可能取最小值。
遺憾的是有些設(shè)備無(wú)法處理256甚至512大小的緩存。比如我聽(tīng)說(shuō)華為P20 / P20 Pro / Mate 20 Pro / Mate 20X做不到(即使這些型號(hào)都是頂級(jí)手機(jī)產(chǎn)品?。?。那些使用Unity內(nèi)部方案而非Native Audio的音頻(如BGM)將會(huì)變得混亂而無(wú)法使用?,F(xiàn)在該怎么辦呢?我能想到的最佳解決方案是在選項(xiàng)菜單下增加一個(gè)觸發(fā)AudioSettings.Reset(https://docs.unity3d.com/ScriptReference/AudioSettings.Reset.html)的滑條,讓玩家發(fā)現(xiàn)問(wèn)題時(shí)進(jìn)行調(diào)整。我接到反饋稱前述設(shè)備能在1024大小的緩存下正確工作。令人驚訝的是Native Audio在那些設(shè)備上使用遠(yuǎn)低于1024的值(約為240)且沒(méi)有發(fā)生緩存欠載。唯一的結(jié)論就是Unity在音頻處理中投入的工作量對(duì)于那些設(shè)備而言太多了,以至于任何更低的緩存量都無(wú)法處理。這個(gè)“bug”早在5.5.5版本中就被發(fā)現(xiàn)了,并且至今仍然存在(https://issuetracker.unity3d.com/issues/android-sound-stuttering-issue-on-huawei-mate-20-pro)!天啊!
出于某種原因,在Windows構(gòu)建中最佳延遲(Best Latency)選項(xiàng)必然導(dǎo)致緩沖區(qū)大小無(wú)法使用。Unity你怎么回事???
初始校準(zhǔn)問(wèn)題
如果您考慮在Android設(shè)備上游玩,那就必須在選項(xiàng)界面提供校準(zhǔn)功能。然而想象一下,一個(gè)萌新并不知道如何游玩,也不知道選項(xiàng)界面在哪。他入了坑,去玩教程,然后因?yàn)檠舆t問(wèn)題而失敗,那么隨后他就會(huì)退出游戲。您絕對(duì)不希望發(fā)生這種事。
許多游戲會(huì)在教程結(jié)束之后彈出一個(gè)窗口說(shuō):“如果您剛才的游玩體驗(yàn)不正確,請(qǐng)前往選項(xiàng)界面”,這是因?yàn)榘堰@個(gè)通知放到教程前面會(huì)打斷“新手引導(dǎo)儀式”,使一些像我媽媽一樣的非技術(shù)玩家感到困惑。
更好的做法是預(yù)設(shè)一定的校準(zhǔn)值,這使得新玩家也能享受到教程(可能還有一些簡(jiǎn)單歌曲),直到他意識(shí)到校準(zhǔn)錯(cuò)誤阻礙了他的游玩。那時(shí)您的玩家已經(jīng)投入到游戲中了,不太可能退出。
最簡(jiǎn)單的方法是使用“魔數(shù)”(magic number),您需要非常確信沒(méi)有任何Android設(shè)備在延遲方面的性能要好于這個(gè)數(shù)。但更好的做法是以某種方式“估計(jì)”每個(gè)設(shè)備的延遲,并用這個(gè)延遲開(kāi)啟游戲。問(wèn)題是如何做到?一些中間件確實(shí)具備估計(jì)功能,如Criware的adx2,它能用一些黑科技為您提供可信的估計(jì)值,而并不需要特殊校準(zhǔn)工具(如環(huán)回線纜)和麥克風(fēng)輸入。(這項(xiàng)技術(shù)是閉源的?。┪蚁M业腘ative Audio也能有這個(gè)功能,但目前……先忍一忍吧。
您的“0 ms”延遲是什么?您可以將初始校準(zhǔn)值作為“0 ms”顯示在UI上,但其背后填寫(xiě)了一個(gè)固定值。這可以讓玩家感到“0 ms”已經(jīng)比較準(zhǔn)確。如果您的0 ms是字面上的0 ms,那它很可能永遠(yuǎn)是錯(cuò)的。即使認(rèn)為設(shè)備是完美的,如果玩家不用耳機(jī)的話,聲音在空氣中傳播也需要時(shí)間??紤]將這一小段時(shí)間納入0 ms也是一個(gè)好主意,例如把0 ms定為實(shí)際的5-10 ms(提前播放)。
您可能想要通過(guò)查閱一個(gè)“延遲數(shù)據(jù)庫(kù)”來(lái)使每個(gè)設(shè)備都從正確的延遲值開(kāi)始。Superpowered網(wǎng)站(https://superpowered.com/latency)上有一個(gè)數(shù)據(jù)庫(kù),但我還沒(méi)有找到一個(gè)足夠好的方法在運(yùn)行時(shí)將設(shè)備映射到這個(gè)數(shù)據(jù)庫(kù)上。祝您好運(yùn)!
判定問(wèn)題
玩家根據(jù)音樂(lè)點(diǎn)擊屏幕,但您的代碼如何判定這次點(diǎn)擊?當(dāng)您有機(jī)會(huì)運(yùn)行您的判定邏輯時(shí)(一幀到來(lái)),就已經(jīng)落后于對(duì)應(yīng)的音頻時(shí)刻了,這是100%的事實(shí)。如果您使用該幀開(kāi)始的時(shí)間做判定,那么仍然會(huì)比實(shí)際點(diǎn)擊時(shí)間要晚,因?yàn)橥婕冶仨氃谇耙粠瑑?nèi)點(diǎn)擊屏幕,才能在這一幀被檢測(cè)到。使用這個(gè)時(shí)間做判定會(huì)使得偏早點(diǎn)擊的玩家獲得優(yōu)勢(shì)(因?yàn)辄c(diǎn)擊時(shí)間被稍稍后移),而偏晚點(diǎn)擊的玩家會(huì)受到懲罰,這可能導(dǎo)致判定結(jié)果降級(jí)。
正確的解決方案是觸控時(shí)間戳(touch timestamp)。所有原生Android和iOS系統(tǒng)都提供了這種時(shí)間戳,但Unity把它們?nèi)珤仐壛?,讓幀時(shí)間成為最準(zhǔn)確的時(shí)間。如果您對(duì)提高判定準(zhǔn)確度感興趣,可以試試The new Input System(https://www.youtube.com/watch?v=hw3Gk5PoZ6A)。
“手指敲擊音”(“finger nail”)玩家
我相信現(xiàn)在的玩家已經(jīng)學(xué)會(huì)了在設(shè)備延遲較差時(shí)關(guān)閉點(diǎn)擊音效,所以請(qǐng)確保為他們提供一個(gè)關(guān)掉反饋音的選項(xiàng)。
但是!經(jīng)驗(yàn)豐富的玩家甚至?xí)室怅P(guān)掉點(diǎn)擊音效,無(wú)論其設(shè)備延遲如何(即便他們可能使用的是iOS這種原生且延遲最低的設(shè)備)。因?yàn)樗麄兊氖种盖脫粢魧?huì)成為最準(zhǔn)確的反饋音直接傳到耳朵中。這些玩家將對(duì)游戲進(jìn)行校準(zhǔn),使得最佳判定的時(shí)機(jī)表現(xiàn)為:手指敲擊音穿過(guò)空氣的用時(shí)與音頻通過(guò)硬件延遲后再穿過(guò)過(guò)空氣/耳機(jī)的用時(shí)相匹配。因此包含一個(gè)校準(zhǔn)選項(xiàng)和點(diǎn)擊音效開(kāi)關(guān)是非常重要的,這使得玩家可以如上操作。
這就是為什么DDR的判定如此嚴(yán)格,但玩家依然有辦法命中。因?yàn)橥婕铱梢宰?strong>鞋子發(fā)出的腳步聲和音樂(lè)相匹配,并真正感受到節(jié)拍(在這個(gè)游戲中您的身體甚至?xí)瑫r(shí)振動(dòng),而不需要額外的硬件反饋)。DDR是現(xiàn)存為數(shù)不多的幾個(gè)不允許在游戲內(nèi)以任何方式校準(zhǔn)的音游之一。這并不是因?yàn)樗麄兛梢源_保機(jī)臺(tái)沒(méi)有延遲(這是不可能的),而是因?yàn)闄C(jī)臺(tái)已經(jīng)被精確地校準(zhǔn)過(guò)了,如果您鞋子的腳步聲(經(jīng)空氣傳播)匹配上了您從機(jī)臺(tái)聽(tīng)到的音樂(lè)(經(jīng)過(guò)揚(yáng)聲器到踏板間的站立距離),那么您將得到一個(gè)Marvelous判定(16 ms,60 FPS下的1幀)。這樣考慮的話,DDR游戲代碼必須提前播放音頻,從而使音頻在通過(guò)硬件和空氣的過(guò)程中被延后到最佳時(shí)刻,這個(gè)時(shí)刻正是您的腳步聲不經(jīng)過(guò)硬件延遲到達(dá)耳朵的時(shí)刻,這也是箭頭(arrow)匹配到接收器(receptor)的時(shí)刻。(空氣中的聲速也會(huì)隨氣溫變化(https://www.nde-ed.org/Physics/Sound/tempandspeed.xhtml),但別因?yàn)榇虿怀龊门卸ǘ髾C(jī)廳小哥調(diào)低空調(diào)?。?/p>
不要將游戲狀態(tài)同步到音頻時(shí)間/dspTime
您可能會(huì)想到這種方案:播放音樂(lè)并從硬件逐幀請(qǐng)求當(dāng)前的音樂(lè)時(shí)間,然后根據(jù)這個(gè)時(shí)間決定屏幕上的一切內(nèi)容(結(jié)合速度調(diào)節(jié)器(speed mod)等游戲機(jī)制)。通過(guò)這種方法使游戲和音頻始終保持同步。
這并不是一個(gè)好注意。首先,音頻時(shí)間是dspTime。dspTime不是實(shí)時(shí)的,而是會(huì)以一種特定步驟更新。所以您無(wú)法得知此刻音頻正在播放哪里。這聽(tīng)起來(lái)有些荒謬,但想象一下,音頻是一種“即發(fā)即忘”的東西,就像發(fā)射火箭一樣。當(dāng)您想知道火箭的位置時(shí),您或許可以測(cè)量一下,但當(dāng)測(cè)量完成時(shí)火箭已經(jīng)自己飛快地離開(kāi)了,這讓測(cè)量結(jié)果不再具備實(shí)用價(jià)值。從技術(shù)的角度講,音頻運(yùn)行在自己的線程中,并且測(cè)量行為不能阻塞這個(gè)線程。您所聽(tīng)到的內(nèi)容來(lái)自于流水線上的一個(gè)處理鏈條,它不斷把音頻數(shù)據(jù)灌到揚(yáng)聲器中。這個(gè)“流水線”并不像指示播放位置(“播放頭在這,您聽(tīng)到的內(nèi)容是這個(gè)”)一樣簡(jiǎn)單,而是一些音頻數(shù)據(jù)已經(jīng)在排隊(duì)并等待播放(這并不是您所請(qǐng)求的當(dāng)前位置)。所以通過(guò)請(qǐng)求音頻時(shí)間得到的結(jié)果并不代表您當(dāng)前聽(tīng)到的內(nèi)容。在iOS上您或許可以這樣處理,但您會(huì)發(fā)現(xiàn)Android平臺(tái)返回的時(shí)間不夠流暢(無(wú)論通過(guò)Unity的API還是Native Audio的API,結(jié)果都是如此)。即使游戲“從技術(shù)上”與音頻時(shí)間始終同步,這種同步也不夠流暢,這帶來(lái)的游戲體驗(yàn)比流暢游玩卻因卡頓而不斷偏離音頻的游戲體驗(yàn)更令人惱火。
相反,您應(yīng)該讓游戲基于自己維護(hù)的浮點(diǎn)變量,并讓該變量逐幀增加。在維護(hù)了這個(gè)浮點(diǎn)變量的基礎(chǔ)上,讓播放音頻的方法總是可靠地請(qǐng)求該變量的值,并將該值解讀為音頻時(shí)間。這種方案下,您必須寄希望于該變量值在一段時(shí)間后仍與音頻同步前進(jìn)。但在做這種期望之前,應(yīng)當(dāng)通過(guò)一些手段來(lái)保證正確起步,比如讓音頻預(yù)定在某個(gè)精確的未來(lái)時(shí)刻開(kāi)始播放,并使用您的浮點(diǎn)變量“錨定”這個(gè)未來(lái)時(shí)刻,對(duì)應(yīng)的浮點(diǎn)變量值就是音頻的“第0秒”。請(qǐng)記住,立即播放永遠(yuǎn)做不到立即,而延遲到未來(lái)是更好的選擇。
如果您的游戲完美運(yùn)行,那么浮點(diǎn)變量應(yīng)該是可靠的。而如前所述,如果游戲發(fā)生卡頓,“即發(fā)即忘”的音頻可能不會(huì)隨游戲一同卡頓,這會(huì)導(dǎo)致您的浮點(diǎn)變量落后,而后您可能必須重新同步:要么讓音頻跳轉(zhuǎn)到浮點(diǎn)變量處,要么讓浮點(diǎn)變量跳轉(zhuǎn)到音頻處(但這會(huì)導(dǎo)致游戲的跳轉(zhuǎn),因?yàn)槟挠螒驙顟B(tài)是基于浮點(diǎn)變量值的)。不幸的是,如果音頻因?yàn)榫彺媲份d或其他原因發(fā)生卡頓,這將很難修正。您只能寄希望于玩家重啟這首歌曲并重試。
有什么其他提示嗎?
2019.1改進(jìn)了Android上的普通Unity音頻延遲,目前更多設(shè)備可以收到更快的音頻源,請(qǐng)參閱這篇文章(https://gametorrahod.com/unitys-android-audio-latency-improvement-in-2019-1-0)。如果您的游戲一部分使用了Native Audio,但較長(zhǎng)的聲音內(nèi)容(以及需要使用混音器或效果器的聲音內(nèi)容)使用了Unity的方案,那么這個(gè)改進(jìn)是有用的。
在iOS上使用Native Audio時(shí)請(qǐng)不要忘了把Project Settings的Audio選項(xiàng)設(shè)置為最佳延遲(Best Latency)。而在Android中無(wú)所謂,因?yàn)檫@個(gè)設(shè)置只會(huì)影響Unity申請(qǐng)的音頻源,而我們會(huì)使用自己的設(shè)置申請(qǐng)一個(gè)新的。但iOS中這個(gè)設(shè)置會(huì)修改設(shè)備的緩沖區(qū)大小,而這個(gè)緩沖區(qū)是Unity和Native Audio共用的。Native Audio并不會(huì)嘗試影響Unity的當(dāng)前設(shè)置,所以在iOS上選擇最佳性能(Best Performance)選項(xiàng)也會(huì)降低Native Audio的速度。(您仍然可以通過(guò)跳過(guò)Unity的混音器來(lái)改善播放延遲,并且能獲得幀內(nèi)即時(shí)播放的優(yōu)勢(shì)而不必等到幀尾。)
如果您的設(shè)備是插電狀態(tài),觸控將會(huì)因?yàn)殪o電而隨機(jī)變慢。由于輸入用時(shí)會(huì)影響對(duì)音頻延遲的感知,因此這種現(xiàn)象可能會(huì)讓您產(chǎn)生音頻不時(shí)變慢的錯(cuò)覺(jué)。
請(qǐng)檢查您的音頻文件開(kāi)頭是否有一段無(wú)聲部分。像Audacity之類的程序提供了立即修剪頭尾無(wú)聲部分的命令。
尖銳而突發(fā)的音頻帶來(lái)的反饋感更強(qiáng),而緩慢淡入的音頻會(huì)讓人感覺(jué)有延遲(這個(gè)延遲實(shí)際上是音頻內(nèi)容的一部分)。如果您自己合成SFX,那么可以通過(guò)聲音設(shè)計(jì)來(lái)改善玩家感知到的延遲。
在前幾點(diǎn)的基礎(chǔ)上,您還可以修改原生端的觸控處理入口來(lái)調(diào)用原生端的Native?Audio,從而完全避免經(jīng)過(guò)Unity,來(lái)達(dá)到更快且配合度超強(qiáng)的音頻播放效果!這種方案就像寫(xiě)一個(gè)原生游戲一樣!然而這種hack行為將會(huì)非?;靵y:您如何在純?cè)藱z查Unity的內(nèi)容,從而檢查一些觸控條件?(除非您的游戲無(wú)論在何種情況下都要在玩家點(diǎn)擊屏幕時(shí)播放音效)這里只是想指出沒(méi)有什么方案能比這更快了。(我的插件也不支持這種可怕的優(yōu)化,但如果您想的話可以hack進(jìn)去……),