最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

【翻譯】音游開發(fā)速成:背景音軌(作者:5argon)

2023-07-02 02:26 作者:Starbow-Iridum  | 我要投稿

【文章原載于https://exceed7.com/native-audio/rhythm-game-crash-course/backing-track.html,授權(quán)翻譯轉(zhuǎn)載,再次轉(zhuǎn)載請聯(lián)系原作者獲取授權(quán)?!?/span>

【系列文章目錄】

  1. 音游開發(fā)速成(概論)

  2. 背景音軌【本文】

  3. 游戲設(shè)計與制譜

  4. 四類音頻應(yīng)用程序

  5. 音頻導(dǎo)入設(shè)置

  6. 與DSP同步

  7. 來自Bemuse作者的提示

背景音軌

在這一大節(jié)中我們將討論如何播放背景音軌!(劇透:這可不只是AudioSource.Play。)這個任務(wù)給人一種很簡單的錯覺,因為其中的數(shù)學(xué)計算看起來是非常非常簡單的加減法,但它將使您大傷腦筋。我甚至不確定本文能否給除我以外的其他人講明白。

您的接收器運行在“時間”上

接收器是指向前運動的判定線,或者反過來說是Note落到它上面,這取決于您如何看待。本文中我假定接收器以時間為單位,并且它會根據(jù)增量時間(delta time)逐幀自增。在此基礎(chǔ)上,把“接收器時間”看作該接收器的一種“位置”,而其他位置參數(shù)都不會被轉(zhuǎn)換為時間。這樣做的優(yōu)勢在于可以在整個游戲中通過增加增量時間來驅(qū)動游戲前進。我不知道您目前用什么來代表歌曲中的“位置”,請隨意轉(zhuǎn)換本文的定義來適應(yīng)您的定義,而不是將游戲調(diào)整成與本文相同的單位。

接收器時間將用于:

  • 判定:在您檢測到輸入的那一幀中,您可能需要將當(dāng)前的接收器時間和每個受影響的Note的時間相比較,從而判斷您是否應(yīng)當(dāng)為這些Note計分,以及使用什么判定結(jié)果來計分。

  • 渲染:舉例而言,您的接收器時間是1.5 s,而某個Note在2.25 s。您可以將渲染邏輯實現(xiàn)為:當(dāng)接收器在2 s時,這個Note應(yīng)當(dāng)比之前距離更近。(這就是您所感知到的“Note落在接收器上”,但實際上是接收器在向前運行。)

這里并沒有請求音頻位置來設(shè)置接收器時間。接收器時間的變化只基于增量時間。正如您已經(jīng)閱讀過的內(nèi)容:請求音頻時間并不可靠,我們從揚聲器聽到的內(nèi)容很可能早于音頻DSP時間,并且音頻DSP時間以難以捉摸的步驟進行更新,甚至?xí)诮酉聛淼膸仔写a內(nèi)發(fā)生改變(抑或不變)。

更好的做法是只在一個關(guān)鍵時刻(即音樂開始的時刻)上確保游戲和音頻時間相同步,然后任它自行前進。如果緩存超載/欠載導(dǎo)致音頻卡頓,或游戲卡頓導(dǎo)致音頻超前,那也就不管了。這比讓整個游戲基于音頻時間要好得多!請將您的整個游戲建立在增量時間上。

您的任務(wù):從任意接收器時間開始游玩

您當(dāng)然可以從頭啟動歌曲,但請記住,您的游戲可能可以暫停和恢復(fù),甚至可能有一個可以從任何位置開始“測試游玩”的制譜器。此處的“播放背景音軌”功能應(yīng)當(dāng)能夠處理當(dāng)前接收器時間在任何位置的情況。以常規(guī)方式開始游戲只意味著將接收器移動到一個負數(shù)時間,然后啟動這個我們正在摸索和實現(xiàn)的奇妙功能。

PlayScheduled:請尊重這個方法

Unity內(nèi)置的PlayScheduled有一個連Native Audio也做不到的優(yōu)勢:準(zhǔn)確性(不是指延遲準(zhǔn)確,也不能即時響應(yīng))。您可以指定一個未來的時間,它將努力在那時啟動音頻(您依然可以預(yù)先將音頻設(shè)置在任何位置,之后它將從那個時間點開始播放)。

