浅析游戏中的音频GameObject管理

Wwise 技巧和工具

一、前言

GameObject是使用Wwise进行音频设计的基础理念,从Wwise基础功能的使用(尤其是Profiling),到使用WwiseSDK开发都会涉及这一概念。笔者在日常工作中遇到过不少因为音频GameObject管理不善而引发的问题。故而尝试对音频GameObject的注册与管理方法进行一定的梳理。

首先我们来介绍一下关于本问题的相关背景和一些基础概念。

1、什么是GameObject

首先我们来统一一下概念。

为了避免大家和广义上游戏引擎中的GameObject以及Object-Based Mixing等概念相混淆,此处的定义以Wwise引擎中关于GameObject的定义为准:

image001

同时为了与Wwise的界面及文档统一,后文中也统一使用(Wwise中的)GameObject这一名称。

2、为什么要进行音频对象管理?

由上文可知,GameObjectWwise中的核心概念——Wwise对音频的管理都是基于管理GameObject来实现的。

可以说,在使用Wwise的情况下,游戏中任何声音的播放都离不开GameObject(无论声音是2D/3D)。

若音频GameObject管理不善,会导致各种问题——轻则Wwise内的功能异常或失效,重则在游戏引擎开发层面留下隐患。

3、以GameObject为单位管理音频是Wwise独有的么?有什么特别之处?

GameObject为单位(Object-based)管理音频并非Wwise独有,也存在其他音频引擎(或音频管理器实现)支持这一管理方式。

  • 不使用GameObject为单位管理音频的音频管理器实现,通常是以事件为单位(Event-based)来管理音频——这种情况下,音频设计师只能从Event维度进行控制。
  • 而Wwise引擎则提供了GameObject和Event两个维度供设计师使用(尽管这里Event也并非绝对的脱离GameObject),这使得声音设计的灵活性与表现力都得到了提升。

Wwise Event、GameObject与UE Component的关系梳理

为了方便理解GameObject的重要性及其工作原理,我们以一个最简单的蓝图接口实现为例,看一下GameObject是如何在代码层面被创建和引用的。

由于AudiokineticWwise的开发公司)官方维护有一套功能丰富的Unreal引擎集成,因此Unreal引擎集成当中对于WwiseSDK的用法具有一定的代表性,故而笔者在此处以PostEvent蓝图接口为例来论述。

以下截图为Unreal中PostEvent蓝图节点的C++代码:

Unreal中Postevent蓝图节点定义:

image002

该段代码对应该蓝图界面:

image003

该蓝图节点上AkEventActor两个输入点必须连入变量或具体的值,否则将会在编译时报错

代码逻辑简单讲就是:运行PostEvent节点时会给指定Actor对象的RootComponent层级附加AkComponent

image004

附加AkComponent的过程,首先遍历当前Actor的子级:

image005

如果已有AkComponent则直接启动,

如果没有,那就新建一个AkComponent

image006

然后AkComponent会通过类别转换和Wwise内的GameObject进行强绑定。

所以,每个Actor播放Wwise的声音时,AkComponent必须存在。

AkComponent的作用:

image007

AkComponent就是存在与UE中的Wwise专用的GameObject(的容器),其可以调用到以下函数:

image008image009

纯看代码可能不方便部分没有代码基础的同学理解相关概念。

接下来我们换一个更加通俗易懂的方法来描述下这个逻辑:游戏引擎使用音频中间件播放声音的逻辑是这样的:

image010

总结(第一节)

Wwise中的)GameObject的数据来源是游戏引擎中的AkComponent,且该来源不可或缺。(数据可能包括方位、转向、GameSync等等信息。)

可以说每个声音的播放都指向了一个目标对象——AkComponent

Wwiss会为目标对象注册一个与之匹配的(Wwise中的)GameObject来承载声音,其基础属性全部从AkComponent继承。

所有的以AkComponent为目标对象PostEvent都是以该(Wwise中的)GameObject为目标的。

如果使用一个感性的说法:

Wwise中的)GameObject就是某个人(AkComponent)在声音世界的分身,负责承载所有指向这个人的事件。

另外,(Wwise中的)GameObject除可依据AkComponent被注册,也可被解注册。

明白了这些以后我们可以做什么呢?

二、Object-based音频管理与Event-based音频管理之间的潜在矛盾

1、什么是Event-based音频管理

早期的游戏音频中间件或者小体量游戏的音频管理器实现基本都是以Event-based的理念来管理游戏中的音频的。而现如今,通常游戏中的每一个声音都是一个独立的对象。

偶尔会有一些接手音频开发的同学在缺乏考虑的情况下选择简单粗暴的方式来实现音频播放的管理与控制:

  • 为每次声音播放都注册GameObject,在声音播放完成后便销毁。
  • 创建少数负责声音播放的游戏对象,将所有将要播放的声音都指派至该对象进行播放。

我在本文中将他们统称为基于事件的(Event-based)音频管理。

之所以这样划分,是因为他们在实现时通常只考虑了声音的触发,并没有考虑到声音在触发后进一步进行控制的必要和复杂性。

尽管上述对象的创建和管理模式存在一定的合理性,但它们肯定是不能满足所有开发需求的。

