《Hardspace: Shipbreaker》是一款第一人称零重力飞船拆解游戏,近期已在 PlayStation 5、Xbox Series S/X 和 PC 平台发行。这是一部关于工作、债务和资本主义的讽刺作品。游戏中构筑了身临其境的太空环境。在这里,玩家将亲身体验为一家未来超级跨国企业工作的日常。玩家会漂浮在地球轨道上,负责回收大小不一的飞船,同时还要小心避免被失控的燃料系统或核反应堆炸飞。这是一项辛苦且危险的工作。不过,当中也充满了乐趣。
《Hardspace: Shipbreaker》中的打捞舱视角
当我在 2017 年刚参与到项目中的时候,它还是个规模较小、相对简单的游戏。不过,核心框架已然成型:
- 模拟优先。模拟虚构的未来职业;为玩家打造真切的飞船打捞作业环境。
- 物理玩法。通过真切的物理模拟生成大部分玩法,以此鼓励玩家进行有趣的互动和实验。
- 切割玩法。借助精细、逼真的切割机制让游戏玩法更有层次。玩家可使用切割工具将物体切成碎片。
- 蓝领氛围。浓重的蓝领美学氛围;设备都是二手的,时不时发生故障。动不动就卡壳,哪哪都有问题。一切都给人以老旧不堪的工业感。
团队和我希望游戏音频能够真正融合这些要素,以尽可能充实而有意义的方式合理地穿插其中。同时,这些音频还要与模拟玩法、物理机制和美学设计紧密结合,真实地做出回应,跟玩家操作完美同步,并与游戏场景协调一致。因为游戏以模拟为核心框架,所以我们希望音频也能遵循相应的规则。比如,如果玩家处在太空真空环境中,就不应该有任何通过空气传播的声音。只有玩家处在加压环境下的飞船内时才会有通过空气传播的声音。
这种“遵循模拟规则”的诉求很快就带来了一些有趣的挑战:
- 玩家大部分时间都待在太空真空环境中,我们如何提供引人入胜的听觉体验?
- 如果玩家可对区域加压减压,我们如何突显这种过渡效果,而非直接从嘈杂切换到无声?
- 游戏中的任何物体都可被切割成碎片,我们如何让物理互动听起来自然逼真?
- 如何让设备给人一种老旧不堪的感觉,同时又能给玩家带来满足感和愉悦感?
经过反复讨论和多次原型设计,我们最终构建了一套系统来解决上述问题,打造了细节丰富、独具特色、引人入胜并能完美支持核心模拟玩法的声景。在本文中,我将介绍其中的几个系统并分享我们如何利用 Wwise 的功能予以实现。
完了…
再合成
首先,说说我们的再合成系统。在游戏中,我们希望玩家能听到环境中危险源发出的声音,因为太靠近火花迸溅的电缆或熊熊燃烧的燃料管路显然…没啥好处。这让我们立即面临上述第一个问题:如果我们模拟的是声音在真空中的行为,玩家怎么才能通过听来感知这些危险呢?简单来说,听不到。
为此,我们跟游戏设计师一起开发了名为 Resynthesizer 的虚构装备。该装备可与玩家的打捞工服相连。它可以检测环境中的危险源,并播放与之对应的模拟音效。该装备作为可选升级项提供给玩家,不过要用游戏币购买。装备本身还可以升级,进一步扩大检测范围。
再合成 – 实现
为了实现前面说的“通过升级扩大检测范围”,我们选择了在 Wwise 中的 "FX" Actor-Mixer 上启用和旁通 Wwise Gain 效果器。在将 RTPC 与 Distance 绑定之后,可使用这些 Wwise Gain 效果器在玩家处于一定距离之外时关掉声音。藉此,为危险源发出的声音设置标准衰减,确保其在加压空间中能够正常衰减。不过,在非加压空间中只有处于 Resynthesizer 装备检测范围之内时才能听到声音。
"FX" Actor-Mixer 上应用的 Wwise Gain 效果器
我们使用了 State 来旁通和启用效果器。同时,灵活运用了 Wwise 的音乐系统来触发 State 变化。在音乐系统中,可查询变量(如 State 或 Switch),并根据该变量的值来触发 Event。比如,您可以查询某一 State Group 的值,并以此设置另一 State Group 的值。
玩家当前的装备升级档次被作为 RTPC 而非 State 发给 Wwise。因为该 RTPC 是全局设置的,而危险物体使用的是本地 RTPC 值,所以没办法直接使用该 RTPC 来启用 Wwise Gain 效果器。我们需要接收全局 RTPC 值,并将其转换为所有危险物体都可引用的 State 值,以此来旁通/启用相应的 Wwise Gain 效果器。幸运的是,Wwise 的音乐系统正好可以做到这一点。
首先,我们创建了一个 Switch 用以通过全局 RTPC 来选择目标值:
然后,在音乐系统中设置了一个容器来每秒循环播放一次,以此查询 "Upgrade_Resynthesizer" Switch 的值。据此,无声的 Music Segment 通过 Music Event Cue 设置 State 的值。
然后,通过该 State 启用和旁通危险音效上应用的 Wwise Gain 效果器。
当然,这种实现方式相对比较复杂,所以我一般不推荐这么做!这里只是为了展示在急于实现某项功能而又没有程序员从旁支持的情况下能用 Wwise 做什么。
游戏中的再合成功能视图
再合成 – 声音
我们在实现再合成功能的时候还面临一个难题,就是让它听起来逼真又不跟蓝领美学设计冲突。虽然我们希望玩家能在真空环境中听到危险物体发出的声音,但再合成的音效不能太完美或听着太像通过空气传播的声音。我们已经接受 Resynthesizer 作为虚构装备的设定,并确定玩家的设备需要给人一种老旧不堪的感觉。要让 Resynthesizer 听起来真实可信,必须加入杂音、嘶嘶声和卡顿音效。
为了实现这一点,我们最初采用了一种基于离线流程的解决方案,核心工具是 Glitchmachines 开发的 Fracture 插件。我们将该插件托管在了 Plogue Bidule 内,并利用其生成伪随机采样保持值,然后将这些值传给 Fracture 来操控其部分参数。
在通过此工具链对危险音效做离线处理后,我们对最终生成的杂音效果感到非常满意。
然而,这种方法仅适用于同一时间只播放一个声音。在同时播放两个或更多声音时,其采样保持变化的幅度不同步,导致最终产生的效果混乱不堪;随着声源的增加,情况只会更糟糕。显然,离线处理无法达到预期效果。如果要将这种 DSP 用于再合成声音,必须使用实时效果器,将其一并应用于所有再合成声音的子混音。
为此,我们开始尝试开发自研的 Wwise 插件,使用 Pure Data 生成 Pd 补丁并通过 Heavy 编译器将其转成插件。在 Pd 中,我们构建了跟 Fracture 中的配置相近的流程;其核心是个复合效果器,包含采样降质器、步进镶边器、微卡顿器和蜂鸣生成器。
Resynthesizer Pd 补丁 – 顶层
为了便于在 Wwise 中进行配置,我们将所需参数作为 @hv_param 节点暴露了出来。在此之后,可将 RTPC 指派给这些参数来在玩家对游戏中的 Resynthesizer 进行升级后减弱杂音和降质效果。
随后会在子补丁中实施处理。各声道的处理方式完全相同。上图中 adc 和 dac 节点之间的就是子补丁。
声道处理子补丁
在通过 Heavy 进行转换后,便可在接收所有 Resynthesizer 声音的混音器总线上将生成的插件实例化。这时所有采样保持变化的幅度都会同步。我们在整个 Resynthesizer 子混音中只运行了一个 DSP。所以,CPU 用量非常低。我们要的就是这个效果。
以下是在 Wwise 中运行插件来处理 flame_plume 循环的情形:
以下是在游戏中的实际运行效果:
Heavy 的问题
在此阶段,我要说一下我们在使用 Heavy 生成的插件时遇到的几个问题。
首先,Heavy 库原本最多只支持 2 个声道。我们希望在环绕声总线上实施声像摆位之后将效果器实例化。为此,需要修改编译器来支持 6 个声道。我们在 hvcc 的 c2wwise.py 文件中轻松完成了这一操作。不过,为了避免出现严重的稳定性问题(在使用 6 声道以上的输出设备时发生崩溃),只能在 Wwise 的 Channel Configuration 下拉菜单中将效果器所在总线限制为 5.1。
其次,虽然为设计工具和 PC 游戏生成插件 DLL 相对简单,但为 PlayStation 和 Xbox 生成插件时过程就没那么轻松了。我比较幸运,可以向 Blackbird 团队的程序员寻求帮助。最终,他们设法在 Visual Studio 中配置目标,并生成了主机平台所需的文件。注意,如果各位没有程序员从旁提供支持,要费点功夫才能在主机上正常运行效果器。
Touch Transfer 声音
接下来,说说我们的 Touch Transfer 系统。该系统确保玩家可在与声源接触时听到声音。这样即便是在非加压环境下也可以听到声音。我们在做相应设计的时候受到了电影《地心引力》的启发。这部电影在开发前好几年就上映了,不过还是对游戏产生了很大的影响。
在游戏开发过程中,我们经常会想“要是能怎么怎么样就好了”。Touch Transfer 系统就源自于这样的思想碰撞。在加压的飞船内部,2D 环境声与 3D 环境发声体营造出丰富的声景;在区域减压时,声音逐渐消退。这种效果本身还不错。但是,我们忍不住进一步设想:“要是能在触摸发出声音的物体时借助工服听到这些声音就好了?”
这一构想最终演变为三个相辅相成的设计目标:
1. 把飞船内的物体打造得更真实,而不仅仅是空洞的多边形箱体。
2. 激励玩家通过触摸来探索飞船,营造一种极具触感的沉浸体验。
3. 有效传达环境中存在的危险;比如,汩汩的燃料管路和嗡嗡的电力线缆。
我们发现以下声音比较适合通过 Touch Transfer 系统进行处理:
- 2D 环境声。在触摸船体结构时,会听到 2D 环境声。
- 3D 环境发声体。在触摸电脑或燃料管路等物体时,会听到其发出的声音。
- 物理碰撞。在触摸碰撞物体或被碰撞表面时,会听到物理冲击产生的各种声音。
除此之外,还有几种专门为 Touch Transfer 设置的声音。不过,绝大多数都可以归到上述类别。
在游戏中通过触摸来听物体发出的声音
Touch Transfer 声音设置
在 Wwise 中,我们创建了三个 RTPC,分别代表三种会触发 Touch Transfer 声音的玩家操作:手触摸物体、身体触碰物体(倚靠物体)、物理碰撞中接触物体。它们在 Wwise 中分别标记为 Grab、Prox 和 Physics。
三个 "Touch_Transfer" RTPC
对于 2D 环境声,这些 RTPC 应用在包含所有 2D 环境声的父级 Actor-Mixer 节点上。在触碰到舱壁或船体时,我们会将相应的 RTPC 提升到 100,并将声音发送到 Touch Transfer 总线。
2D 环境声节点及与之对应的 "Touch Transfer" RTPC
对于 3D 发声体,情况稍微有点复杂,因为通过空气传播的声音是 3D 的且带有衰减。但在发送到 Touch Transfer 总线时,我们希望把它变成 2D 的且无衰减。对于这些声音,我们会播放两个实例:一个用于加压区域的 3D 衰减版本,一个在提升 "Touch Transfer" RTPC 后才会听到的 2D 版本。
为 Touch Transfer 配置的 3D 发声体
物理声音的处理方式与 2D 环境声相同(在包含所有物理声音的父节点上应用 RTPC)。
通过将 Touch Transfer 声音分为 Grab、Prox 和 Physics 类别,我们可以构建混音器结构来为声音设定优先级。比如,双手传递的声音会压低玩家靠在物体上传递的声音。这是因为使用双手是积极主动的行为,身体靠在物体上则是相对被动的行为。另外,我们还对一次性的 Touch Transfer 物理声音做了专门的设置以压低其他 Touch Transfer 声音。如此一来,无论 Touch Transfer 声音发生何种变化,玩家都能清楚地感知到。
我们对这套系统的运行效果很满意,尤其是在将其用作游戏反馈机制时。游戏中有很多危险物体,玩家不小心可能会切到,引发致命的火球或电弧。我们可以通过切断燃料或电源来确保安全。为此,可将音频与这些物体绑定。在切断能源时,发声体的音频会停止。这样玩家就可通过触碰物体来判断其是否安全。
下面的视频展示了将 Touch Transfer 声音用于游戏反馈的情形:
- 飞船两侧各有一套燃料管路系统。
- 管路充满燃料,切割时要当心。管路内燃料流动的声音可被听到。
- 燃料冲刷阀位于飞船另一端,在玩家要切割的管路段对面。
- 在排出空气并对船舱进行减压之后,无法再听到管路中燃料流动的声音。
- 不过,玩家可触摸管路来听辨哪条管路内含有燃料,哪条管路已被排空。
- 在冲刷两套燃料系统后,玩家触摸两条管路,确认其安全便可进行切割。
举例展示如何利用 Touch Transfer 声音确认燃料系统是否安全
Touch Transfer – 幕后
不过,用于追踪哪些物体在被触摸的系统比较复杂。下面是我们的技术总监理查德•哈里森 (Richard Harrison) 对物体追踪代码的简要描述:
在我们的飞船中,物体的分类包含三个层次:首先是 Part:飞船的单个组成部分。其次是 Group:一小组 Part(比如安装板和天线为 Part,两者组成的卫星天线则为 Group)。Part 和 Group 都可以是 3D SFX 发声体。最后是 Hierarchy:涵盖所有物理上相连的 Part 和 Group;如果从船体上切下一部分,所有从源头切除但仍相连的 Part 和 Group 都将被指派给新的 Hierarchy。
Touch 操作可通过 Grab(左/右手)或 Prox 来实现。这两种操作会被单独追踪,因为其分别影响不同的 RTPC 值。Grab 相对直观,Prox 则通过在玩家周围一定半径范围内做球形重叠检查实现。若有物体落入球体范围内,则视为在距离上存在接触。
Touch Transfer 系统本质上是个经过强化的引用计数器。每次开始触摸 Part、Group 或 Hierarchy,都会将特定物品的计数加 1;每次停止触摸,都会将计数减 1。只要有 Part、Group 或 Hierarchy 的触摸次数大于零,就可以确定存在接触。对于计数值增加到零以上的 Part 和 Group,我们要增大对应物品上应用的 "Grab" 或 "Prox" RTPC。如果该 Part 或 Group 在发出 3D 音效,RTPC 便会以某种方式使其可被听到。在触摸次数恢复为 0 的时候,对应 RTPC 也会被重置为零。
Hierarchy 与 Part 和 Group 的追踪方式相同,但用法不一样;只有在 Hierarchy 包含飞船的反应堆时才会设置 "Hierarchy" RTPC(反应堆被视为 2D 飞船环境声的声源)。否则,通过追踪告知碰撞系统是否与受到冲击的 Hierarchy 存在接触,以此来判断要不要触发相应的物理音效。
最终的难题源于物体的拆解和破坏。引用计数器要明白在 Part、Group 或 Hierarchy 被拆解时需解除与原有物品的接触并与新生成物品建立接触。当中的实现细节高度依赖于《Hardspace: Shipbreaker》的切割逻辑。不过,这已经超出本文的讨论范围。
物理系统的难题
最后,简单介绍一下《Hardspace: Shipbreaker》的物理音频系统。
在之前的项目中,物理系统都是基于物体的“物理音频标签”构建的。该标签决定在物体与表面发生碰撞时要播放哪种声音。我们可以通过查询表来确定在两个带标签的物体发生撞击时要播放什么声音;另外,在原有物体破碎的时候还可能会生成带单独物理标签的碎片物体。在发生碰撞时,会以 RTPC 的形式传递碰撞力度,同时由物理标签和力度值共同决定要播放哪种声音。不过在《Hardspace: Shipbreaker》中,我们遇到了一个传统方法无法解决的独特难题。
在《Hardspace: Shipbreaker》中,玩家可以拾取大型物体(如电脑终端),并像标准的刚体物理对象一样随意抛掷。不过,玩家有时会用切割工具将电脑一分为二。然后,我们就会有两半的电脑终端可拿来抛掷。到目前为止,还不算太难:只需使用 RTPC 来表示物体的重量,然后在 Wwise 中通过该 RTPC 调整电脑终端声音的大小。不过,玩家在用切割工具时可能会从电脑终端上切下一层薄薄的金属板。这时候听起来不能跟电脑终端一样,而要像金属板一样晃动并发出声音。换句话说,我们要在某个时刻切换电脑终端发出的物理声音。
飞船内的电脑终端
这个问题困扰了我们好一阵子。怎么让电脑终端知道要在被切得很薄时转换成金属板呢?事实上,我们最终采用的解决方案非常简单。只需要为物体设置两个物理标签并配置一个重量值,在低于该重量值时从一个标签切换到另一个就行了。
我直接把物理音频设计规范里的内容复制粘贴在了下面。各位不妨看一下。
像电脑终端这样的物体会添加 AudioMaterialData 组件。该组件包含三个字段:
- Tag:该物体的物理音频标识符。电脑终端的值可能为 object_large_metal_electronic_heavy。
- Family:该物体所属的通用材质分组。电脑终端的值可能为 metal_light。
- Threshold:该数值表示相当于原有物体的重量百分比。在低于该数值时,物体将使用 Family 值而非 Tag 值。
只要物体在 Threshold 中指定的重量百分比之上,就会选用 Tag 值而弃用 Family 值。
在发生碰撞时,物体重量的绝对值将作为 RTPC 传给 Wwise。在此之后,声音设计师可利用该值来调节对声音大小的感知。此 RTPC 不管物体是在 Threshold 值之上还是之下都会更新并传递。
Family 和 Tag 字段是选择性的:
- 如果物体碰撞时未检测到 Tag,系统应使用 Family 值,无论 Threshold 值为何。
- 如果 Family 字段未设定值,物体将使用 Tag 字段的值,直到该值低于 Threshold 值。在此之后,碰撞时不再发出声音(某些情况下可能需要如此)。
不同的 Tag 和 Family 值将被指派给不同的 Wwise Event;物理音频系统会根据使用的是 Tag 还是 Family 来触发相应的 Event。
Unity 中的物理音频设置
在想出这个解决方案之后,后面的事情就水到渠成了。在 Wwise 中,我们像往常一样设置物理声音。在 Unity 中,物体会结合 Tag/Family 值及相较于 Threshold 值的重量决定何时选用哪个声音。
下面展示了系统在游戏中的实际运行效果。
希望这篇文章能让大家对我们的部分系统有一定的了解。《Hardspace: Shipbreaker》游戏采用了多系统协同架构。除了这里说的,它还包含很多其他的系统。只不过文章篇幅有限,没办法全都介绍一遍。以后有时间的话再展开细说吧。感谢各位抽空阅读我写的博文!
评论