版本

menu_open
Wwise SDK 2019.1.11
Wwise SoundBank 管理示例

在决定如何管理游戏中的 SoundBank 之前,强烈建议您仔细阅读以下文档。

理解不同的 SoundBank 管理方法

简介

为了尽可能灵活满足广大游戏类型的需求,Wwise 引进了一种新方法来管理游戏中的 SoundBank。此新方法与传统方法并不抵触,只是更加可控和灵活,让您能更好地满足游戏的需求。

此新方法与现有传统方法相比有三项重大改进:

  • SoundBank 中现在不仅可以装入事件,还可以装入对象结构内容(声音、容器、角色混音器等)、工作单元和文件夹。
  • 您可以随意指定 SoundBank 中要包含的信息类型。这意味着您只可向 SoundBank 中添加媒体、结构数据、事件内容或它们的任意组合。例如,您可以创建一个 SoundBank,让其只包含与特定事件相关的媒体。
  • 您可以启用或弃用 SoundBank 中的条目。

此新方法的主要优势是可以将媒体内容拆分到载入内存的多个 SoundBank 中去。 例如,假设整个游戏的音乐是用单一事件来启动的。在使用传统方法时,您将向 SoundBank 中添加一个事件,该事件会自动添加所有相应内存内声音和预读媒体,包括预读只在游戏结束时播放的歌曲。把整个游戏的全部媒体都存储到内存里似乎极其低效。而使用新方法则可以更好的管理内存,您可以把音乐媒体分到多个 SoundBank 中,只在可能需要播放声音时才加载相应声音。

通过把媒体分到多个 SoundBank 中,您还可以对要加载的媒体优先排序。例如,在内存有限的环境中,您只需要加载最重要的媒体。可以把非关键的媒体存储在单独的 SoundBank 中,只在空间够用时才载入内存。在传统方法中,关键的和非关键的媒体文件全都包含在同一个 SoundBank 中。如果 SoundBank 太大,无法载入内存,则里面的声音都不会播放,包括关键声音。

不同的 SoundBank 管理方法

以下各节介绍可用于生成 SoundBank 并把 SoundBank 集成到游戏中的不同方法。您可以在同一款游戏中使用其中一种方法或者多种方法的组合。由于每款游戏各不相同,因此您选择的方法将取决于游戏的具体需要。

在创建 SoundBank 时您所做的选择会极大影响到音频素材管理所需的工作量,并且对游戏性能有直接影响。强烈建议声音设计师和程序员仔细阅读本文档,以便充分了解各种可能性。声音设计师和程序员应该携手找出满足游戏具体需求的策略。记住,所有解决方案都可行,但您在选择时应考虑到内存占用、I/O 以及集成到游戏中的便利度。每种方法各有长短,因此主要问题是平衡内存占用和集成便利度的问题。

方法 1:一个“一体化” SoundBank

使用此方法时,所有事件内容、声音结构数据和媒体文件全部存储在一个 SoundBank 中,一次性载入内存。

适用此方法的情况如下:

  • 游戏中音频素材数量有限。
  • 游戏有大量的内存空间。

当然,大多数游戏的内存很紧张,但此方法的主要优点是使用和维护都非常简单。采用此方法的主要理由是用极少的时间就能把整个 Wwise 工程集成到游戏中去。

操作方法:

