【翻译】音游开发速成:概论(作者:5argon) 您所在的位置:网站首页 unity新手入门难度 【翻译】音游开发速成:概论(作者:5argon)

【翻译】音游开发速成:概论(作者:5argon)

2024-06-17 19:12| 来源: 网络整理| 查看: 265

【文章原载于https://exceed7.com/native-audio/rhythm-game-crash-course/index.html,授权翻译转载,再次转载请联系原作者获取授权。】

【译注:

本文作者5argon是Native Audio的开发者,这是Unity引擎中用于解决“移动端声音播放延迟较高”这一问题的第三方解决方案,而本文就发布在Native Audio的官网上。作者5argon有丰富的音游游玩经验,并有一定的音游开发实践经历(作品Mel Cadence),同时具备音乐创作能力,曾为VOEZ、Dynamix和Pump It Up等音游供曲,对音游的游戏设计和制谱等问题具有独到见解。另外作者拥有计算机工程学士学位和互动媒体设计硕士学位,对音游开发中的许多技术问题理解极为深刻,曾长期更新Unity游戏开发技术博客(许多内容与音游开发直接相关)。

本系列文章中,作者主要讨论了音游开发中与音频相关的技术问题,以及游戏设计和制谱相关问题,内容见系列目录。系列文章成文较早(约为2019-2020年间),参考时请注意时效问题。】

【系列文章目录】

音游开发速成(概论)【本文】

背景音轨

游戏设计与制谱

四类音频应用程序

音频导入设置

与DSP同步

来自Bemuse作者的提示

音游开发速成(概论)

我猜许多对Native Audio感兴趣的开发者都正在开发一款基于Unity的音游,因此我不妨针对音游开发提供一些指南,从而帮您节约开发时间。

其中部分内容与Native Audio无关,如果您不知道什么是Native Audio或Unity,或许也能从这篇文章中学到些什么!

祝您在这种小众而美妙的游戏门类的创作之旅中一切顺利。

您的游戏应当有趣

在运用这些技术手段让游戏正确运行起来之前,应该首先考虑用键型配置(pattern)和游戏设计使游戏变得有趣。我建议您先去看看《游戏设计与制谱》,然后再利用本文的信息解决技术方面问题。

我认为自己不会发布一款无趣的游戏。这个道理不言而喻,但无论您是否相信,当真正做出一款游戏后,您就会感受到发布它的诱惑,即使您的内心并不这么认为。更不必说您将面对来自亲戚朋友们的“意见风暴”,诸如“就先发布一个最小可用产品(然后由此继续改进)”或“发布总比什么都不做要强”。找到您对作品质量的“底线”标准,然后告诉他们:“只是可以游玩是不够的”。如果您有丰富的音游经验,您就可以清晰地听到自己的心声:“我的游戏还远不如其他游戏有趣”。

播放背景音轨

除非您正在做一种从一无所有的状态中创造出音乐的游戏,否则您的游戏中必然有某种背景音轨。您无需使用Native Audio处理背景音轨,因为它不属于反馈音,使用Unity的AudioSource + AudioClip方案就很好。

在此基础上您需要使用一些技巧来尽可能让背景音乐和Unity的时间值相同步,由此便可以判定玩家操作的准度,这个话题稍后再讨论。

比如在一个根据压缩后的背景音乐敲鼓的游戏中,您可以使用Native Audio处理鼓声,从而获得最及时的反馈,同时这不会占用很多空间。Native Audio用于解决反馈音的延迟问题,这些声音长度较短且无法事先预测是否会被播放,而且您希望在它需要被播放时能尽快被播放出来。

例如在敲鼓游戏中,如果您不点击屏幕,鼓声将不会被播放。而在收集金币的游戏中,如果玩家错过了金币,收集音效也不会被播放。

但如果您的游戏中只有必定会被播放的背景音轨,那么问题可以使用固定偏移量或校准来解决,而不是使用Native Audio。

背景音轨的延迟问题

在音游中,您必须先让背景音轨与第一个“Note”(这里的Note具体指什么取决于您的游戏)对齐,后续部分就会保持正确,除非游戏或音频发生卡顿。90%的情况是游戏发生卡顿而音频走在了前面,这是由于执行播放指令后,音乐和游戏并不在同一个处理单元中运行。这种卡顿需要在游戏和音频中分别解决,本文不会立刻讨论这个问题。

Unity中的音频是“即发即忘”的(fire and forget,发射后就不用再管)。当您要求Unity的AudioSource开始播放时,AudioSource将为此花费不定长的时间,并在它觉得恰当的时候实际播放出来。实际播放行为最快也会发生在当前帧的末尾,因为Unity存在一种机制来实现混音并依优先级决定所播放的音频源,这意味着Unity必须先收集所有播放指令再实施播放,而不能在发出指令的那一行代码处立刻开始播放。