先来看一下第一种方式(一个事件伴随一个随机对象):

稍有编程功底的同学们都知道,游戏中对象的创建和销毁是需要占用系统资源的,低频率事件可以使用该模式,如果是高频触发事件,那么将会在短时间内大量的创建和销毁对象,会导致不必要的系统消耗——这是Event-based模式在性能消耗方面可能存在的隐患。

当然某些场合可以采取创建对象池的思路来优化,但并不是所有声音的播放都适合指派到随机的对象上。

如果我们的游戏中存在多个对象同时触发相同声音的情况,那么Event-based模式将很难精细的管理这些声音,因为所有的Event都是相互独立的,音频引擎无法区分哪些Event属于游戏里的哪个对象。这导致在进行发声数上限等音频管理时就只能进行全局管理,也就是只能使用Wwise中的Global模式进行管理。

再来看一下第二种方式(大量事件指向同一个对象):

通常在游戏对丰富的声音设计提出需求的情况下,设计师本身也要将性能的控制考量进去。

而将所有声音分配至同一个游戏对象的做法,会导致以实际游戏内对象为单位的更细致的声音并发控制无法实现(与第一种方式一样会遇到这个问题)。

若此时还对此同一个音频对象的声音并发量设置了上限(Global),则可能导致声音被随机误伤,进而产生不合理的听感。

而若要解决声音被误伤的问题,就需要解除声音并发量上的限制,而这又会导致较大的性能消耗。

总结(第二节)

Event-based音频管理是将GameObject视为Event的附属物,为每个声音事件匹配一个GameObject的做法。

Object-based音频管理则是将Event视为Gameobject的附属物,先注册和游戏中对象对应的(Wwise中的)GameObject,然后Event作为子项继承Gameobject的相关信息的做法。

三、如果GameObject管理不善可能造成的问题

为了更方便大家理解,我们此处直接使用一些工作中可能会接触到的案例来进行分析:

假定我们当前正在开发的游戏中并没有加入Object-based的理念,依然基于Event-based的理念来触发和管理声音,那么Wwise将不会正确接收和游戏内对象对应的GameObject信息,而是盲目的为每个音频事件独立生成GameObject

那么我们在使用Wwise时可能会遇到以下问题:

1Wwise内所有和GameObject相关的功能全部失效或表现异常

  • 所有选择了Game Object模式的RandomSequence容器在被触发播放时不会刷新自己的列表。尤其值得注意的是,若每次播放都注册新的Game Object,被播放的Sequence容器会永远都只播放第一个子项。
    遇到类似情况,设计师若不通过Profiler仔细检查不同声音的目标对象指派情况,将会在Debug上花费大量的时间。

image011
  • 选择了Per Game Object的playback limit将失效,完全无法限制游戏内的实际声部数量,针对并发控制的声部管理功能将不得不在引擎层面重新被实现,然而这也要看程序员同学是否乐意。
    如我们在第二部分当中已经提到的,以游戏对象为单位的声音并发控制将无法实现。无论是一声一对象还是多声一对象,设计师都无法在创作工具层面实现性能上的控制。这相当于程序同学把已经分出去的工作又自己抢了回来。

image012


  • 插件参数的自动化(LFO)作用域的不同选项表现将会变得混乱。

image013

 具体会如何混乱,参见下图:

image014

  • 在一声一对象的情况下,Stop和Break等音频事件将无法针对直观存在的Game Object使用(这些行为都不得不交由程序管理),只能使用Stop all等针对全局性的功能。
    这与前面提到的播放限制问题一样,相当于程序同学把已经分出去的工作又自己抢了回来。
  • 无法使用旧版本Wwise中的Game Object Profiler,因为Game Object都是顺序生成的,你很难知道即将播放的声音的Game Object ID是啥,除非你需要监视的是一个持续的Loop声音。
    这给音频设计师测试与Debug带来了进一步的困难。

2、产生不必要的系统消耗

  • 如果有高频次触发的One-Shot声音,会导致声音对象高频次的反复创建和销毁,这个情况我们前文已经讲述了,这会产生不必要的系统消耗;
  • 因为每个声音都独立注册GameObject,那么每个声音也都会独立计算方位与转向、读取自己的GameSyncs,这会进一步造成无意义的系统消耗。

以玩家的脚步为例:

  • 如果玩家身上有一个常驻的Game Object,那么Wwise只会在玩家脚下的地面材质发生改变时接到SetSwitch API的调用,然后播放正确的音频内容;
  • 如果不这么做,那么玩家的脚每踩一下,都会检测一次地面材质、注册一个随机IDGame Object,然后为此Game Object调用SetSwitch API确保播放正确的内容,再进行音频内容的播放,在音频播放完毕后解注册掉Game Object,走每一步都会重复以上过程。

再以一辆车为例:

若车的引擎、胎噪、风噪彼此是独立事件触发的,它们都需要读取车辆的车速。

  • 如果车辆上有一个常驻的Game Object,那么该Game Object对应的三个事件的车速参数可通过同一个RTPC进行控制(调用SetRTPCValue一次);
  • 反之,若采取一声一对象的方式实现,则会生成三个Game Object,并且需要为每个Game Object传递车速RTPC(调用SetRTPCValue三次),哪怕这几个Game Object需要的RTPC数值是一样的。
    这便会导致计算车速这个RTPC的导致的内存/CPU消耗上升到合理做法的三倍。