在 Wwise 中

  1. 创建一个 SoundBank,并适当命名。
  2. 加载 SoundBank 到 SoundBank Editor 中,然后把以下项目拖入“Add”选项卡:
    • “\Actor-Mixer Hierarchy\Default Work Unit”
    • “\Events\Default Work Unit”
    • “\Interactive Music Hierarchy\Default Work Unit”(只在使用互动音乐时需要)
    • “\Dynamic Dialogue\Default Work Unit”(只在使用动态对白时需要)
      备注: 默认情况下,Events、Structures 和 Media 列下的复选框会全部选中。此方法的好处在于,我们可在 SoundBank 中加入所需的一切内容。

      技巧: 向 SoundBank 中添加完整工作单元而非单个工程元素的优势是,您后续不必重新编辑 SoundBank ,工作单元的内容变化可以直接生效。这是因为 Wwise 会维护 SoundBank 元素与工程元素之间的链接。如果更改工程中的工作单元, 则 SoundBank 会自动更新。如果使用此方法,只需单击 Generate 按钮便可生成一套新的 SoundBank。
      备注: 在新工程中,只有默认工作单元可用。如果创建了更多的工作单元,则这些工作单元也应按照需要添加到 SoundBank 中。
  3. 在所有工作单元均已添加到 SoundBank 中后,您可以生成 SoundBank,然后把生成的 SoundBank 文件夹复制到游戏应用程序中。

在游戏中

由于此游戏只有一个 SoundBank,因此您只需在初始化游戏时加载它。当然,第一步是必须正确初始化声音引擎。

...
// 在此初始化声音引擎。
...
// 加载初始化 Bank 和“一体化” SoundBank 。
AkBankID bankID; // 此示例中未使用。
AKRESULT eResult = AK::SoundEngine::LoadBank( L"Init.bnk", AK_DEFAULT_POOL_ID, bankID );
if( eResult == AK_Success )
{
eResult = AK::SoundEngine::LoadBank( L"MyAllInOneBank.bnk", AK_DEFAULT_POOL_ID, bankID );
}
...

有关此方法的更多说明

优点:

  • 从声音设计师的角度看,用这个方法来维护 SoundBank 内容最容易。
  • 更改 SoundBank 的内容时无需重新编译游戏,因为游戏总是加载同一 SoundBank 。
  • 游戏中没有复杂的 SoundBank 加载和卸载操作。
  • 无需在游戏中跟踪哪些声音可用或不可用。

缺点:

  • 内存使用效率低,整个游戏的所有事件、结构和内存媒体一直保持加载状态。
技巧: 虽然此方法可以快速、轻松地把音频集成到游戏中去,但是不要等到项目结束时再换方法来改进内存占用。

方法 2:多个完整的 SoundBank

适用此方法的情况如下:

  • 游戏或其音频内容拆分成多个部分。

此方法非常适用于单人游戏。在单人游戏中,玩家的当前位置决定要用到什么声音。通过把音频内容分到多个 SoundBank 中,内存管理比第一种方法更高效,同时把音频集成到游戏中仍然比较容易。

操作方法:

首先,您必须确定如何拆分 SoundBank。 例如,您可以按以下方式来拆分:

  • 一个通用 SoundBank ,其中包含游戏中任意时刻都可能发生的所有事件。此 SoundBank 可保持一直加载。
  • 每个关卡或每个环境配备一个 SoundBank。对于根据主人公的实际位置而播放的声音。
  • 可增加一些 SoundBank ,具体取决于特定游戏的需求。

在 Wwise 中

  1. 创建游戏所需要的 SoundBank ,并取好名字,例如“CommonEvents”、“Level_1”、“Level_2”和“Level_3”。
  2. 在 Wwise 中把事件分组到文件夹中。为每个 SoundBank 创建一个文件夹,然后把每个文件夹拖到相应 SoundBank 中。通过文件夹来添加 SoundBank 内容,就可以避免每次工程中加了新事件时都必须编辑 SoundBank 内容的情况。当文件夹中的内容改变时, SoundBank 会自动更新。
  3. 把所有事件添加到各自的 SoundBank 中。只有添加放在原始文件夹之外的事件时才需要此步骤。如果某个事件需要同时位于多个 SoundBank 中,则只需把该事件添加到所需的 SoundBank 中即可。
  4. 生成 SoundBank,然后把生成的 SoundBank 文件夹复制到游戏应用程序中。

在游戏中