任何您可以提前預(yù)知到的音頻都應(yīng)當(dāng)以此播放,如背景音軌、自動播放的輔助音效、節(jié)拍器等。

預(yù)定(schedule)的特殊性在于它獨立于幀。如果您指定的時間在幀的內(nèi)部,音頻也能按時播放。這是什么神奇技術(shù)?幀是Unity引入的一種抽象概念,而原生端通常支持更緊湊的回調(diào)或一些幀以外的東西(如果您從事過微控制器或Arduino等設(shè)備的開發(fā)工作,您可能聽說過“中斷”信號,它能讓您瞬間觸發(fā)一些東西)。Unity有一些平臺專用代碼來對接本地的音頻預(yù)定方法。PlayScheduled實際上比它看起來更有價值。

您在update函數(shù)中等待并于正確時間點播放的天真方案總是會比真正想要的時間點更晚,因為無法確保幀時間精確地落在想要的時間點上。您可以假設(shè)在完美的情況下,下一幀會在1/60秒后到達,這是您可以實現(xiàn)的最大精度。

使用預(yù)定方案引入了新問題,即播放必然發(fā)生于未來。這會對我們造成什么影響?

暫且假設(shè)延遲為0

為保證思路清晰,讓我們先假設(shè)音樂將在PlayScheduled上指定的時間點立即而可靠地播放。

修正超時

請先考慮一個簡單情況:您的接收器位置是0 s,并且您想從此處開始。此時您想要聽到的是哪些音頻數(shù)據(jù)?

當(dāng)然是音頻文件0 s處的數(shù)據(jù)!也就是說如果下一幀將接收器位置移動到16.66 ms,那么您想在那時聽到音頻16.66 ms處的數(shù)據(jù)。所以歌曲應(yīng)當(dāng)從接收器時間的0 s處開始。(即沒有偏移量,本文將在稍后討論這個問題)

但是請等一下,我們有“播放必須發(fā)生在未來”的規(guī)則,而這一幀中我們剛剛收到了播放指令,那么我們怎么可能在此時聽到文件中0 s處的音頻數(shù)據(jù)呢?

請考慮這種策略:

  • 將音頻時間設(shè)置為0 s。

  • 然后預(yù)定在當(dāng)前時間后的2 * 16.66 ms處開始播放音頻(我認為游戲可以在2幀時間內(nèi)準(zhǔn)備好音頻)。請記住預(yù)定時間以DSP時間為單位,例如本行代碼處您請求了DSP時間并得知它是20000 ms,那么您應(yīng)該“期待”的播放時間是20033 ms。PlayScheduled接收的也是DSP時間軸中的時間點,因此您應(yīng)該傳入20033 ms。

  • 請不要立刻開始您的游戲!正確的計劃是在20033 ms時開始游戲。

  • 兩輪更新結(jié)束后,您再次檢查DSP時間,其結(jié)果可能是:

    • 20033 ms(實際上極不可能):太幸運了!您現(xiàn)在聽到的是0 s處的音頻,而接收器當(dāng)前的位置也是0 s。這一幀中您不會在接收器位置上增加增量時間,因為目前的位置是正確的,而之后的每幀中您將持續(xù)地在接收器位置上增加增量時間。增量時間與音頻時間無關(guān),但是您寄希望于從現(xiàn)在開始音頻時間會與您加到接收器時間上的增量時間保持“遠程同步”。至少您已經(jīng)實現(xiàn)了一個非常好的開始!

    • 20018 ms(糟糕):音頻播放的預(yù)定計劃已經(jīng)向前推進,只是目前還沒有開始播放。但下一幀可能就已經(jīng)開始播放了,而您的代碼錯過了這個時間(比如那時可能已經(jīng)是20041 ms了)!但是目前已經(jīng)預(yù)定的音頻還沒有開始播放,所以您在音頻開始前還有最后的機會來執(zhí)行一些代碼。一個顯然的選項是使用StopScheduled取消并嘗試重新預(yù)定,但這可能產(chǎn)生無限循環(huán)。因此我建議讓這一幀過去,看看下一幀的情況。

    • 20041 ms(糟糕):音頻已經(jīng)開始播放,并且沒法停下來!如何解決呢?對于這一幀,可以增加一個代表超時時間的特定增量時間,即20041 - 20033 = 8 ms。請記住,如果兩幀的用時是精確的(第一種情況),我們不會在這一幀上為接收器時間增加增量時間,因為此時接收器時間已經(jīng)是正確的時間了。增加特定增量時間意味著您的游戲會在這一幀上奇怪地向前移動一點,而下一幀就可以像往常一樣持續(xù)增加約為1/60 s的增量時間了,并且也要寄希望于“遠程同步”(和第一種情況相同,已經(jīng)解釋過了)。

