menu
 

构建无缝游戏音频测试工作流:Unity-Reaper-Wwise 音频回放工具链

游戏音频

前言

在游戏音频开发的世界里,我们常常面临这样的挑战:你刚刚搭建好一个全新的 Wwise 项目,准备进行 boss 战的实机测试,却发现现有的游戏管理(GM)命令并不如预期般便捷。

你的账号等级太低,无法直接进入特定关卡的 boss 战?没关系,从头开始闯关。

但当你以开挂的方式快速击败 boss,却发现 boss 的技能还没完全展示?没关系,找游戏策划调整数值。

最后,你发现某个大招难以复现,因为它的操作门槛太高……(让游戏程序更新 GM 命令需要排期,而你等不及……)

最终,你只能依靠短暂的战斗记忆来调整项目中的各种问题。

还有更多令人头疼的情况:进行大型测试时,服务器只允许一次测试机会; 测试多人战斗时,人手不足,只能测试一两次; 等等……

可不可以像电影一样,把游戏流程录制成 Wwise 声音胶片存起来,以后可以无限次回放呢?

当然可以,我们尝试开发了 URW(unity-Reaper-Wwise)回放工具链,只需将一次流程录制下来,就可以生成一个 Reaper 工程,在 Reaper 中触发 Wwise event,满足之后无限次的联调与测试。

生成的 Reaper 工程就是独属于你的 Wwise 声音胶片,在 Reaper 这个放录机上,你可以自由回看自己的胶片,这样,Wwise 就成为了 Reaper 的混音台,允许声音设计师反复打磨声音表现。

此外,将游戏流程录制成声音胶片(Reaper工程)还意味着,设计师可以在工程中随意调整播放进度,定位到游戏流程的某个特定时间点,针对某一帧或某一小段游戏流程反复打磨。

​图 1 - 工具链流程图

视频 1 - 工具链演示视频 Unity-Reaper-Wwise

第一步:Unity 录制音频信息

既然是回放 Wwise 的声音,那么这部分需要回放的声音数据。数据形式是什么?声音数据该怎么抓取?

关于数据形式

因为是 1.0 版的工具流,所以当前的数据形式还比较简单。一句话来说,只是知道在什么时间?播放了哪些声音事件即可了。如下图。


图 2 - 数据形式

关于数据抓取

前期思考过两种抓取方式:

1、Unity runtime 阶段嵌入抓取逻辑。

这种方式我们构想的理想实现方法是:项目中所有的播放声音行为有一个统一的入口。然后在这个入口处嵌入抓取逻辑,来一个声音,便抓取一个声音,存储一个声音。但考虑到不同的项目有不同的逻辑,或许就有项目不会有这样的统一的播放入口。我们给这套工作流的首要要求就是通用性,即做到任何项目都可拿来即用。因此如果后续存在在不同的项目去改造逻辑适配这套工作流的情况,这是不可接受的。另外,我们要求可以在Editor模式下也可使用。最后,在实现层面还有一个尚未验证的担忧,考虑到游戏跑起来之后的时间稳定性,或许会导致本该是同一时间触发的声音,最后数据记录下来的时间数据有所差异。所以,综上我们采用了以下第二种抓取方式。

2、Waapi

Waapi 中有一个订阅接口:ak.Wwise.core.profiler.captureLog.itemAdded

这个订阅函数会在新的日志条目添加到 Wwise 中时,执行你定义的回调函数。我的处理是在这个时候存储新的条目的信息,然后在录制结束之后,通过另外一个函数将条目信息列表生计算出所需的可供播放的数据。

需要注意的是

  1. Wwise 条目时间单位是毫秒,且存在同一时间触发多个 event 的情况。因此,不管是在存储还是在播放都应该针对这个做出相应的应对操作。
  2. Wwise 条目时间是声音引擎初始化开始所经过的时间,因此有可能录制的第一个数据触发时间量级很大。(或许你在声音引擎初始化了 1 小时之后才开始录制,那么第一个声音数据的时间就要 >=60*60*1000)。所以,不管是为了录制完成之后及时回放,还是为了把录制的数据能在后续生成的 Reaper 工程中能顶头播放,都应该把所有的数据时间等比例前移,我选择的做法是将所有的时间统一从 0 秒或 0.5 秒处开始。

其他便是播放的时间控制逻辑、用户界面的逻辑以及保存加载逻辑编写了,这里不作过多展开。