在游戏中,只需在正确的时间加载正确的 SoundBank 即可。例如,游戏可以在开始时加载通用 SoundBank ,然后根据游戏中玩家的实际位置加载其它 SoundBank 。注意,某些游戏需要有足够的内存才可以一次加载多个“关卡”的 SoundBank,实现各关卡之间的过渡。

...
// 在此初始化声音引擎。
...
// 加载初始化 Bank 和通用 SoundBank 。
AkBankID bankID; // 此示例中未使用。
AKRESULT eResult = AK::SoundEngine::LoadBank( L"Init.bnk", AK_DEFAULT_POOL_ID, bankID );
if( eResult == AK_Success )
{
eResult = AK::SoundEngine::LoadBank( L"CommonEvents.bnk", AK_DEFAULT_POOL_ID, bankID );
}
...
// 在代码的多个位置,根据实际需要而定:
eResult = AK::SoundEngine::LoadBank( L"Level_1.bnk", AK_DEFAULT_POOL_ID, bankID );
...
eResult = AK::SoundEngine::LoadBank( L"Level_2.bnk", AK_DEFAULT_POOL_ID, bankID );
...
eResult = AK::SoundEngine::LoadBank( L"Level_3.bnk", AK_DEFAULT_POOL_ID, bankID );
...
eResult = AK::SoundEngine::UnloadBank( L"Level_1.bnk", NULL );
...
eResult = AK::SoundEngine::UnloadBank( L"Level_2.bnk", NULL );
...
eResult = AK::SoundEngine::UnloadBank( L"Level_3.bnk", NULL );

有关此方法的更多说明

优点:

  • 可能比“一体化” SoundBank 技术需要的内存少得多。
  • 非常容易集成到游戏中。

缺点:

  • 不太适合在线游戏或者基于事件的游戏,在这些游戏中,音频需求不仅仅由主人公位置等简单事实来驱动。
  • 可能造成内存中重复加载某些音频源,由于不同 SoundBank 中可能会包含重复数据。
  • 可能造成 SoundBank 的磁盘总空间增加,因为不同的 SoundBank 可能有相似的内容。

方法 3:微管理媒体

适用此方法的情况如下:

  • 游戏有大量的媒体素材要载入内存。
  • 声音设计师难以事先预测媒体需求。
  • 项目使用切换开关和状态来确定具体事件要播放的声音。
  • 声音不易拆分。

操作方法:

有的游戏非常复杂,可能要按多种因素来触发声音,这些因素包括游戏纹理、时辰、游戏对象的运动,在多人在线游戏中,还可能包括其它玩家的操作。例如在基于事件或对象的环境中,可根据声音与其它游戏对象的距离来决定把声音载入内存。每个游戏对象视情况都可能有一系列必须加载的 SoundBank,这可能是当游戏对象位于一定距离内时要加载的内容,也可能是只要这些对象存在就得加载的内容。

另外,切换开关和状态可决定播放的具体声音。当播放这些声音的事件添加到 SoundBank 时,所有可能用到的媒体也会自动添加到 SoundBank 中。例如,您可能有一个叫“Play_Footstep”的事件。此事件将根据地面的当前纹理播放相应的声音,要播放的具体声音通过切换开关来指定。虽然此方法效果很好,但当游戏场景发生在伦敦的一栋建筑物内时,在内存中一直保留声音“footstep_sand.wav”或“footstep_winter.wav”是浪费内存。

为了避免浪费内存,您可以向 SoundBank 中添加事件或声音结构,然后指定 SoundBank 中启用哪些相应的声音。举例而言,如何我们对不同的纹理使用不同的脚步声,则可创建以下 SoundBank :

  • “EventBank”,其中将包含“Play_Footstep”事件和结构。
  • “Winter_Footstep_bank”,其中包含仅在游戏某一部分(冬天)才会发生的脚步声的媒体。
  • “Desert_Footstep_bank”,其中包含仅在游戏某一部分(沙漠)才会发生的脚步声的媒体。
  • “Common_Footstep_bank”,其中包含在任何地方(木材、混凝土等)均可发生的脚步声的媒体。