使用這種策略可以通過修正超時來規(guī)避“預(yù)定”只適用于音頻的事實限制,Unity沒有“ExecuteCodeScheduled”這種方法來讓您在想要的時間點啟動游戲。接收器也和音頻一樣以時間為單位,這使得您可以在自己的方案中使一些小手段,游戲循環(huán)會給您機會來強行前移接收器以實現(xiàn)時間匹配。這是您所擁有的唯一一個精確的修正機會,因為您為PlayScheduled方法指示的預(yù)定時刻是20033 ms,而目前比那個時刻稍晚一點,并且您知道我們具體晚了多少。我將那個預(yù)定時刻稱之為NOW!時刻。

而未來各幀的同步情況都由增量時間和DSP時間的命運決定。(還取決于玩家的聽覺是否能捕捉到游戲和音頻間的錯位,如果玩家并不在意準(zhǔn)度那就更好了。)

從暫停中恢復(fù)會怎么樣

當(dāng)游戲暫停時,您并不會將AudioSource暫停(pause),而是完全將之停止(stop)。當(dāng)玩家點擊恢復(fù)按鈕的那一幀中,我們要將先前說過的事情重做一遍。區(qū)別在于當(dāng)玩家想要再次開始游玩時,您需要將播放頭時間事先設(shè)置在當(dāng)前接收器位置上。

這是一種實現(xiàn)暫停和恢復(fù)的好方法,因為此時我們可以修正DSP時間和實際接收器時間的任何錯位,后者是判定和渲染的依據(jù)。我們重做了一次“修正超時”工作,又得到了一次確保二者精確同步的機會。

在歌曲之前開始游戲會怎么樣

音頻可能在接收器時間的0 s處開始,但您酷炫的UI上有一段先導(dǎo)動畫和“READY?”字樣。您或許會希望在屏幕上出現(xiàn)“READY?”字樣時Note也同時滾動(或在背景中看到一個空白的游戲區(qū)在不斷滾動)。

解決這個問題只需簡單地根據(jù)先導(dǎo)動畫的長度將接收器時間設(shè)置為負數(shù)即可。但有一點不同:當(dāng)從暫停中恢復(fù)時,您需要事先將音頻時間提前。然而目前您無法事先將音頻時間提前到一個負數(shù),最小值是0 s。

沒錯,您必須在使用預(yù)定方法PlayScheduled時加上這個“提前量(under-time)”,并事先將音頻設(shè)置在0 s(這是它能被設(shè)置到的最小值)。

這意味著預(yù)定播放將被推后一大段,NOW!時刻被推后一大段。游戲在每一幀中都檢查DSP時間,那么最后應(yīng)該有一幀中返回的DSP時間會稍稍超過我們預(yù)定的時間。

區(qū)別在于我們需要在NOW!時刻前持續(xù)逐幀增加增量時間,從而使游戲在顯示“READY?”字樣時滾動。在檢測到的DSP時間超過了預(yù)定的DSP時間的那一幀中,完美的做法是不向接收器位置上增加增量時間,而是將之設(shè)置為:與預(yù)定的DSP時間相對應(yīng)的接收器時間,加上當(dāng)前的DSP時間和預(yù)定的DSP時間之差?!咀g注:此處描述相對混亂,請對比前文20041 ms情況進行理解,原文為:This frame where you detected DSP over your scheduled DSP time, it would be perfect if you rather not add delta time to your receptor position, but set the receptor position to an equivalent time of the DSP time you scheduled plus the difference.】此處的“設(shè)置”意味著無論當(dāng)前接收器位置在哪里(應(yīng)當(dāng)在附近),都會跳轉(zhuǎn)到您想要的時間。

這會產(chǎn)生一些副作用:一些設(shè)備差但眼力好的玩家將會注意到游戲向前或向后跳了一點,來補齊歌曲開始的NOW!時刻和過往幀的不一致。如果這附近沒有Note,那可能就不是什么問題。無論如何,大部分歌曲在開始其音頻內(nèi)容時,屏幕上是沒有任何Note的。我認為確保與音樂的同步性值得我們對接收器時間做這樣的微調(diào)。