第二步:Json to Rpp

在生成 rpp(即 Reaper 工程)这一环,我选择了用 Node.js 来编写脚本。因为对于技术音频来说,我认为基于 Node.js 的 JavaScript 语言是一种相对来说比较轻量级的选择,可以快速地达到“写工具”的目的,从而将更多的时间用在“设计”上。

首先,Node.js 有 npm 包管理工具,可以将脚本所需的依赖包粘合在一起,使脚本开发过程变得相对流畅。比如 FFmpeg 是一个非常强大的音视频处理工具,支持多种音视频格式,提供了丰富的功能和参数选项。而在 Node.js 中使用它也相对便携,只需使用 npm 命令进行安装:

   Plain Text
   npm install fluent-ffmpeg

安装完成后,就可以在 Node.js 代码中引入 fluent-ffmpeg 模块,然后使用其提供的 API 进行音视频处理了。在我之前给音频部写的打 metaData 标签的工具中,也是调用了 FFmpeg 中相关的 API:

图 3 - npm 中的 FFmpeg Metadata API 示例

相比于其他编程语言,Node.js 在处理 IO 密集型任务上表现更为优异,这意味着 Node.js 脚本能够更快地读取和写入大量的音频数据,而不会因阻塞而导致程序运行缓慢。另外,Node.js 还提供了异步编程模型,可以使脚本在进行 IO 操作时不会阻塞主线程,从而保持应用的高响应性。

更加令人惊喜的是:JavaScript 有相关的 Waapi 接口,你可以在网页中、桌面应用程序中和 Wwise 做相关的交互。这也为技术音频在设计工作流的过程中提供了很多的思路。通过 Waapi 接口,可以方便地使用 JavaScript 与 Wwise 进行通信和交互,实现对音频资源的实时控制、管理和自动化处理。例如,可以通过 Waapi 接口实现动态更改音频事件、创建和管理游戏中的声音对象等操作,从而为游戏音频设计提供更多的可能性。

图 4 - 官方用 JavaScript 写的“Hello Wwise” Waapi 示例

在当前这个回放器工具中,我就是通过 Waapi 来获取实践中音频样本的最大长度,从而将其转换为 rpp 中 item 的长度的。我使用的 uri 是 ak.Wwise.core.object.get,options 为'maxDurationSource',以 WAQL 的形式向 Wwise 发送 http 请求,从而获取信息。

​图 5 - 使用 Waapi 的查询事件信息函数

至此,结合 Unity 中录制的 Json、mp4 和从 Wwise 获得的信息,我已经能完完全全地生成一个 rpp 的文本文件作为这个工作流中的 Reaper 工程了。

图 6 - rpp 文本信息对应

第三步:Reaper to Wwise (ReaWwise Caster)

图 7 - ReaWwise_Caster UI 界面

概述

在 Reaper 端实现重放的初衷,是为音频设计师提供一个能够在 DAW 中配合视频调试 Wwise 工程的环境;更加贴合 DAW 使用者的操作习惯,播放方式能够与制作视频贴片时的播放方式相同。

我把脚本设计成小 UI 的形式,相当于是个小遥控器——开关开启,遥控器丢在一边就不用管了(当然报错了还是要管一下 0 0)。

为了实现这个设计目标,工具需要实现这些功能:

  1. 基于播放光标位置捕捉 item 的 event 触发器
  2. 连接 Waapi 并调用 API 的通道
  3. 播放视频的同时低延迟 post event
  4. 操作者自由开启或关闭 capture 功能, 无感化 Wwise 通信

前置扩展

ReaWwise 是 Wwise 中的一个插件,它允许用户将 Reaper 中的音频和音乐资源集成到 Wwise 中,以便在游戏中使用。因此,ReaWwise 插件提供了一些 API,用于在 Reaper 和 Wwise 之间进行交互,开发人员可以将 Reaper 中的音频资源快速有效地转换为 Wwise 项目,并在游戏中使用它们。

图 8 - 前置扩展 1

图 9 - 前置扩展 2

Reaper 端 Waapi 调用的实现

今年一月份 AK 官方扩展了 ReaWwise 的功能,提供了在 ReaScript 调用 Waapi 的 API:

图 10 - 官方使用 ReaScript 编写的示例脚本