在 Wwise 中

重建上一示例的方法:

假设您的游戏中有三种不同的纹理(雪地、沙地和混凝土地)。在 Wwise 中,有一个切换容器根据切换开关“ground_texture”来播放三个随机容器中的一个。三个随机容器中的每一个针对给定的纹理都有四种版本的脚步声。

  1. 创建一个叫“EventBank”的 SoundBank ,并加载到 SoundBank Editor。
  2. 把“Play_Footstep”事件拖到 SoundBank Editor 的 Add 选项卡中。
  3. 取消选择此事件的“Media”复选框,但保留选中 Events 和 Structures 复选框。
  4. 创建另外三个 SoundBank,每种纹理对应一个 SoundBank。
  5. 将与此纹理相关的随机容器拖入各个 SoundBank 中。
    备注: 如果我们不采取拖放随机容器,而是分别拖放每个声音,也同样可行。 使用容器的优点是,当容器中的内容发生变化时,容器中的所有声音将自动添加到 SoundBank 中,而无需手动完成所有更改。
  6. 取消选择三个纹理 SoundBank 中每个 SoundBank 的“Events”和“Structures”复选框,只保留选中 Media(媒体)类型。
  7. 生成 SoundBank,然后把生成的 SoundBank 文件夹复制到游戏应用程序中。

到现在,我们有四个 SoundBank了。一个 SoundBank 中包含与要播放的音频相关的事件和结构数据,而其它三个 SoundBank 中仅包含与特定地面纹理相关的媒体。

在游戏中

// 加载初始化和事件 SoundBank
AkBankID bankID; // 此示例中未使用。
AKRESULT eResult = AK::SoundEngine::LoadBank( L"Init.bnk", AK_DEFAULT_POOL_ID, bankID );
if( eResult == AK_Success )
{
eResult = AK::SoundEngine::LoadBank( L"EventBank.bnk", AK_DEFAULT_POOL_ID, bankID );
}
if( eResult == AK_Success )
{
eResult = AK::SoundEngine::LoadBank( L"Common_Footstep_bank.bnk", AK_DEFAULT_POOL_ID, bankID );
}
...
// 可能位于代码中的多个位置,视情况而定:
eResult = AK::SoundEngine::LoadBank( L"Winter_Footstep_bank.bnk", AK_DEFAULT_POOL_ID, bankID );
...
eResult = AK::SoundEngine::LoadBank( L"Desert_Footstep_bank.bnk", AK_DEFAULT_POOL_ID, bankID );
...
eResult = AK::SoundEngine::UnloadBank( L"Winter_Footstep_bank.bnk", NULL );
...
eResult = AK::SoundEngine::UnloadBank( L"Desert_Footstep_bank.bnk", NULL );

有关此方法的更多说明

此示例只是此方法能做到的众多操作之一。由于可以根据声音和事件逐一决定各个 SoundBank 中要启用的内容,因此您可以全面控制 SoundBank 的内容。虽然您可以为游戏中的各个声音创建单独的 SoundBank ,但维护起来非常困难,因为新声音都需要新代码,才能在游戏的正确位置加载 SoundBank 。每款游戏的目标是尝试在控制粒度和集成到游戏中的便利度之间找到良好的平衡。

技巧: 如果您对分别加载声音的方案感兴趣,可考虑使用 PrepareEvent API、PinEventInStreamCache API 或 SetMedia API,而非 LoadBank。

优点:

  • 是优化内存利用率的最佳选择。
  • 您可以全面控制在游戏的什么地方加载哪个媒体。

缺点:

  • 声音设计师和游戏程序员需要大量的沟通,共同决定要加载什么 SoundBank 以及何时加载。

方法 4:准备事件

适用此方法的情况如下:

  • 媒体需要极小的粒度级别,以保持较低的内存占用。
  • 您无需担心把媒体素材拆分到多个 SoundBank 中。