您已經(jīng)考慮了所有情況!您甚至可以在游戲還在顯示“READY?”字樣時將之暫停和恢復(fù),游戲依然會在歌曲真正開始播放前以恰當(dāng)?shù)姆绞降却?,因為您對該問題的處理方案已經(jīng)具備足夠的普遍性。

偏移量

在上一節(jié)中我們假設(shè)當(dāng)接收器時間為1 s時,您想聽到1 s處的音頻。

但這種情況并不常見。音頻文件的開頭往往有一段無聲部分,或者有一些前奏內(nèi)容讓您寧可向前或向后移動一些,從而使第一聲镲落到您寫在2 s處的那個Note(小知識:此處正是120 BPM的4/4拍歌曲的第二小節(jié),因為一拍是0.5 s)。這是音頻文件具有的偏移量,可能適用于所有譜面難度。

所以您只要在做各種事情前加上這個時間就可以了。例如偏移量顯示歌曲延后了4 s,但游戲在0 s時精確啟動【譯注:可能是作者筆誤,原文為“歌曲在0 s時精確啟動”,無法讀通】,或者玩家可能在0.5 s時暫停了游戲(被蚊子咬了)。這將會把情況轉(zhuǎn)化為“在歌曲之前開始游戲”,您必須把“提前量”加到預(yù)定時間中,這在之前已經(jīng)解釋過了。

負偏移量的做法也是相同的,如果歌曲的偏移量為負數(shù),那么在0 s時您已經(jīng)聽到了歌曲的某些內(nèi)容。

偏移量通過前后移動歌曲來解決“在當(dāng)前接收器時間下我們應(yīng)該聽到歌曲的哪一部分?”這個問題,其正值和負值都有意義。或許您真的希望讓歌曲的5 s處落在第0小節(jié)上,這需要您將歌曲移動到更早處。(或者調(diào)整譜面,讓Note從更晚的位置開始,而非從第0小節(jié)開始。)

您可能為了播放動畫和“READY?”字樣而在游戲開始時將接收器時間移動到負值,但請確保在實現(xiàn)這個效果時也考慮到歌曲偏移量。即用偏移量作為計算開始時間的被減數(shù),而不是從0 s做減法計算。否則一首將偏移量移動到很晚處的歌曲會導(dǎo)致在顯示“READY?”字樣和游戲真正開始之間存在過長的間隔。

玩家可調(diào)的“校準(zhǔn)

有時稱作“輸入校準(zhǔn)”,有時稱作“音頻校準(zhǔn)”,又或是“調(diào)整延遲”?

延遲補償

請您先認可我的這一觀點:負延遲是沒有意義的。延遲說的是音頻播放晚于預(yù)期。因此如果您要求補償0.5 s延遲,那么所有可以提前計劃的東西(您已經(jīng)被迫將之提前過了,從而滿足我們的未來預(yù)定規(guī)則)都需要再提前0.5 s,這讓它們可以經(jīng)由延遲回到正確時間。

延遲的定義使得“我想要-0.5 s的延遲補償!”這一想法是沒有意義的。

當(dāng)從暫停中恢復(fù)時(假定我們已經(jīng)進入了歌曲),您知道有幾幀是不會移動接收器時間的,因為我們正在等待NOW!時刻。但現(xiàn)在您必須在檢測到DSP時間超過所承諾的NOW!時刻后繼續(xù)等待,這樣您才能修正超時問題并最終讓游戲繼續(xù)。簡單地說,就是在預(yù)定時間的基礎(chǔ)上再加上和所補償?shù)难舆t相等的時間(請不要直接加到預(yù)定時間中,只是讓游戲在到達預(yù)定時間后多等一小會兒就好)。

在前面的例子中,我們等待的是20033 ms位置。假設(shè)您已經(jīng)到達了DSP時間為20035 ms的那一幀,但延遲補償被設(shè)定為10 ms,因此即使您知道預(yù)定的音頻已經(jīng)開始播放,但本幀仍然不該為接收器時間上增加任何東西。相反,您需要再等一幀(或多幀),讓DSP時間的讀數(shù)達到20055 ms(例),之后您通過比較20055和20043(而不是20033)來修正超時【譯注:可能是作者筆誤,原文為“比較20055和20045(而不是20035)來修正超時”】。