虽然这个消耗可能依然不算大,但是这也是一个不健康的状态。

3、针对GameObject设置的插件反复实例化

这个前文已经提到过一次,因为每个声音都是一个独立的object,所以每有一个声音都会实例化一个新的插件,每个插件都有自己独立的运行参数,而不是从属于同一个GameObject的插件拥有相同的参数。

4、限制了虚实例技术的使用

因为不能独立注册和销毁GameObject,也没有办法通过让多个发出相同声音的发声体只实例化其中一个的方式来减少系统消耗,所有的发声体都将是真实实例化的。

总结(第三节)

如果大家在工作中遇到了类似以上所述的情况,可以尝试查询一下游戏内针对音频的GameObject的创建和注销机制是否正常。有可能有助于大家定位问题产生的原因。

四、良好的GameObject管理理念

1、游戏开发初期应当规划音频对象池,对于可能高频重复触发的声音事件采用对象池技术管理,甚至所有的音频对象都采用对象池技术进行管理。

2、游戏开发时除了规划声音的触发和停止机制以外,还要注意规划声音所属GameObject的创建和销毁(引用和解引用)时机。

3、游戏引擎的音频接口开发时应注意尽量保持和Wwise官方SDK的参数设置保持统一,必要的时候给Wwise开发独立的音频接口。

4、进行音频中间件的更换或升级时应注意新旧中间件SDK的兼容度。如果兼容度低,升级过程中可能需要开发新的音频接口和同时修改声音的挂接逻辑。

结语与致谢

以上就是截止目前我们对于音频GameObject管理的相关研究,目前的结论也可能存在一定的疏漏,我们会在此基础上不断完善。

最后鸣谢协助我一起完成该文章的伙伴——张正昱阳、温哲奇。

同时,郑重鸣谢Audiokinetic大中华区产品专家侯晨钟老师在文章的修订过程中提供了丰富的相关资料与宝贵意见,个人获益匪浅。

徐巍

资深音效设计师

网易互娱

徐巍

资深音效设计师

网易互娱

立志成为全品类音频制作人,曾参与制作过电影、电视、动画、球幕影院、沉浸式互动场馆等音频设计与制作,现在开始搞游戏了。

评论

留下回复

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

更多文章

借助 Wwise Motion Source 稳健设计振动效果

还记得你第一次体验到妙不可言的振动效果是什么时候吗?当时可能你刚买了 Nintendo 64 Rumble Pak,满心期待地准备玩《Golden Eye》–...

15.4.2019 - 作者:马克西米连·西马德·波里尔 (Maximilien Simard Poirier)

SoundSeed Grain 的开发历程

很长时间以来,由于粒子合成器在声音设计上的广泛应用,我们一直都想为 Wwise 开发一款通用的粒子合成器插件。这样就可以利用它生成各种各样的声音,来为游戏设计提供丰富灵活的支持。 我记得几年前曾在...

12.8.2019 - 作者:路易斯-扎维尔·布法尼 (Louis-Xavier Buffoni)

在Wwise中进行大批量音频管理的思路分享

在我的日常音频工作中,经常会遇到需要做大批量语音文件导入、配置的情况,由此产生的批量化处理需求大致可分为三种:...

11.5.2020 - 作者:葛鑫

在 Wwise 和 Unity 中构建对白 | 叙事音频

9.12.2021 - 作者:杰克•盖米林 (Jake Gamelin)

Wwise Audio Lab (WAL) 配套更新

Wwise Audio Lab (WAL) 是一个采用 Unreal Engine 4 开发的类似游戏的 3D 开源环境,其可通过 Wwise Launcher 进行下载。在 WAL 中,用户可对...

27.4.2022 - 作者:戴米安·卡斯特鲍尔(Damian Kastbauer)

关于对 Wwise Unreal Integration 的改进

作为在 Unreal 中管理 Wwise 素材的便捷解决方案,Wwise 2019.2.1 中引入的 Event-Based Packaging (EBP) 素材管理工作流程受到了 Wwise...

7.2.2023 - 作者:纪尧姆·雷诺 (GUILLAUME RENAUD)

更多文章

借助 Wwise Motion Source 稳健设计振动效果

还记得你第一次体验到妙不可言的振动效果是什么时候吗?当时可能你刚买了 Nintendo 64 Rumble Pak,满心期待地准备玩《Golden Eye》–...

SoundSeed Grain 的开发历程

很长时间以来,由于粒子合成器在声音设计上的广泛应用,我们一直都想为 Wwise 开发一款通用的粒子合成器插件。这样就可以利用它生成各种各样的声音,来为游戏设计提供丰富灵活的支持。 我记得几年前曾在...

在Wwise中进行大批量音频管理的思路分享

在我的日常音频工作中,经常会遇到需要做大批量语音文件导入、配置的情况,由此产生的批量化处理需求大致可分为三种:...