确切地说,什么是采用 Prepare 方式的事件?在调用 PrepareEvent 功能时,系统分析事件,确保与此事件相关的所有结构和媒体都载入了内存。否则,系统将自动从磁盘中传输缺少的信息。在显式撤消 Prepare 之前,事件会一直处于准备就绪状态。

操作方法:

此方法要求您显式地把媒体从包含元数据(结构和事件)的 SoundBank 中排除。如果需要,这些结构可以是单独 SoundBank 的一部分。然而,结构的最小加载粒度是一个 SoundBank,因此通常合理的做法是把结构和所在事件装在同一个 SoundBank 中。

在创建使用 PrepareEvent 机制的 SoundBank 时,所需事件和结构必须位于至少一个 SoundBank 中,而且媒体必须作为文件系统中的松散文件来访问。如果 SoundBank 引用的媒体不包含在任何其它 SoundBank 中,CopyStreamedFiles 工具将假定它搭配 PrepareEvent API 使用,并把此松散文件复制到输出目录下。

在对一个事件做 Prepare 之前,事件本身必须已从一个 SoundBank 加到内存中(使用 LoadBank())。这个操作是必需的,因为事件中包含要对事件做 Prepare 操作所需的依赖关系信息。

在 Wwise 中

  1. 创建名为“Events”的 SoundBank,并加载到 SoundBank Editor 中。
  2. 把工程中的一些事件添加到“Events” SoundBank 中,或仅添加事件工作单元。
  3. 取消选择“Media”复选框,保留选中“Events”和“Structures”复选框。
  4. 生成 SoundBank。当生成的 SoundBank 引用了若干件松散媒体时,您将收到警告消息:未在任何 SoundBank 中找到 SoundBank <bank> 中的 <structure> 所引用的媒体。您可以右键单击此消息,把它添加到忽略列表中。
  5. 在默认情况下,将使用 CopyStreamedFiles 工具把 SoundBank 所引用的松散媒体复制到 SoundBank 的同一输出目录中。
  6. 把生成的 SoundBank 文件夹(随松散媒体一起)复制到游戏应用程序中。
备注: 单一 SoundBank 中所包含的结构数据不可在运行时拆分。因此,如果使用 AK::SoundEngine::PrepareEvent,并且需要另一 SoundBank 中的结构数据,则将一次性加载该 SoundBank 中的所有结构。为此,您可能需要把工程中的结构内容拆分为多个 SoundBank,从而最大限度地避免把不必要的信息载入内存中。

在游戏中

// 初始化声音引擎。
AkInitSettings initSettings;
AkPlatformInitSettings platformInitSettings;
// 设置所需的设置。
...
// 设置 PrepareEvent 相关设置。
initSettings.bEnableGameSyncPreparation = false; // 当前示例中未使用。
// 把内存池分配成采用 Prepare 方式的媒体将加载的空间。
// 如果不这样做,则 Prepare 操作将失败。
{
// (可选)为内存池命名。这在性能分析时非常有用。
AK::MemoryMgr::SetPoolName( initSettings.uPrepareEventMemoryPoolID, L"PrepareEventPool" );
}
AKRESULT eResult = AK.SoundEngine.Init( initSettings, platformInitSettings );
if( eResult != AK_Success )
{
// 处理错误。
}
// 加载初始化 SoundBank 和事件/结构 SoundBank 。
AkBankID bankID; // 此示例中未使用。
AKRESULT eResult = AK::SoundEngine::LoadBank( L"Init.bnk", AK_DEFAULT_POOL_ID, bankID );
if( eResult == AK_Success )
{
eResult = AK::SoundEngine::LoadBank( L"Events.bnk", AK_DEFAULT_POOL_ID, bankID );
}
...
// 然后在代码中的各个位置:
const char * pEventsNameArray[1] = { "My_Event_Name" };
// Prepare 事件:
eResult = AK::SoundEngine::PrepareEvent( Preparation_Load, pEventsNameArray, 1 ); // 1 is the array size
// 撤消 Prepare 事件:
eResult = AK::SoundEngine::PrepareEvent( Preparation_Unload, pEventsNameArray, 1 ); // 1 is the array size