在比較極端的情況下做這種修正可能會出問題,想象一下這種情況:玩家將延遲補償調(diào)得很大,比需要的量多出很多。從暫停中恢復(fù)后,玩家將聽到音樂開始播放,但游戲內(nèi)容并沒有開始移動!這顯然是很奇怪的,玩家調(diào)整延遲補償?shù)哪康氖窃谟螒騼?nèi)容開始運動時精準(zhǔn)地聽到歌曲開始播放。

這是利用暫停情況下接收器的靜止?fàn)顟B(tài)來修正延遲問題帶來的副產(chǎn)物。我們能否以某種方式解決這個問題?

  • 您可以通過增加一個恢復(fù)倒計時來解決這個問題,而且您必須確保這個倒計時比玩家可調(diào)整的最大延遲補償還要長。(所以您可以鎖定選項界面中的最大延遲調(diào)整范圍,比如說鎖定為3 s。)這使得靜止的等待時間變得更自然,因為您可以在倒計時中遮住游戲畫面。(在倒數(shù)到0之前,就已經(jīng)到預(yù)定的播放時間了。)

  • 您可以將接收器后退(到時間更早處)來補償延遲。然而這會導(dǎo)致一個可利用的漏洞,即玩家可以通過反復(fù)暫停和恢復(fù)游戲來使接收器持續(xù)后退。有些游戲?qū)⒅鳛橥婕矣螒蝮w驗(QoL)的一部分,例如DJMAX Respect不但增加了倒計時,甚至還會為您倒退時間。您可以在倒退時間的過程中掩蓋延遲。

  • 您可以讓接收器在恢復(fù)游戲后才開始向前運行,然而您可能被迫需要以靜音狀態(tài)播放一小段歌曲!為什么呢?因為靜音播放的那一部分實際上是我們的垃圾Android設(shè)備將音頻最終播出到我們的耳朵中所用的時間。當(dāng)預(yù)定播放時間到達時,音樂還在以靜音狀態(tài)播放,因此當(dāng)音樂最終重新出現(xiàn)時所播放的歌曲段落也會比您暫停時所在的段落更靠后一些,因為您為了在不發(fā)生奇怪停頓的前提下完成延遲補償而將其作為“代價”。實現(xiàn)這種方案需要您故意事先將音頻位置向未來移動,到比想要矯正的延遲更靠前的位置,并通過PlayScheduled讓它準(zhǔn)確地回到正確狀態(tài),(換句話說,每次您暫停并恢復(fù)后,暫停點前方的那一點歌曲就再也聽不到了,因為音頻會被向前調(diào)整一些來對齊靜音播放部分的末尾。)這種情況中,在歌曲播放時不會因校準(zhǔn)錯誤而出現(xiàn)奇怪的暫停,但是過度校準(zhǔn)會讓音樂在靜音狀態(tài)下播放更久。【譯注:原文對該方案的描述較難理解,建議閱讀時通過畫圖梳理思路,或先閱讀后文中對該方案的詳細介紹。拋開原文中引入的“靜音播放”這一概念,以如下思路理解可能會更清晰一些:為了不讓音樂實際播出時間早于譜面運動時間 → 所以在譜面開始運動的那一幀(或之后)才下達播放音樂的指令(而非預(yù)先下達) → 由于延遲,實際聽到音頻時,譜面已經(jīng)向前方運動了一段,因此需要預(yù)先將音頻的播放位置向前方調(diào)整一些,才能讓聽到的音樂和譜面對齊。這里音頻需要向前調(diào)整的最小值就是玩家設(shè)置的延遲補償值,因此作者將這一段音頻內(nèi)容損失稱為補償延遲的“代價”?!?/p>

我更推薦最后一個方案,因為玩家很可能在暫?;謴?fù)后將注意力集中于移動中的Note上(使用“視力”而不是耳朵來讀譜),所以這種方案可能好于音樂開始重新播放而Note還沒有移動起來,也好于接收器位置被自行拽到另一個地方。

如何實現(xiàn)以靜音狀態(tài)播放

在您決定從某個接收器時間開始游戲后,您所聽到的內(nèi)容并非由當(dāng)前時間決定,而是由向未來方向的校準(zhǔn)時間決定。