该帧结束后,获取数据和驱动扬声器的速度取决于设备自身。每个设备(特别是Android设备)的音频延迟都不同。

我们难以在游戏中计算这种延迟,除非拥有一些合适的装备(如环回线缆loopback cable)或一些复杂的技巧(如令设备录制其自身扬声器以自动校准)。

所以Android设备上的背景音轨问题通常通过玩家自行校准来解决,因为不同Android设备间的音频延迟各不相同。如果只有背景音轨而没有反馈音,那么我认为这是最好的做法。

当玩家设置了本设备正确的偏移量后,您的任务是确保该偏移量始终不变,即在每次播放的开头都保持该值相同。一些音游在手工校准后依然存在问题,因为每次重启游戏后这个偏移量都会改变。这是程序员的错误,并且这样的错误会使玩家对他是否校准正确感到懊恼,从而使玩家反复回到校准界面,即使这并非他的过错。

精准地启动音乐

如上一节所提到的:当玩家帮您解决特定设备的延迟问题后,您需要保证这个值每次都被正确执行(包括每次重开,冲分玩家会重开很多次)。

因地制宜地预加载音频。怎么做到“因地制宜”?这进一步取决于您的音频导入类型!更多相关信息请参考《音频导入设置》。

字面意义上的“立即播放”是不可能实现的,唯一的解决方案是使用AudioSource.PlayScheduled(https://docs.unity3d.com/ScriptReference/AudioSource.PlayScheduled.html),该方法可以指定一个未来的精确时间点。您的音频将被故意延后播放,但会更精确地在所指定的时间播放出来。这比要求引擎立即播放但并不能真正实现“立即播放”的效果更好。该方法使用dspTime,请留意您请求AudioSettings.dspTime的位置,因为这个值可能在各行代码间发生改变(也可能不变)。唯一需要确保的是这个时间点应当足够远,以供音频“准备完成”。如果您指示的时间点是“当下”(即AudioSettings.dspTime本身或更早的时间,这是一个不可能完成的请求),这个方法将不会比audioSource.Play更好,它无法满足您对“当下”的请求。

请根据您在此处使用的未来时间点,将您的游戏事件(Note或第一个小节,或者和您游戏相关的某些事物)对齐到尽可能靠近该时间点的位置。由于Unity基于帧和游戏循环,所以瞄准未来的某一帧从而准确地在所预定的时间点启动是不可能的。因此您的逻辑必须用某种方法在计算中涵盖“超时”(即您所设置的预定时间和该时间实际落到的那一帧之间的时间差),从而表现得像预定时间准确地落到您启动游戏的那一帧一样。

关于处理背景音轨问题的全面论述,请参阅《背景音轨》(这篇文章需要一定时间来消化)。

反馈音问题

 (终于谈到Native Audio的工作了!)

当背景音轨正确播放后,唯一剩下的问题是您游戏中可能存在的反馈音,如区分perfect/great/bad的音效,或收集金币的音效。反馈音无法被校准/补偿,所以最好依赖于一种以尽可能低的延迟播放音效的方法。请参阅《四类音频应用程序》并理解为什么有反馈音的游戏(如音游)是最难解决的。

这最终成为Native Audio的一项任务。用这个库实现尽可能及时地播放音效并不困难。

您需要理解:立即播放的准确性总是不如正确校准,但校准并不能用于反馈音,因为您必须让音效移动到更早的时刻播放从而补偿延迟(而延迟导致音效更晚被播放出)。您无法将作为响应的音效移动到比造成该音效的输入更早的位置,除非您会预知魔法或用神经网络之类的技术预测出玩家必然点击屏幕并激活反馈音的时刻。

有一些有趣的情况实际上是可以预测或巧妙应对的,这取决于您的游戏。例如如果您的游戏有一个用于长条的音效,并且在击中长条头部时播放该音效,那么或许可以稍晚一些播放。因为这个音效的播放是持续不断的,并且稍晚一些不会大幅破坏游戏体验。(所以您可能并不需要Native Audio。)或者如果您的游戏中有一种Note,在Miss和击中时分别播放两种音效,那会发生什么呢?这种情况下您可以将击中音效恰当地设计成融合了Miss音效的效果,这样您就可以在确认玩家输入前预先播放Miss音效。如果玩家击中了Note,那么播放的Miss音效将可以成为稍后播放的击中音效的一部分。这种方案能正确工作的原因是该音效在无输入(Miss)时也会播放。相信以您的游戏为基础还可以做出其他巧妙设计。

一些判定时机的数值

如果您的背景音轨或反馈音只晚了10-20 ms,那问题真的严重吗?为了客观地看待这个问题,下面将展示一位音游大佬从诸多游戏中整理出的“最佳判定”时间窗口。

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【译注:原推文已无法查看】

VOEZ是30 ms

IIDX(弐寺)是20 ms

DDR >>>15ms<<<

Cytus是70 ms

— StaLight(@Sta_Light_)2018.7.22【译注:原推文已无法查看】

请注意,这些数字并不是随机选取的,而是大致为单帧时间(60 FPS下是16.666 ms,120 FPS下是8.3333 ms)的若干倍,因此检查判定可以基于帧内时间而非真实的“硬件输入时间”。

无论如何让我们先来看看DDR,其Marvelous判定的时机只有60 FPS下的1帧(不确定机台在120FPS下运行时会怎么样,或许您可以在这个时间窗口内“看到”箭头移动了2步),并且有玩家可以像这样MFC(Marvelous Full Combo)一首难度离谱的歌曲:https://youtu.be/r2Z-qi9IPF8。

这意味着人类肯定可以连续2分钟在16.67 ms的时间窗口下不出错。这个很小的窗口时间值实际上是相当重要的。

音频卡顿较为少见

这是一个或许不那么明显的关键点。您的游戏中的所有内容都是随游戏循环一同更新的,这种更新可能基于每一帧的deltaTime。无论您如何运用deltaTime,音频总是向前播放而不会随游戏发生卡顿。如果游戏发生了卡顿,那么游戏可能就会落到音乐的“后面”。这种情况下需要进行“重新同步”。(否则玩家就会重开……)

音频卡顿源于缓存欠载(buffer underrun)

Unity会将音频缓存的大小设置为256、512或1024,这取决于您选择的设置是最佳延迟Best Latency/较优延迟Good Latency/最佳性能Best Performance中的哪一个。(我认为这将取决于设备,但就我做的测试而言,情况似乎并非如此。这个问题的答案在未来也无法得到确认。)

您可能希望让缓存尽可能小,从而获得最小延迟,但如果缓存过小,CPU就不能把扬声器正在播放的音频数据及时地提取到内存中。此时扬声器将不知道如何工作,从而产生人们所说的卡顿lagging、干扰glitching、可怕的声音scary、尖叫squealing、变慢slowdown、故障bugged、吱吱声squeaking、僵尸声zombie、结巴stuttering、混乱garbled(老实讲,我收到过很多症状描述,里面的形容词就没有重复过)。重要的是,此时您的游戏将走到音频前面。这种情况难以通过程序检测出来,最好的设置是以缓存足够大为前提,尽可能取最小值。

遗憾的是有些设备无法处理256甚至512大小的缓存。比如我听说华为P20 / P20 Pro / Mate 20 Pro / Mate 20X做不到(即使这些型号都是顶级手机产品!)。那些使用Unity内部方案而非Native Audio的音频(如BGM)将会变得混乱而无法使用。现在该怎么办呢?我能想到的最佳解决方案是在选项菜单下增加一个触发AudioSettings.Reset(https://docs.unity3d.com/ScriptReference/AudioSettings.Reset.html)的滑条,让玩家发现问题时进行调整。我接到反馈称前述设备能在1024大小的缓存下正确工作。令人惊讶的是Native Audio在那些设备上使用远低于1024的值(约为240)且没有发生缓存欠载。唯一的结论就是Unity在音频处理中投入的工作量对于那些设备而言太多了,以至于任何更低的缓存量都无法处理。这个“bug”早在5.5.5版本中就被发现了,并且至今仍然存在(https://issuetracker.unity3d.com/issues/android-sound-stuttering-issue-on-huawei-mate-20-pro)!天啊!

出于某种原因,在Windows构建中最佳延迟(Best Latency)选项必然导致缓冲区大小无法使用。Unity你怎么回事???

初始校准问题

如果您考虑在Android设备上游玩,那就必须在选项界面提供校准功能。然而想象一下,一个萌新并不知道如何游玩,也不知道选项界面在哪。他入了坑,去玩教程,然后因为延迟问题而失败,那么随后他就会退出游戏。您绝对不希望发生这种事。

许多游戏会在教程结束之后弹出一个窗口说:“如果您刚才的游玩体验不正确,请前往选项界面”,这是因为把这个通知放到教程前面会打断“新手引导仪式”,使一些像我妈妈一样的非技术玩家感到困惑。

更好的做法是预设一定的校准值,这使得新玩家也能享受到教程(可能还有一些简单歌曲),直到他意识到校准错误阻碍了他的游玩。那时您的玩家已经投入到游戏中了,不太可能退出。

最简单的方法是使用“魔数”(magic number),您需要非常确信没有任何Android设备在延迟方面的性能要好于这个数。但更好的做法是以某种方式“估计”每个设备的延迟,并用这个延迟开启游戏。问题是如何做到?一些中间件确实具备估计功能,如Criware的adx2,它能用一些黑科技为您提供可信的估计值,而并不需要特殊校准工具(如环回线缆)和麦克风输入。(这项技术是闭源的!)我希望我的Native Audio也能有这个功能,但目前……先忍一忍吧。

您的“0 ms”延迟是什么?您可以将初始校准值作为“0 ms”显示在UI上,但其背后填写了一个固定值。这可以让玩家感到“0 ms”已经比较准确。如果您的0 ms是字面上的0 ms,那它很可能永远是错的。即使认为设备是完美的,如果玩家不用耳机的话,声音在空气中传播也需要时间。考虑将这一小段时间纳入0 ms也是一个好主意,例如把0 ms定为实际的5-10 ms(提前播放)。

您可能想要通过查阅一个“延迟数据库”来使每个设备都从正确的延迟值开始。Superpowered网站(https://superpowered.com/latency)上有一个数据库,但我还没有找到一个足够好的方法在运行时将设备映射到这个数据库上。祝您好运!

判定问题

玩家根据音乐点击屏幕,但您的代码如何判定这次点击?当您有机会运行您的判定逻辑时(一帧到来),就已经落后于对应的音频时刻了,这是100%的事实。如果您使用该帧开始的时间做判定,那么仍然会比实际点击时间要晚,因为玩家必须在前一帧内点击屏幕,才能在这一帧被检测到。使用这个时间做判定会使得偏早点击的玩家获得优势(因为点击时间被稍稍后移),而偏晚点击的玩家会受到惩罚,这可能导致判定结果降级。

正确的解决方案是触控时间戳(touch timestamp)。所有原生Android和iOS系统都提供了这种时间戳,但Unity把它们全抛弃了,让帧时间成为最准确的时间。如果您对提高判定准确度感兴趣,可以试试The new Input System(https://www.youtube.com/watch?v=hw3Gk5PoZ6A)。

“手指敲击音”(“finger nail”)玩家

我相信现在的玩家已经学会了在设备延迟较差时关闭点击音效,所以请确保为他们提供一个关掉反馈音的选项。

但是!经验丰富的玩家甚至会故意关掉点击音效,无论其设备延迟如何(即便他们可能使用的是iOS这种原生且延迟最低的设备)。因为他们的手指敲击音将会成为最准确的反馈音直接传到耳朵中。这些玩家将对游戏进行校准,使得最佳判定的时机表现为:手指敲击音穿过空气的用时与音频通过硬件延迟后再穿过过空气/耳机的用时相匹配。因此包含一个校准选项和点击音效开关是非常重要的,这使得玩家可以如上操作。

这就是为什么DDR的判定如此严格,但玩家依然有办法命中。因为玩家可以让鞋子发出的脚步声和音乐相匹配,并真正感受到节拍(在这个游戏中您的身体甚至会同时振动,而不需要额外的硬件反馈)。DDR是现存为数不多的几个不允许在游戏内以任何方式校准的音游之一。这并不是因为他们可以确保机台没有延迟(这是不可能的),而是因为机台已经被精确地校准过了,如果您鞋子的脚步声(经空气传播)匹配上了您从机台听到的音乐(经过扬声器到踏板间的站立距离),那么您将得到一个Marvelous判定(16 ms,60 FPS下的1帧)。这样考虑的话,DDR游戏代码必须提前播放音频,从而使音频在通过硬件和空气的过程中被延后到最佳时刻,这个时刻正是您的脚步声不经过硬件延迟到达耳朵的时刻,这也是箭头(arrow)匹配到接收器(receptor)的时刻。(空气中的声速也会随气温变化(https://www.nde-ed.org/Physics/Sound/tempandspeed.xhtml),但别因为打不出好判定而要求机厅小哥调低空调!)

不要将游戏状态同步到音频时间/dspTime

您可能会想到这种方案:播放音乐并从硬件逐帧请求当前的音乐时间,然后根据这个时间决定屏幕上的一切内容(结合速度调节器(speed mod)等游戏机制)。通过这种方法使游戏和音频始终保持同步。

这并不是一个好注意。首先,音频时间是dspTime。dspTime不是实时的,而是会以一种特定步骤更新。所以您无法得知此刻音频正在播放哪里。这听起来有些荒谬,但想象一下,音频是一种“即发即忘”的东西,就像发射火箭一样。当您想知道火箭的位置时,您或许可以测量一下,但当测量完成时火箭已经自己飞快地离开了,这让测量结果不再具备实用价值。从技术的角度讲,音频运行在自己的线程中,并且测量行为不能阻塞这个线程。您所听到的内容来自于流水线上的一个处理链条,它不断把音频数据灌到扬声器中。这个“流水线”并不像指示播放位置(“播放头在这,您听到的内容是这个”)一样简单,而是一些音频数据已经在排队并等待播放(这并不是您所请求的当前位置)。所以通过请求音频时间得到的结果并不代表您当前听到的内容。在iOS上您或许可以这样处理,但您会发现Android平台返回的时间不够流畅(无论通过Unity的API还是Native Audio的API,结果都是如此)。即使游戏“从技术上”与音频时间始终同步,这种同步也不够流畅,这带来的游戏体验比流畅游玩却因卡顿而不断偏离音频的游戏体验更令人恼火。

相反,您应该让游戏基于自己维护的浮点变量,并让该变量逐帧增加。在维护了这个浮点变量的基础上,让播放音频的方法总是可靠地请求该变量的值,并将该值解读为音频时间。这种方案下,您必须寄希望于该变量值在一段时间后仍与音频同步前进。但在做这种期望之前,应当通过一些手段来保证正确起步,比如让音频预定在某个精确的未来时刻开始播放,并使用您的浮点变量“锚定”这个未来时刻,对应的浮点变量值就是音频的“第0秒”。请记住,立即播放永远做不到立即,而延迟到未来是更好的选择。

如果您的游戏完美运行,那么浮点变量应该是可靠的。而如前所述,如果游戏发生卡顿,“即发即忘”的音频可能不会随游戏一同卡顿,这会导致您的浮点变量落后,而后您可能必须重新同步:要么让音频跳转到浮点变量处,要么让浮点变量跳转到音频处(但这会导致游戏的跳转,因为您的游戏状态是基于浮点变量值的)。不幸的是,如果音频因为缓存欠载或其他原因发生卡顿,这将很难修正。您只能寄希望于玩家重启这首歌曲并重试。

有什么其他提示吗?

2019.1改进了Android上的普通Unity音频延迟,目前更多设备可以收到更快的音频源,请参阅这篇文章(https://gametorrahod.com/unitys-android-audio-latency-improvement-in-2019-1-0)。如果您的游戏一部分使用了Native Audio,但较长的声音内容(以及需要使用混音器或效果器的声音内容)使用了Unity的方案,那么这个改进是有用的。

在iOS上使用Native Audio时请不要忘了把Project Settings的Audio选项设置为最佳延迟(Best Latency)。而在Android中无所谓,因为这个设置只会影响Unity申请的音频源,而我们会使用自己的设置申请一个新的。但iOS中这个设置会修改设备的缓冲区大小,而这个缓冲区是Unity和Native Audio共用的。Native Audio并不会尝试影响Unity的当前设置,所以在iOS上选择最佳性能(Best Performance)选项也会降低Native Audio的速度。(您仍然可以通过跳过Unity的混音器来改善播放延迟,并且能获得帧内即时播放的优势而不必等到帧尾。)

如果您的设备是插电状态,触控将会因为静电而随机变慢。由于输入用时会影响对音频延迟的感知,因此这种现象可能会让您产生音频不时变慢的错觉。

请检查您的音频文件开头是否有一段无声部分。像Audacity之类的程序提供了立即修剪头尾无声部分的命令。

尖锐而突发的音频带来的反馈感更强,而缓慢淡入的音频会让人感觉有延迟(这个延迟实际上是音频内容的一部分)。如果您自己合成SFX,那么可以通过声音设计来改善玩家感知到的延迟。

在前几点的基础上,您还可以修改原生端的触控处理入口来调用原生端的Native Audio,从而完全避免经过Unity,来达到更快且配合度超强的音频播放效果!这种方案就像写一个原生游戏一样!然而这种hack行为将会非常混乱:您如何在纯原生端检查Unity的内容,从而检查一些触控条件?(除非您的游戏无论在何种情况下都要在玩家点击屏幕时播放音效)这里只是想指出没有什么方案能比这更快了。(我的插件也不支持这种可怕的优化,但如果您想的话可以hack进去……)



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

      专题文章
        CopyRight 2018-2019 实验室设备网 版权所有