有关此方法的更多说明

记住,必须把调用 AK::SoundEngine::PrepareEvent 视为调用 I/O 函数。在上例中,我们使用了阻塞函数。您可以使用 AK::SoundEngine::PrepareEvent 函数的其它重载来实现非阻塞调用,然后用另一个回调来响应加载完成的通知。

优点:

  • SoundBank 生成过程简单。
  • 媒体的粒度级别很小。
  • 保持较低的总体内存占用。
  • 易于实现自动化。

缺点:

  • 逐一加载媒体素材,可能导致磁盘上的读取和寻址次数增加。
  • 对总体内存占用量的控制减弱。
  • 不适合互动音乐。

方法 5:Prepare 事件和游戏同步器(切换开关和状态)

适用此方法的情况如下:

  • 媒体需要较小的粒度级别以保持较低的内存占用。
  • 无需担心把媒体素材拆分到多个 SoundBank 中。
  • 工程中有事件根据切换开关或状态播放不同的声音。
  • 工程中有互动音乐需要根据切换开关或状态来播放。

此方法与上一方法(方法 4:准备事件 )基本相同,不同的是在 Prepare 事件时能更好地控制要加载的媒体。使用此方法时,内存中只加载与采用 Prepare 方式的事件和当前活跃的游戏同步器均相关的媒体。

假设有一个简单的工程,里面有两个事件:“Play_Maincharacter_FootSteps”和“Play_Monster_Footsteps”。各个事件播放不同的切换容器。切换容器根据主人公脚下的地面纹理播放不同的随机声音。切换开关组名称是“GroundTexture”,有三个可能的状态:“Snow”、“Concrete”和“Sand”。

Wwise 中的切换容器层级结构如下所示:

Switch_Container_Footstep_Main_Character

  • Random_Container_Sound_Snow_main
    • Sound_Snow_main_1
    • Sound_Snow_main_2
    • Sound_Snow_main_3
  • Random_Container_Sound_Concrete_main
    • Sound_Concrete_main_1
    • Sound_Concrete_main_2
    • Sound_Concrete_main_3
  • Random_Container_Sound_Sand_main
    • Sound_Sand_main_1
    • Sound_Sand_main_2
    • Sound_Sand_main_3

以及

Switch_Container_Footstep_Monster

  • Random_Container_Sound_Snow_Monster
    • Sound_Snow_Monster_1
    • Sound_Snow_Monster_2
    • Sound_Snow_Monster_3
  • Random_Container_Sound_Concrete_Monster
    • Sound_Concrete_Monster_1
    • Sound_Concrete_Monster_2
    • Sound_Concrete_Monster_3
  • Random_Container_Sound_Sand_main
    • Sound_Sand_Monster_1
    • Sound_Sand_Monster_2
    • Sound_Sand_Monster_3

在本例中,我们有 18 个声音(6 组声音,3 个声音一组)可能会载入内存。

您可以使用方法 方法 4:准备事件 ,但粒度级别不会比根据事件载入内存的 6 个声音低。您可以使用 方法 3:微管理媒体 来获得更好的粒度级别,但您必须为此简单项目创建 6 个不同的 SoundBank (每个工程中的 SoundBank 数量快速增加)。然后,当怪兽出现时,您必须查看可能的纹理,然后加载相应的 SoundBank 。

使用当前方法时,您只需指定可能的事件和游戏同步器,然后加载相应媒体即可。和 PrepareEvent 一样,所有的媒体必须作为文件系统中的松散文件可被访问。

操作方法:

在 Wwise 中

  1. 创建名为“Events”的 SoundBank ,并加载到 SoundBank Editor。
  2. 把 2 个事件拖到 SoundBank Editor 的 Add 选项卡中。
  3. 取消选择“Media”复选框,保留选中两个事件的“Events”和“Structures”复选框。
  4. 生成 SoundBank。当生成的 SoundBank 引用了若干件松散媒体时,您将收到警告消息:未在任何 SoundBank 中找到 SoundBank <bank> 中的 <structure> 所引用的媒体。您可以右键单击此消息,把它添加到忽略列表中。
  5. 在默认情况下,将使用 CopyStreamedFiles 工具把这些 SoundBank 所引用的松散媒体复制到 SoundBank 的同一输出目录中。
  6. 把生成的 SoundBank 文件夹(随松散媒体一起)复制到游戏应用程序中。

在游戏中

  • 在游戏需要两个事件前,加载“Event” SoundBank 。
  • 当可能的地面纹理在附近时,激活游戏同步器。
  • 一直激活 Concrete 纹理。
  • 一直 Prepare 主人公脚步事件。
备注: 单一 SoundBank 中所包含的结构数据不可在运行时拆分。因此,如果使用 AK::SoundEngine::PrepareEvent,并且需要另一 SoundBank 中的结构数据,则将一次性加载该 SoundBank 中的所有结构。为此,您可能需要把工程中的结构内容拆分为多个 SoundBank,从而最大限度地避免把不必要的信息载入内存中。
// 初始化声音引擎。
AkInitSettings initSettings;
AkPlatformInitSettings platformInitSettings;
// 设置所需的设置。
...
// 设置 PrepareEvent 相关设置。
////////////////////////////////////////////////////////////////
// bEnableGameSyncPreparation 标志设为 true,以激活
// 准备游戏同步器机制。当设置为 false 时,与所有游戏
// 同步器相关的媒体将加载,并且无需
// 调用 AK::SoundEngine:PrepareGameSyncs。
//
// 当设置为 true 时,不会加载与游戏同步器相关的媒体,
// 除非游戏同步器已通过调用 AK::SoundEngine:PrepareGameSyncs 被激活
////////////////////////////////////////////////////////////////
initSettings.bEnableGameSyncPreparation = true;
// 把内存池分配成采用 Prepare 方式的媒体将加载的空间。
// 如果不这样做,则 Prepare 操作将失败。
{
// (可选)为内存池命名。这在性能分析时非常有用。
AK::MemoryMgr::SetPoolName( initSettings.uPrepareEventMemoryPoolID, L"PrepareEventPool" );
}
AKRESULT eResult = AK.SoundEngine.Init( initSettings, platformInitSettings );
if( eResult != AK_Success )
{
// 处理错误。
}
// 加载初始化 Bank 和事件/结构 SoundBank 。
AkBankID bankID; // 此示例中未使用。
AKRESULT eResult = AK::SoundEngine::LoadBank( L"Init.bnk", AK_DEFAULT_POOL_ID, bankID );
if( eResult == AK_Success )
{
eResult = AK::SoundEngine::LoadBank( L"Events.bnk", AK_DEFAULT_POOL_ID, bankID );
}
// ... 在此时,
// 两个事件已加载但尚未 Prepare。当前没有加载媒体。
const char * pNameArray[1];
// Prepare 主人公脚步事件。
pNameArray[0] = "Play_Maincharacter_FootSteps";
eResult = AK::SoundEngine::PrepareEvent( Preparation_Load, pNameArray, 1 ); // 1 是数组大小
// ... 在此时,
// 已 Prepare 一个事件,但尚未加载任何媒体。
// 从现在起,游戏中始终可用 concrete。
pNameArray[0] = "Concrete";
eResult = AK::SoundEngine::PrepareGameSyncs( Preparation_Load, in_eType, "GroundTexture", pNameArray, 1 );
// ... 在此时,
// 已加载 3 个声音:Sound_Concrete_main_1、Sound_Concrete_main_2 和 Sound_Concrete_main_3。
// 现在,假设主人公进入一片雪地。
pNameArray[0] = "Snow";
eResult = AK::SoundEngine::PrepareGameSyncs( Preparation_Load, in_eType, "GroundTexture", pNameArray, 1 );
// ... 在此时,
// 又加载了 3 个声音:Sound_Snow_main_1、Sound_Snow_main_2 和 Sound_Snow_main_3
// 然后假设怪兽突然在此时出现。
pNameArray[0] = "Play_Monster_Footsteps";
eResult = AK::SoundEngine::PrepareEvent( Preparation_Load, pEventsNameArray, 1 ); // 1 是数组大小
// ... 在此时,
// 又加载了 6 个声音( Sound_Concrete_Monster_1.2.3 和 Sound_Snow_Monster_1.2.3)
// 现在我们的玩家决定逃离怪兽,而怪兽在玩家身后紧追不舍。
// 怪兽和玩家一直奔跑,来到没有积雪的地方。
pNameArray[0] = "Snow";
eResult = AK::SoundEngine::PrepareGameSyncs( Preparation_Unload, in_eType, "GroundTexture", pNameArray, 1 );
// ... 在此时,
// 与积雪相关的 6 个声音(Sound_Snow_Monster_1.2.3 和 Sound_Snow_main_1.2.3)已从内存中卸载。
...
备注: 您调用 AK::SoundEngine::PrpareEvent 和 AK::SoundEngine::PrepareGameSync 的顺序不重要。当状态改变时,每次都会通过交叉匹配事件和游戏同步器来更新媒体池。

