一、前言
在独立游戏的制作流程中,音频往往是最晚介入且资源最有限的环节。在《呓语小镇》中,我们的团队于 2024 年 10 月中旬正式接入项目音频开发。当时游戏的主要功能模块已基本完成,制作组正处于批量填充内容的阶段。我们初步评估后发现,正式版的音频需求将包括约 40 分钟的音乐(后增加至 50 分钟)以及 1000 余条音效(最终增加至 2000 余条)。
与此同时,我们收到的制作排期是项目将在 2025 年 2 月发售。除去假期与 QA 测试周期,真正留给音频开发的时间仅剩四个月。换句话说,我们不仅要在四个月内交付全部音乐与音效,还需从零完成所有音频的引擎集成与复杂功能开发。虽然项目最终延期至 2025 年 8 月上线,但我们在开发初期并不掌握这一信息,因此仍必须以最初计划为目标推进。
在如此紧迫的时间条件下,构建高效的自动化工作流成为唯一可行的选择。AI 工具的辅助与 Wwise 的灵活特性,使我们在未引入任何音频程序或技术人员的情况下,独立完成了整个自动化系统的设计与实现,极大地节约了人力成本。以下章节将详细介绍这套系统的构建思路与具体方法。
二、基于标签的自动化方案
在《呓语小镇》中,绝大部分音效内容都来自家具物品的交互。针对家具物品的管理,《呓语小镇》并没有采用大家熟知的表格管理方案,而是使用了一套专门定制的物品管理器——这使得用表格来进行音频集成的方案变得极为不经济。然而,项目本身对如此大量的物体就有分类需求,每个物体已经由项目组策划打上了精细的分类标签,这发生在我们介入开发之前,和音频工作完全无关,但现在我们可以通过这些标签来快速定位每个正在交互的物体。于是我们想到可以从这些标签入手来做一些文章。
接下来关注我们的工作流目标,我们要创建一个具有如下特征的工作流程:
- 最大程度降低项目组程序侧的工作量,由我们发出的单个程序需求必须控制在程序员可以在半小时之内做完的程度。
- 实现音频侧的集成工作完全独立于项目组的工作流,做到彻底的互不干扰。
- 简化音频部门的操作流程,最好能做到从reaper里点击render按钮后,音频就自动出现在游戏引擎里。
基于这一前提,我们提出了基于标签读取的 Switch 自动化集成方案:
1.通过通用事件控制同类型家具的交互音效(例如“拿起”“放下”),将原本上千个 Wwise Event 压缩至两个。
2.使用 SwitchGroup 对音效进行分层分类,再以 SwitchValue 精确定位播放内容,类似于脚步声根据材质切换音效的机制。
3.通过两级分类体系(关卡类型 → 具体物品名称)避免单组 SwitchGroup 过于庞大。
这套系统最大的优势就在于,最复杂的工序(给物品打上标签)在我们接入开发之前就已经被完成了。如此一来,我们可以仅依靠两个事件就能控制游戏中约九成的音效播放,同时实现与主项目的完全解耦。音频部门仅需使用策划已完成的标签信息,所有集成都在 Wwise 内独立完成;上传内容时仅需更新 SoundBank,无需修改游戏引擎文件。
在此系统建立后,原本需要绑定在动画上的音效,也可通过交互事件延迟触发,从而由同一事件接口统一管理。这不仅实现了流程自动化,也极大减少了重复性人工操作。
现在我们完成了音频工作流与项目组工作流的完全独立,但是也随之而来了一个问题——这样看起来是独立了,但是实际上又在Wwise内产生了非常多的操作——为了集成一个音频,需要手动创建数个与标签对应的SwitchGroup与Value,分类管理一个复杂的层级结构,然后再把每个音频手动指派到其对应的SwitchValue上。这似乎并没有比从零开始创建一个表格工具更省事?或许我们还需要进一步的优化。不过,当我们仔细观察上述的工作流时,我们不难发现其非常具有规律性,完全可以通过创建一个自动化脚本来实现全自动导入。
三、语义系统
通过在音频文件的名称上注入自动化集成所需要的全部信息,我们得以在编写任何代码之前就完成这一脚本的创建。具体的命名规则例子如下:
Main_Item(5)_Down(5)(Room)_Luna(5)(Luna)_Bed(2)_02
总的来说,该文件是一个在名为Luna的关卡中,Bed物件的放下声音的其中一个。具体含义为:
- 用下划线 “_” 分隔不同层级。
- 括号外的字符为层级名称,例如第二层级 “Item”。
- 第一个括号内的数字代表层级类型(如 5 表示 SwitchContainer,2 表示 RandomContainer)。
- 第二个括号内的字符串表示该层级需绑定的 SwitchGroup 名称。若工程中不存在该 Group,则自动创建。
- 每个绑定了 SwitchGroup 的层级会在自身 Group 中创建与下一层级同名的 Value,并将下一层级指派至该 Value。
- 第一个层级默认视为 WorkUnit,最后一个层级默认视为 SFX。
为了更清晰地展示这套逻辑,让我们画个图来看看这个文件导入后的样子:
/%E5%B1%82%E7%BA%A7%E7%BB%93%E6%9E%84%E7%A4%BA%E6%84%8F%E5%9B%BE.png)
也就是说,在我们按照上述逻辑完整执行一遍后,这个文件就应该全自动地创建完全部的层级,待在它应该待的地方,并且所有的指派工作也都将自动执行。
四、使用AI完成脚本开发
AI工具的引入让设计师能够在不懂程序并且没有任何技术支持的情况下就完成这一脚本工具的创建。在上一章节中我们已经将所有的信息都集成在了这一套命名系统中,现在我们只需要将每个步骤写成设计师也能写出来的伪代码,逐次喂给AI就可以得到我们需要的工具。
接下来让我们省略这个和AI沟通的过程(大概花费半个人天),直接端上我们的成果:
/AI%E5%B7%A5%E5%85%B7%E7%9A%84%E7%95%8C%E9%9D%A2.png)
似乎运行得很完美,除了最后一个层级的默认信息显示有些问题外,其他部分都已经完美运行了。接下来看看导入后的成果:
/AI%E5%B7%A5%E5%85%B7%E5%AF%BC%E5%85%A5%E5%90%8E%E7%9A%84%E5%B1%82%E7%BA%A7%E7%BB%93%E6%9E%84.png)
/AI%E5%B7%A5%E5%85%B7%E5%AF%BC%E5%85%A5%E5%90%8E%E7%BB%91%E5%AE%9A%E7%9A%84SwitchGroup.png)
完全没有问题!所有的层级都被自动创建为正确的类型,工程中不存在的SwitchGroup与对应的Value也被创建,对应的指派工作也进行得非常完美。
现在我们有了完美的脚本,让我们回过头来看看这一套系统究竟是怎么运作的。
首先在游戏引擎端,举个例子,在Luna关卡中存在一个名为Bed的物品,当我们放下上述物品时,游戏引擎内发生的事情是:触发了“放下”事件,检测到放下的物品有“Luna”“Bed”标签,对这些标签执行SetSwitchValue。
于是,传给Wwise的信息是:播放play_putdown(该事件绑定播放Down层级) 事件,对“Room”SwitchGroup(用于容纳下一级分类信息的最高级Group)执行字符串为“Luna”的SetSwitchValue操作,对“Luna”SwitchGroup(容纳具体物品信息的次级Group)执行字符串为“Bed”的SetSwitchValue操作。
而我们此时恰好有一个名为Main_Item(5)_Down(5)(Room)_Luna(5)(Luna)_Bed(2)_02的音频文件,那么接下来会发生什么事情?在play_putdown事件中,恰好就存在在执行两次SetSwitchValue操作后指向的这么一个音频文件,那就恰好能播放声音。游戏引擎中的内容在我们初期进行一次性设置后就再也不需要关注,Wwise里的内容也由自动化脚本完成了设置,也就是说只要我们用脚本导入这个音频,那么这个声音就能自动从游戏引擎里长出来。
接下来让我们以开发组的视角看一看:
音频组提出了一个要求:“请将游戏内所有物品交互行为归类为“拿起、放下、使用”三类通用接口,触发接口时抓取物品上的标签,关卡标签用于 Room Group,物品名称标签用于与关卡标签同名的 Group,执行 SetSwitchValue。”
没了!这就是整个项目开发周期内,项目程序部门为了配合我们完成游戏内绝大部分音频内容集成自动化所需要进行的全部开发工作,工作量几乎可以忽略不计,从制作人的视角看,他们完成了这个需求后,后面所有的音频就源源不断地自己从游戏里长出来了,期间没有和他们的工作文件产生任何交集,也没有看到任何其他与音频相关的程序或者表格,只需要他们定期看看游戏里的音频表现验收即可。
再来看看音效设计师的视角:做完音效后,只需要按照我给出的文件名复制粘贴一下,然后在工具上点击一下导入,声音就在游戏引擎里出现了。这期间没有任何人操作过Wwise,也没有任何人操作过游戏引擎,总之工作就是这么完成了。不过要说省事的导入,ReaWwise工具似乎也可以做到类似的效果。但我们建立该工作流的目的在于:在这套模式下,负责音频集成的音效设计师可以在完全不了解Wwise的情况下独立完成从音频宿主到游戏引擎的全部集成工作——毕竟工作内容只有点两三个按钮。
不过目前还是有一些不足之处,在现在的情况下,为了集成一个音频进入游戏引擎,音效师至少需要点击3个按钮:reaper中的render按钮、脚本中的选择文件夹按钮以及脚本中的导入按钮。可我们在前面的章节中提到希望能达到这样的效果:最好能做到从reaper里点击render按钮后,音频就自动出现在游戏引擎里。接下来我们还要继续优化,将这些步骤全部集成到一个按钮当中。老规矩,我们还是要先设计出具体的执行方案:
- 点击reaper中的render按钮后,自动唤起导入脚本。
- 将导出文件的路径传递给导入脚本。
- 自动执行导入操作。
接下来继续请AI为Reaper写一个Lua脚本,有了具体的执行方案后,该脚本的编写对于AI来说就几乎没有任何难度了。此时我们再通过reaper的custom action功能将render键与该lua脚本组合起来。于是,我们彻底实现了本项目中的音频一键集成。
五、打直球的功能设计
除了上述的自动化功能外,我们在具体的音频系统搭建上同样也需要一些更加直接的方案,才能让我们赶得上工期。例如,即使作为一款2D游戏,我们在游戏内也设计了一些实时的立体声方位变化,用来增强游戏内声音的空间感。要做这种实时的3D方位感,最方便的方法当然是使用Wwise中的3D空间化设置。
/Wwise3D%E5%8A%9F%E8%83%BD%E7%A4%BA%E6%84%8F.png)
简单,方便,高效,只要启动该功能就能自动产生声音的3D方位效果。遗憾的是《呓语小镇》是一个2D项目,游戏中的物体与摄像机并不处在同一个平面内。事实上,在编辑器中,摄像机所处的位置距离实际游戏画面的位置极其遥远,这使得游戏内的物体不管怎么移动都仅仅只是在摄像机前方一个非常小的角度内移动,这样当然产生不了任何的3D效果。
/%E9%A1%B9%E7%9B%AE%E6%91%84%E5%83%8F%E6%9C%BA%E4%BD%8D%E7%BD%AE%E4%B8%8E%E6%B8%B8%E6%88%8F%E7%94%BB%E9%9D%A2%E7%9A%84%E4%BD%8D%E7%BD%AE%E7%A4%BA%E6%84%8F.png)
或许我们可以尝试进行一些开发来让这个功能正常生效,最常见的办法即在摄像机与游戏画面的轴线上添加一个Object,使其处于游戏画面的平面中,然后将该物体设置为默认听者,这样就能正确地反映游戏内的3D关系。然而游戏内还存在镜头缩放的功能,在调整缩放级别后,现有的听者就无法正确地反应游戏内物体的相对位置信息了。当然我们可以继续添加一些功能来适配它,这或许是一个很专业的思路,但不是解决问题的思路,我们必须用一种更加简单直接的方法一劳永逸地解决这一问题。我们给出的答案是:
/%E5%B1%8F%E5%B9%95%E4%B8%AD%E7%BA%BF%E8%AE%A1%E7%AE%97%E6%96%B9%E6%B3%95%E7%A4%BA%E6%84%8F.png)
划出屏幕中线,计算物体距离屏幕中线的相对数值(在左侧为负,在右侧为正),将该数值映射至GameParameter上,然后直接通过RTPC来实现声音方位的变化。
/RTPC%E5%AE%9E%E7%8E%B0AUTO%20PAN%E5%8A%9F%E8%83%BD%E7%A4%BA%E6%84%8F.png)
非常简单直接,并且不管镜头怎么缩放,其位置信息始终是正确的。至于其他声音属性的变化(例如EQ等),同样可以通过RTPC来进行调整,但是作为一款装修休闲游戏,我们不需要这些信息。
游戏内还有一台很好玩的唱片机。平时可以用来播放其他关卡的BGM,但是为了强调其家具属性,在玩家拾起唱片机时,我们会让它的声音变成像是经过了Speaker处理后的效果。按照上面的逻辑,我们当然不可能真的做一个实时渲染的效果器来实现它。这样做或许很厉害,但我们没有理由为了一个功能去引入一位技术人员。于是,我们的实现方法是:
/%E5%94%B1%E7%89%87%E6%9C%BA%E7%B3%BB%E7%BB%9F%E7%A4%BA%E6%84%8F.png)
把音乐文件预先处理成Speaker效果,然后直接用Switch轨道来控制两个音轨的切换:放着的时候播放正常的音轨,玩家拿起来后就切换到处理后的音轨。最后还要给这些音乐也加上上面的3D方位RTPC,让它真正变成一个有空间信息的唱片机。
六、尾声
在完成《呓语小镇》的开发后,我们还收获到了一些意外之喜——这套自动化工作流很大程度上可以套用到我们后续承接的独立游戏音频开发工作中,甚至有些项目的适配性比《呓语小镇》还要更高。对于小型独立游戏团队来说,这样的工作流可以最大化地节约在音频模块上投入的人力成本。而对于大型游戏公司来说,其也并非一无是处——如果仅针对某一个重复性较高的模块应用该模式,则仅在该模块内,音效师可以在完全不了解中间件的情况下独立完成集成工作,极大降低了人员培训成本,也能解放更多的人力投入在音频资产本身的品质打磨上。
回顾整个自动化流程,我们会发现发挥主要作用的地方跟技术完全无关,在开始技术方面的工作之前,自动化流程实际上就已经通过设计的手段搭建完毕了,剩下的就只剩把具体的每一步都执行出来,而这正是AI最擅长的——不需要做设计,只需要根据一个非常具体的指令来编写程序。这也是Wwise带给独立游戏的最核心的优势之一,让设计师可以通过设计的手段绕过一些技术难点,极大地节约本会用于音频开发的资源,从而为其他模块的完善争取更多时间。
如果你对《呓语小镇》设计方面的更多内容感兴趣,不妨看看下面的视频:

评论