相关 API:

   Lua
   reaper.AK_Waapi_Call()
   reaper.AK_Waapi_Connect()
   reaper.AK_AkJson_Map()

使用 ReaScript 相关API,可收集 Reaper 中 item、track 所包含的相关信息,最终通过 reaper.AK_AkJson_Map_Set() 转化为调用 ak.soundengine.postEvent 所需要的格式。

基于播放光标位置捕捉 item 的 event 触发器

由于 ReaScript API 中没有提供捕捉 item 的 trigger 功能,所以只能自己写啦。基本思路是收集各轨道 item 起始位置数据,实时刷新播放光标位置坐标,当判定与起始位置重合时,post event;

然而在实际码代码时遇到两个问题:刷新函数时间间隔(用 while do 秒崩)、判定点精确度。

在献祭头发后,问题解决:

  • ReaScript 中提供每帧刷新函数,​运行流畅没烦恼。

    Lua
    reaper.defer()




  • 为了保证声音播放的准确性,将起始位置精确到1ms,以兼顾延迟和判定的准确度。

 

后续功能展望

打通 Reaper-Waapi 通道意味着,Reaper 中更多的参数可转化为 Wwise 相关参数,实现 Reaper 控制 Wwise 播放行为的目标。我们希望最终可以实现的是 Wwise 可以作为 Reaper 的拓展辅助调音台。功能展望:

  1. 轨道包络控制 rtpc
  2. Item 控制 switch、state 切换
  3. 为轨道分配 GO,根据 Unity 回放器记录数据,实时更新 GO 坐标信息
  4. 结合 ReaWwise 原生功能实现 Reaper item、Wwise audio clip 无感化自由替换,实现在 Reaper 中还原 Wwise 中听感并直接挂插件调整素材,一键导回 Wwise 工程

鸣谢

感谢幕后@张成功的鼎力支持,Unity 端信息抓取链是由他开发完成,在开发过程中帮助我们解决了超多难题。文章作者本应有他,然而他只想做一个隐藏在黑暗中的扫地僧~

孟庆杰 & 张宜喆

孟庆杰 & 张宜喆

孟庆杰

来自巨人网络,音频设计师,与 GPT 协同编码的 rapper。

Bilibili

X (Twitter)

LinkedIn

张宜喆

来自米哈游,一个捣鼓reaper脚本用来偷懒的音频设计师,酷爱搓碟拼胶的究极游戏宅。

评论

留下回复

您的电子邮件地址将不会被公布。

更多文章

游戏音频岗位技能 – 如何谋得游戏音频设计师职位

游戏音频设计技能:对 100 条游戏音频职位招聘信息的分析

9.3.2021 - 作者:布莱恩·施密特 (Brian Schmidt)

游戏音乐不应仅仅局限于音乐本身 – 第 2 部分

什么是游戏音乐?什么是互动音乐?这些问题的答案并不像看起来那么简单。对此,奥利维尔·德里维耶 (Olivier Derivière)...

20.4.2021 - 作者:奥利维尔·德里维耶 (OLIVIER DERIVIÈRE)

《Moss: Book II》:借助音频增强 VR 体验的沉浸感

21.5.2024 - 作者:Polyarc 音频团队

Speechless | 利用 Wwise 集成 VO 素材管理

大家好,我叫拉乌尔 (Raoul),是一名拥有九年游戏行业工作经验的旁白 (VO) 设计师,主要从事 AAA...

25.6.2024 - 作者:Speechless

Sound Haven Highway | Step Up Your Sound Game Jam

本文是在第二届 Step Up Your Sound Game Jam 之后撰写的。在这次一年一度的 Android 手游 Game Jam 上,参赛团队要结合 Wwise 使用 Dolby...

24.7.2024 - 作者:Petricore

在 Wwise 中构建环境声系统

简介 大家好!我叫刘易斯,是 Soundcuts 的声音设计师,同时也是 Airwiggles...

10.12.2024 - 作者:刘易斯•汤普森

更多文章

游戏音频岗位技能 – 如何谋得游戏音频设计师职位

游戏音频设计技能:对 100 条游戏音频职位招聘信息的分析

游戏音乐不应仅仅局限于音乐本身 – 第 2 部分

什么是游戏音乐?什么是互动音乐?这些问题的答案并不像看起来那么简单。对此,奥利维尔·德里维耶 (Olivier Derivière)...

《Moss: Book II》:借助音频增强 VR 体验的沉浸感