有关此方法的更多说明

优点:

  • SoundBank 生成过程简单。
  • 媒体的粒度级别很小。
  • 保持较低的总体内存占用。
  • 易于实现自动化。
  • 只加载有用的媒体。

缺点:

  • 逐一加载媒体素材,可能导致磁盘上的读取和寻址次数增加。
  • 对内存总体占用量的控制减弱。
  • 在 Prepare 大量需要加载新数据的事件时,激活游戏同步器可能导致流播放带宽升高。
参见
AKSOUNDENGINE_API AKRESULT PrepareEvent(PreparationType in_PreparationType, const char **in_ppszString, AkUInt32 in_uNumEvent)
Audiokinetic namespace
AKSOUNDENGINE_API AKRESULT PrepareGameSyncs(PreparationType in_PreparationType, AkGroupType in_eGameSyncType, const char *in_pszGroupName, const char **in_ppszGameSyncName, AkUInt32 in_uNumGameSyncs)
AKSOUNDENGINE_API AKRESULT SetPoolName(AkMemPoolId in_poolId, const char *in_pszPoolName)
AKSOUNDENGINE_API void GetDefaultInitSettings(AkInitSettings &out_settings)
AkMemPoolId uPrepareEventMemoryPoolID
Memory pool where data allocated by AK::SoundEngine::PrepareEvent() and AK::SoundEngine::PrepareGameS...
Definition: AkSoundEngine.h:214
AKSOUNDENGINE_API AKRESULT UnloadBank(const char *in_pszString, const void *in_pInMemoryBankPtr, AkMemPoolId *out_pMemPoolId=NULL)
AKSOUNDENGINE_API void GetDefaultPlatformInitSettings(AkPlatformInitSettings &out_platformSettings)
AKSOUNDENGINE_API AKRESULT LoadBank(const char *in_pszString, AkMemPoolId in_memPoolId, AkBankID &out_bankID)

此页面对您是否有帮助?

需要技术支持?

仍有疑问?或者问题?需要更多信息?欢迎联系我们,我们可以提供帮助!

查看我们的“技术支持”页面

介绍一下自己的项目。我们会竭力为您提供帮助。

来注册自己的项目,我们帮您快速入门,不带任何附加条件!

开始 Wwise 之旅