假定您的接收器在2000 ms,延遲補償為0 s,偏移量為500 ms。通常情況下您會想要在此刻聽到1500 ms的音頻,但實際上您需要將等待中的音頻時間設(shè)置為1533.33 ms而非1500 ms(核心區(qū)別就在于此)。

接下來,在開始播放的那一幀中盡可能早地請求“當(dāng)前DSP”,此時請求的結(jié)果與我們所依賴的增量時間一致性最強。(請記住DSP時間會在您每次請求間發(fā)生變化。)將1533.33 ms處的音頻預(yù)定在當(dāng)前DSP + 33.33 ms時開始播放。這一幀中我們不會對接收器時間做任何處理,此處的“不做處理”只有當(dāng)我們設(shè)法在該幀的最開頭請求DSP時間(盡可能接近實際的幀時間)才有意義,所以我們準(zhǔn)備在下一幀做一些處理并期望增量時間和DSP時間同步前進。

在下一幀中,我們將增量時間加到接收器上(這意味著游戲的游玩過程已經(jīng)開始,現(xiàn)在需要打開判定和其他所有內(nèi)容)?;叵胍幌轮暗姆桨?,我們不斷檢查DSP時間,直到它超過DSP + 33.33 ms時才真正開始移動接收器,然后我們會修正超時。但當(dāng)前方案中音頻已經(jīng)被前推,所以我們可以立刻開始移動接收器。在本幀中,DSP時間可能是1516.66 ms左右,并沒有到達預(yù)定時間(33.33 ms大約是2幀),但我們已經(jīng)“在靜音狀態(tài)下播放”了!(您可以在這段靜音時間中接到或錯過Note)當(dāng)最終到達預(yù)定時間時,DSP時間將位于我們以靜音狀態(tài)播放到的時間。這33.33 ms的音頻內(nèi)容完全消失了,就好像他們在靜音期間以0音量播放,然后突然恢復(fù)了正常音量。

現(xiàn)在讓我們試著在這一團亂麻中加入100 ms的延遲補償。(請記住,負延遲是沒有意義的)將音頻時間設(shè)置為1633.33 ms,但仍然預(yù)定在DSP + 33.33 ms時間點播放(這依然是可以讓PlaySchedule正確工作的最小時間)。此時預(yù)定的播放行為依然發(fā)生在舊的時間點上,但垃圾Android設(shè)備的延遲讓音頻更晚播出,我們將在靜音狀態(tài)下播放更久。但是當(dāng)音頻播放出來時,可以看到音頻的位置在1633.33 ms而不是1533.33 ms。音頻當(dāng)然會因延遲而更晚播出,但音頻的位置已經(jīng)被前推以抵消延遲,計劃通。

這里需要注意一件非常重要的事:無論什么情況下(指延遲補償、接收器時間和偏移量的取值),預(yù)定的播放時間似乎都固定為當(dāng)前DSP + 33.33 ms。這是因為我們將一切其他問題都用推后音頻時間(并且忍受靜音播放)來解決,而我們對預(yù)定播放的唯一需求來自PlayScheduled正確工作所需的未來時間。(請告訴我您在此時已經(jīng)完全理解并點頭認同,否則您可以試著喝點含糖飲料后再來閱讀這篇文章)。稍后我會揭示這并非100%正確(哈哈),但注意到這一點很有用。

額外內(nèi)容:您注意到了當(dāng)預(yù)定時間到來時,接收器已經(jīng)提前運行了起來。而我們?nèi)匀豢梢匀诤稀俺瑫r修正”方案,不斷詢問DSP時間直至其超過預(yù)定時刻,并在此時如先前一樣微調(diào)接收器時間。(不同點在于此時接收器時間已經(jīng)在運行,被推后的音頻時間反而在等待。延遲使得我們在進行微調(diào)的時刻依然聽不到任何內(nèi)容。)如果您更傾向于讓接收器流暢運行或害怕玩家會感受到接收器時間的跳轉(zhuǎn)(這會影響渲染),您可以跳過這個部分。與先前不同的是,我們在先前方案中并沒有使接收器運動起來,所以有可能在靜止?fàn)顟B(tài)中完全掩蓋“修正”過程。而現(xiàn)在這里變成一個權(quán)衡點:您是否愿意在這個唯一確定的時刻修正它,而付出一點連續(xù)性為代價?這取決于設(shè)備的糟糕程度。如果您選擇不修正,那么即使是最差的設(shè)備也能在預(yù)定時刻流暢地繼續(xù)游戲,但缺失修復(fù)使游戲時間存在偏差。

