目录

Wwise SDK 2019.2.0
集成详情——事件

简介

Events(事件)由 Wwise 用户创建,方法是在 Wwise 对象中指定要执行的动作。例如,事件可能包含在声音Bird1 上的播放动作和声音Bird2 上的停止动作。第二个事件可包含声音 CarEngine 上的 Set Volume 动作 和 Set Switch 动作。Set Volume 动作对音量做一个值为 -2 的相对音量偏置,Set Switch 动作把切换开关组 Ground_Material 的值切换到Tiles。

然后把事件打包成 SoundBank ,以便加载到游戏中,此后,游戏代码便可触发这些事件。例如,当玩家进入厨房时,您会触发将 Ground_Material 切换开关设置为 Tiles 的事件。

事件一旦集成到游戏中声音设计师便可以继续处理它们,更改或修改它们包含的动作或者它们引用的对象。由于游戏触发的仍是同一事件,因此不再需要程序员动手介入,也无需重新编译代码,Wwise 用户所做的更改就会在游戏中生效。

把事件集成到您的游戏中

AK::SoundEngine::PostEvent "PostEvent()" 函数对要处理的事件进行排队,并可依据事件的 ID 或名称来识别事件。每当需要触发事件时,游戏代码都会调用此函数。

然而在调用 AK::SoundEngine::RenderAudio "RenderAudio()" 函数之前,不会处理任何事件。每游戏帧调用一次 AK::SoundEngine::RenderAudio "RenderAudio()" 是种合理的做法。

在调用 AK::SoundEngine::RenderAudio "RenderAudio()" 时:

  • 声音引擎开始处理自上次调用 AK::SoundEngine::RenderAudio "RenderAudio()" 以来发送的所有事件。
  • 另提供新的 3D 位置、切换开关、状态和 RTPC 值。

事件完全按照游戏发送事件的顺序处理。事件中的动作将按照它们在 Wwise 中设计的顺序处理。

在同一帧中发送的所有事件将尽量在同一时间处理,但包含延时的动作除外,它们显然会稍后再执行。

如果因故长时间(例如好几帧)没有调用 AK::SoundEngine::RenderAudio "RenderAudio()" 函数,则流播放声音将继续正常播放,但在下次调用 AK::SoundEngine::RenderAudio "RenderAudio()" 之前不会启动新的事件和应用新的定位。

启用 ID

要使用 ID,必须在选中 Wwise 中 Generate SoundBanks 对话框上的“Generate header file”选项的情况下生成 SoundBank。定义文件 Wwise_IDs.h 包含所有必要的 ID。每次生成 SoundBank 时都会更新此文件。

事件通知

AK::SoundEngine::PostEvent "PostEvent()" 函数接受指向 AkEventCallbackFunc() 函数的指针,当遇到标记(marker)时或者事件被终止时将调用 AkEventCallbackFunc() 函数。这可能对事件和声音播放的同步非常有用。

当事件的所有动作执行完毕,并且此事件触发的所有声音都已完成播放时,事件就被认为终止了。注意,某些事件可能永远都不会自行结束。例如,当事件包含一个无限循环的声音时,只有当另一事件停止此声音时才会触发回调。

标记必须通过外部波形文件编辑器(例如 SoundForge® 或 Adobe® Audition®)创建。声音引擎将识别出这些提示点,如果您在调用 AK::SoundEngine::PostEvent "PostEvent()" 指定了回调函数,声音引擎还会通知您的回调函数。

参见

发送事件与实际播放声音之间的延迟

我们可以估计发送事件的时间与平台开始播放音频数据的时间之间的延迟。

在游戏线程中调用 SDK 功能 PostEvent()。发送一个事件就会发送一个播放请求。在游戏调用函数 RenderAudio() 前不会处理此请求;它实际上并不渲染音频,而只是设置要求处理自上次调用 RenderAudio() 以来所发布请求的通知。正常情况下,游戏为每个游戏帧调用一次 RenderAudio()。但是,这不是硬性要求;可以随时调用它来尽快强制执行最近发送的事件。

一旦调用 RenderAudio(),将许可音频(EventManager)线程来处理完先前发送的事件/命令。然而请注意,该线程与平台音频的处理速率同步。只有当音频输出模块已处理完缓冲区,并因此空出其环形缓冲区的一段以供写入时才能开始“重新填满音频数据”的过程。

最后,还要考虑输出模块的环形缓冲区大小。在 Windows 中初始化声音引擎时,您可以使用 AkPlatformInitSettings::uNumRefillsInVoice 在平台专用参数中指定环形缓冲区的大小。这将给出声部缓冲区中的“填充缓冲区”(refill buffer)数量,其中 2 代表双缓冲,默认值为 4。选择此数字是在减少延迟(小缓冲)和提高系统对内存匮乏的抵抗性(大缓冲)之间做出平衡的结果。

“填充缓冲区”通常又被称为“音频帧”(audio frame),是由采样率与频率的比值所确定的。因此,在 Windows 的高质量模式中,这通常为:(1,024 个采样)/ (48,000 Hz) = 21.3 毫秒。如果我们使用 AkInitSettings::uNumSamplesPerFrame 将采样设为 512,则音频帧为:(512 个采样)/ (48,000 Hz) = 10.6 毫秒

因此,概括地说,“声音引擎的总延迟”由以下决定:

  1. 调用 PostEvent() 与调用 RenderAudio() 之间的时间(这取决于游戏),这意味着在每帧调用 一次 RenderAudio() 的 60 帧每秒的游戏中,此时间为 16 毫秒。
  2. 调用 RenderAudio() 与音频帧边界之间的时间:0 到 21 毫秒,取决于调用 RenderAudio() 是在当前音频帧结束还是开始时。
  3. 输出级缓冲。对于采用双缓冲的输出级:2 * 21 毫秒 = 42 毫秒延迟。
    1. 音频帧速率。对于降低的音频帧速率和双缓冲输出级:2 * 11 毫秒 = 22 毫秒延迟。

总之,在每秒 60 帧的刷新系统中:

  • 最好情况是 42 毫秒延迟
  • 最坏情况是 79 毫秒延迟

流播放

在上述声音引擎延迟计算的基础上,如果声音为 100% 流媒体(不向内存加载任何内容),那么您需要加上固有的 I/O 延迟。为避免 I/O 延迟,可指定 Zero Latency(零延迟),以便把声音的开始段加载到内存中。缓冲的大小由 Prefetch Length(预读长度)决定,在默认情况下设为100 毫秒,这个长度通常是安全的。

事件处理示例

以下是几个有用的示例(伪码):

PostEvent( Play_Sound1, GameObj_X )
PostEvent( Stop_Sound1, GameObj_X)
结果:不会播放任何内容。
PostEvent( Stop_Sound1, GameObj_X )
PostEvent( Play_Sound1, GameObj_X )
Result: Sound1 will play
SetSwitch( Grass, GameObj_X )
PostEvent( Play_SwitchFootStep, GameObj_X )
SetSwitch( Concrete, GameObj_X )
PostEvent( Play_SwitchFootStep, GameObj_X )
Result: Grass and concrete sounds will both play
SetSwitch( Grass, GameObj_X )
SetSwitch( Concrete, GameObj_X )
PostEvent( Play_SwitchFootStep, GameObj_X )
PostEvent( Play_SwitchFootStep, GameObj_X )
Result: Concrete sounds will play twice

有关集成事件的示例,请参阅 快速入门示例集成——事件