相关文章推荐
绅士的花卷  ·  Visual Studio ...·  1 年前    · 
有腹肌的肉夹馍  ·  PostgreSQL invalid ...·  2 年前    · 
俊秀的勺子  ·  sql server列名不明确-掘金·  2 年前    · 

在游戏音频开发的世界里,我们常常面临这样的挑战:你刚刚搭建好一个全新的 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 中时,执行你定义的回调函数。我的处理是在这个时候存储新的条目的信息,然后在录制结束之后,通过另外一个函数将条目信息列表生计算出所需的可供播放的数据。

需要注意的是

  • Wwise 条目时间单位是毫秒,且存在同一时间触发多个 event 的情况。因此,不管是在存储还是在播放都应该针对这个做出相应的应对操作。
  • 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 命令进行安装:

    安装完成后,就可以在 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)。

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

  • 基于播放光标位置捕捉 item 的 event 触发器
  • 连接 Waapi 并调用 API 的通道
  • 播放视频的同时低延迟 post event
  • 操作者自由开启或关闭 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:

    使用 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 中提供每帧刷新函数,​运行流畅没烦恼。
  • 后续功能展望

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

  • 轨道包络控制 rtpc
  • Item 控制 switch、state 切换
  • 为轨道分配 GO,根据 Unity 回放器记录数据,实时更新 GO 坐标信息
  • 结合 ReaWwise 原生功能实现 Reaper item、Wwise audio clip 无感化自由替换,实现在 Reaper 中还原 Wwise 中听感并直接挂插件调整素材,一键导回 Wwise 工程
  • 感谢幕后@张成功的鼎力支持,Unity 端信息抓取链是由他开发完成,在开发过程中帮助我们解决了超多难题。文章作者本应有他,然而他只想做一个隐藏在黑暗中的扫地僧~

    利用 Wwise 将两个 Audio Device 整合到 UE 游戏中

    大家好,我叫埃德·卡辛斯基,是一名来自俄罗斯圣彼得堡的声音设计师和音乐家,目前在做一个非常有趣且重视声音表现的项目:这是一个名为《牧师与鬼驱人(Priest vs....

    26.8.2020 - 作者:埃德·卡辛斯基 (Ed Kashinsky)

    游戏音频存档 | 第 2 部分:案例分析

    今天我们要讲的是之前一个研究项目当中的偶然发现。可惜最终未能得偿所愿,过程听起来也有些曲折。别着急,听我慢慢说。总的来说,在进行历史研究的时候,这种意外发现并不少见。《Conker's Bad Fur...

    16.11.2021 - 作者:范妮•雷比拉德 (Fanny Rebillard)

    如何使用 Wwise 和 Unity 创建可对音频作出反应的对象

    在此,我想向大家展示如何使用 RTPC 在 Unity 中移动游戏对象,并创建由音频驱动/可对音频作出反应的对象。本文要求读者具备 Wwise-101...

    28.5.2024 - 作者:Tomokazu Hiroki

    利用 Wwise 为游戏制作母带 | 第 2 部分:Mastering Suite 预设及其用法

    在本系列博文的第 1 部分,我们介绍了如何为游戏制作母带。在本部分,我们将探讨如何以及何时使用 Mastering Suite...

    4.6.2024 - 作者:Loïc Couthier & Danjeli Schembri

    《遍体鳞伤(Scars Above)》中的音频优化实践

    在本文中,我会试着阐释团队在对《Scars...

    14.10.2024 - 作者:Milan Antić

    为倾斜的 2D 视图构建定制的 Wwise 听者位置投射系统

    这是徐若昊撰写的三篇技术系列博文的第二篇。该系列博文主要分享《逆向坍塌:面包房行动》背后的声音设计。各位可点此阅读第一篇博文。其中深入探讨了如何利用 Wwise 驱动游戏中的过场动画。第 3...

    31.7.2025 - 作者:徐若昊(Jater Xu)