現(xiàn)在讓我們考慮這樣一種情況:計算結(jié)果要求“聽到”的音頻位置為負數(shù)。如果接收器位置在200 ms,偏移量為500 ms,那么我們現(xiàn)在想要聽到的音頻時間為-300 ms,這顯然沒有什么實際意義。真實情況是音頻被偏移到了未來,以至于200 ms的接收器位置下音頻還沒有開始?;蛘叻催^來說,偏移量使得當(dāng)前的接收器時間過于靠前,從而進入了負數(shù)時間的范圍內(nèi)而不是在音頻的開頭。

如果我們和先前一樣計算,則音頻時間會被設(shè)置在 -300 ms + 33.33 ms + 延遲。這可能仍然是個負值……或者當(dāng)延遲補償設(shè)置得比較大時可能會是正值。我指的是接收器時間 – 偏移量 + 33.33 ms(最小未來時間) + 延遲的計算結(jié)果是負值的情況。如果您能計算出正值,那么可以像之前那樣處理。問題在于我們并不能事先將等待播放的音頻時間設(shè)置為負值,必須要做一些其他工作來解決。

這就是我讓您留意的那個“并非100%正確”的東西:固定為當(dāng)前DSP時間 + 33.33 ms的預(yù)定播放時間!這種情況不正是可以被視為“仍在音頻播放前”嗎?您要做的就是將這一段負數(shù)時間加到預(yù)定時間上,現(xiàn)在預(yù)定時間變?yōu)?span id="s0sssss00s" class="color-yellow-04">當(dāng)前DSP + 33.33 ms + (負數(shù)時間 * -1)。您只需要把這一段負數(shù)時間扔進去,然后立刻進行預(yù)定。這樣就可以事先將音頻設(shè)置在0 s處而非無法實現(xiàn)的負值。這里有一個陷阱,當(dāng)該值為正值時不能在當(dāng)前DSP + 33.33 ms上再加任何量(因為這種情況通過事先設(shè)置音頻時間來處理)。您可以簡單地使用if來區(qū)分正負兩種情況,不要冒著大腦過載的風(fēng)險嘗試總結(jié)出一個通用表達。

我相信您已經(jīng)設(shè)計出了一個算法,可以從任何接收器時間正確地播放音頻,無論正負,也無論在歌曲前、歌曲中甚至是歌曲后?。ㄓ捎?span id="s0sssss00s" class="color-yellow-04">AudioSource并不會循環(huán),因此若您事先設(shè)置的音頻時間在歌曲結(jié)束之后,您將聽不到任何內(nèi)容。)恭喜您!??

請您在從暫停中恢復(fù)時設(shè)置一些延遲時間,以防玩家在預(yù)定的播放時間還未到達時再次按下暫停按鈕。否則歌曲可能會在暫停界面上播放出來,或者您可以做一些工作來取消預(yù)定的播放。

其他幾種“校準(zhǔn)

還沒有結(jié)束,讓我們再多討論一些!

在許多游戲的選項界面中可以看到,它允許您通過按“+”和“-”按鈕來以一種未知的單位對游戲進行“校準(zhǔn)”?,F(xiàn)在“校準(zhǔn)到0.5”和“校準(zhǔn)到-0.5”都具有了意義,這是什么情況呢?負數(shù)延遲不是沒有意義的嗎?

游戲沒有直白地說明校準(zhǔn)單位是什么,或者只是在玩家使用“+”和“-”按鈕校準(zhǔn)時顯示一個由運動的小節(jié)構(gòu)成的測試游戲區(qū),正是因為這個話題太傷腦筋了。(看看本文已經(jīng)寫了多長,只為了播放這該死的音樂。)

第一,這些值有可能實際上是對音樂的偏移量進行增減,但有時這會導(dǎo)致一種延遲并沒有改變的感覺。這種情況下該值的正負均有意義。如果游戲只是如此設(shè)計而不運用任何其他技巧,那么它只能在從頭重啟時進行補償。一些游戲可能會嘗試將正值校準(zhǔn)轉(zhuǎn)換為我們一直討論的延遲補償,而負值校準(zhǔn)轉(zhuǎn)換為偏移量。

調(diào)整歌曲的偏移量在一些情況下是有用的,例如當(dāng)玩家不信任開發(fā)者提供的偏移量時,或者只是不同玩家對一些歌曲的聽感不同,有人覺得晚了,有人覺得早了。額外提供偏移量校準(zhǔn)也許是對偏移量出錯的一種很好的兜底保障。

第二,這些值也可能是一種輸入校準(zhǔn)/判定校準(zhǔn)。這種校準(zhǔn)讓您即使在接收器時間剛好是Note時間的那一幀上檢測到輸入,也無法達到完美判定,因為輸入(或判定)已經(jīng)發(fā)生了偏移。采用這種做法是為了幫助輸入延遲較差的設(shè)備。玩家的眼力是完美的,所以他會嘗試在Note真正和接收器重合的時刻點擊屏幕。(該幀正是接收器時間和Note時間相同的那一幀,請記住接收器時間是如何影響渲染的。)當(dāng)然,輸入并不會在精確重合的這一幀中到達,而是可能在一幀后或幾幀后。如果您的眼力真的很強,那么可以看到Note從接收器上移過(因為輸入到達需要時間),最終程序在某一幀前檢測到了輸入,并且讓Note消失。

但如果您將游戲設(shè)計成“在Note和接收器重合的那一幀點擊”,輸入將永遠更晚到達。雖然我認為這是正確的設(shè)計,因為這樣向玩家描述會比解釋“好吧,其實您的輸入稍后才能到達代碼中,哈哈”要容易得多。但是您無法讓Note在玩家手指接觸到屏幕的那一幀中立刻消失,這些Note會不可避免地繼續(xù)移動一段時間。

輸入延遲為0 ms的設(shè)備是不存在的,所以如果您想讓在Note和接收器視覺上重合時進行點擊的玩家能獲得最佳判定分數(shù),那么您應(yīng)當(dāng)在默認情況下就做一些輸入校準(zhǔn),此時接收器再向下一點的地方才是真正的最佳位置。這讓游戲變得以玩家為中心(優(yōu)先考慮玩家視覺所見)而非以代碼為中心(代碼并不關(guān)心輸入多久才能到達)。

否則游戲就會變得經(jīng)常被玩家評論為:“嘿,您需要在Note到達那條線之前一點的位置點擊屏幕,才能得到完美判定?!边@種方案也不錯!因為玩家會明白不同設(shè)備的性能不同,買不起輸入延遲更低的設(shè)備是他自己的問題,并且他會理解為什么他需要稍早一點進行點擊。之后他就會一直提前點擊。

或者您可以通過設(shè)置一些默認的輸入校準(zhǔn)來平衡這種情況,因為您知道輸入延遲為0 ms的設(shè)備并不存在。但如果您不想讓游戲設(shè)置包含輸入校準(zhǔn)的話,一些便宜的設(shè)備仍然需要提前點擊。而提供延遲、音樂偏移量和輸入三種校準(zhǔn)有時可能顯得太多且難以理解。

結(jié)語

終于結(jié)束了!您忍受我闡述了這么久,所以作為額外內(nèi)容,這一部分其實是我自己的一次規(guī)模龐大的小黃鴨調(diào)試(https://en.wikipedia.org/wiki/Rubber_duck_debugging)。如果我不把這些文字都寫出來,我的思維可能永遠無法超越前文向您呈現(xiàn)的思路中的前幾個小段。這個方案只是……可以正確工作。

并且很抱歉的是后面的話題完全否定了前面的推論,但我發(fā)現(xiàn)如果不先經(jīng)過前面的部分就無法理解最終的解決方案。本文到此結(jié)束?。『靡?!

【翻譯】音游開發(fā)速成:背景音軌(作者:5argon)的評論 (共 條)

分享到微博請遵守國家法律
汉寿县| 雷山县| 盈江县| 根河市| 台安县| 宁南县| 吉隆县| 利津县| 浦北县| 仪陇县| 固安县| 邹城市| 巴林左旗| 北安市| 清水河县| 象山县| 旬邑县| 天峻县| 三门县| 合肥市| 东源县| 松阳县| 岳池县| 济宁市| 仲巴县| 孟连| 玛纳斯县| 六安市| 祁阳县| 莫力| 上饶县| 大兴区| 铁岭县| 奉贤区| 赫章县| 昭苏县| 招远市| 大荔县| 肇东市| 永年县| 池州市|