『Scars Above』におけるオーディオ最適化の手段

ゲームオーディオ

はじめに

今回は私たちのゲーム『Scars Above』において、オーディオのプロファイリングや最適化の過程で採用したさまざまな方針について説明したいと思います。私たちが実施した各種処理を解き明かし、オーディオ最適化の考え方やプランニングについてアドバイスをしながら、サウンドデザイナーのみなさんのお役に立てればと思います。

上級者の読者専用の内容とは言い切れませんが、WwiseやUnreal Engineの基本を理解し、それぞれのワークフローをある程度知っていた方は読みやすいと思います。読者がWwiseオーサリングツールやUnreal Editorを使いサウンドをインポート、整理、統合したことがあると考えてすすめてゆきます。

『Scars Above』はUnreal Engineバージョン4.27.2とWwise Unreal Engineインテグレーションのバージョン2021.1.9.7847.2311をベースにした弊社カスタムエンジンでコンパイルしました。

目次

最適化の原則

最適化はゲーム開発における重要なプロセスであり、制作過程の後半に行われることが多いです。しかしオーディオバジェットを明確にしてゲームそのものの限界を理解しておくことは賢明であり、最適化を初期の段階で計画することをおすすめします。あらかじめ決められた範囲内に収めることにより、どのプラットフォームや構成においてもゲームが意図したとおりに動作します。オーディオが正常に機能している時はサウンドの問題が起きません。一方、バジェットを超過した場合は突然の音の欠落、オーディオグリッチ、フレームレートの低下などゲームプレイに悪影響を与えるさまざまな問題が発生する可能性があります。このためオーディオ計画を立ててオーディオの最適化を図り、常に最高のパフォーマンスを目指すことが重要です。

ゲームをよりスムーズに進行させるオーディオ面での選択肢はいくつかあります。オーディオアセットのメモリ消費の削減や、オーディオスレッドへの負担削減に着目した方法などです。多くはメモリとCPUのどちらにも効果的な対策です。

WwiseとUnreal Engineを使用していますが、実施できる対策はどちら側にもあります。『Scars Above』のオーディオ最適化のために行った手順をこれからすべて説明します。

私たちのゲームの制約事項

私たちは制作の後半に入ってからオーディオがゲームパフォーマンスに与える影響に注目するようになりました。プログラミングチームと話し合い、各プラットフォームのオーディオバジェットの配分について合意しました。

ゲーム全体で最も懸念されたことは第8世代のプラットフォームでOOM(アウトオブメモリ、メモリ不足)となることでした。第8世代のメモリリソースはやや制限され、PlayStation 4やXbox Oneなどの必要最小限に抑えたバージョンでは特にそうです(第8世代プラットフォームの使用可能なRAMは第9世代の使用可能なRAMの約半分です)。第8世代やそれ以下のスペックのPCではオーディオの合計メモリバジェットを250MB以下に抑えることに決めました。

オーディオはCPUにも負担がかかります。フレームごとに多くの計算が行われますが、今回はゲーム全体で複雑なSpatial Audioをセットアップしているため、計算負荷が特に第8世代プラットフォームにおいて非常に高くなります。私たちがWwise Profilerでオーディオスレッドの消費量をすばやくプロファイリングできるため、CPUのバジェット配分は私たちに任されました。オーディオスレッドの合計CPUは100%を最大ピーク値とし、平均CPUは第8世代で50%未満、第9世代とPCで30%未満とすることに決めました。

バジェット概要

 

メディアメモリ合計

CPUピーク合計

平均CPU

第8世代

250MB

100%

50%

第9世代

250MB

80%

30%

PC

250MB

80%

30%

表1: 全プラットフォームのオーディオの、メモリバジェット制限とCPUバジェット制限

オーディオ関連のメモリやCPUの問題

ゲームプレイ中にオーディオ問題が出現する方式はいくつかあります。ここでは最も代表的なものであるOOM(メモリ不足)、CPUスパイク、ボイススターベーション(音声枯渇)、ソーススターベーション(ソース枯渇)の4つを取り上げます。

OOM

OOMはシステムが新しいプロセスのためにこれ以上のメモリを割り当てられない時に発生します。プラットフォームで利用可能な物理的なメモリ上限を超過した場合、またはサウンドエンジンに割り当てられた最大メモリ(初期化パラメータAkMemSettings::uMemAllocationSizeLimitで設定)を超過した場合に発生します。OOM問題は物理的なメモリを超過した時にシステムクラッシュを引き起こしたり、サウンドエンジンのメモリを超過した時にサウンドエンジンの初期化の失敗、バンクのロード失敗、トランジションの欠落、サウンドの再生失敗などに繋がります。

OOMエラーには複数の原因が絡んでいることがあります。通常はオーディオを適切に管理している場合、オーディオが合計メモリ消費量のごく一部しか占めません。とは言え、ゲームのバジェット定義時に合意されたサウンドエンジンのメモリ制限を超えないよう、常に注意する必要があります。

CPUのスパイク

オーディオ処理のほぼすべてがゲームスレッドとは別にある、Wwiseオーディオエンジンだけが使用するオーディオスレッドで行われます。Wwise ProfilerのCPU測定値はすべてオーディオスレッド消費に関するものです。

1つのフレームですべてのサウンドをオーディオスレッドでレンダリングしきれない状況では、Wwise Profilerに表示される消費量が100%を超えます。さまざまな要因が考えられますが、主にフレームのオーディオ計算要件がオーディオスレッドの容量を超えた場合に起きます。『Scars Above』においてCPUスパイクが発生した時は、Spatial Audioの伝搬パスの計算負荷または多数のオーディオエミッターの同時登録・解除が主な原因でした。

過度に高くなく頻繁でない単体のCPUスパイクは、耳に聞こえるほどの問題とはなりません。このため安心してTotal CPU Peakの上限を100%に設定することができます(表1)。一方、あまりにもCPUスパイクが高い場合(数百または数千)、またスパイクが何回も連続する場合はオーディオエンジンのオーディオグリッチや音切れが発生するかもしれません。

ボイススターベーション

ボイススターベーションエラーはオーディオスレッドがフレーム内のすべてのサウンドをレンダリングできず、これが数フレーム続いた時に発生し、Total CPU consumptionは100%になります。このエラーはサウンドの欠落やグリッチに繋がります。私たちの開発では第8世代コンソール、特にベースのPS4プラットフォームにおいてボイススターベーション問題を経験しましたが、複雑なSpatial Audioジオメトリがひしめき合い各フレームで多数のエミッターの計算が必要なエリアで多発しました。Spatial Audioの複雑性を緩和して1度にロードされるエミッター数を制限することが、ボイススターベーションエラーの解消に大きく役立ちました。

ソーススターベーション

ソーススターベーションエラーはどちらかと言うとCPUではなくメモリに関係し、ディスクから直接ストリーミングするサウンドのデータをStreaming Managerに適宜提供できない場合に発生します。さまざまな要因がありますが、同時に再生するストリーミングファイル数が多すぎる場合や、ディスク(I/Oデバイス)が遅すぎてすべてのデータを同時に処理できない場合によく起きます。やはりサウンドの欠落が発生し、特にストリーミングサウンドが欠落しやすくなります。I/O設定を一部調整することによりソーススターベーションエラーを軽減することもできますが、最も効果的な解決策はディスクから同時にストリーミングするサウンド数を制限することです(ストリーミングサウンドの詳細は後述します)。

プロファイリング

プロファイリングは最適化プロセスにおいて必要不可欠となるステップです。ゲームの最適化をはじめる時はメモリ消費とCPU消費の現状を分析しつつ、これらのパラメータに影響するかもしれないボトルネックを特定することが極めて重要です。

テストする際は必ずテスト用のビルドまたはリリース用のビルドを使い、Unreal Editor内のテストや開発用のビルドを使ったテストは行わないでください。Unreal Editor上のテストはゲーム動作全体を把握するためには役立ちますが、適切なビルド内でテストを実行した方が信頼性のある正確なパフォーマンスデータを取得でき、オーディオの最適化に利用できます。

Wwise Profiler

Wwise Profilerはオーディオパフォーマンスを評価する時の1番のツールです。最初は直感的に使用できず戸惑うかもしれませんが、オーディオの現況を理解するための貴重な情報源であり、ゲームプレイ中に発生したパフォーマンス問題を特定するために役立ちます。

プロファイリング中に注目するのはCPUやメモリに関するいくつかのパラメータであり、WwiseオーサリングツールのProfilerレイアウトのPerformance Monitorセクションに表示されます。

img1

図 1: Wwise Profilerレイアウト

Performance Monitorでゲーム内のサウンドパフォーマンスに関するさまざまなパラメータの状態を、リアルタイムで視覚的にも数値的にも観察することができます。

これらの重要パラメータの監視を通し、その時々のゲームのサウンドパフォーマンスを明確に把握できます。Profilerにゲームの各フレームで発生したすべてのアクションの詳細なログが表示されます。特定のフレームを分析したい時は下図の通りモニター画面の黄色いカーソルを移動させ、任意の時間の状況を調べることができます:

img2

図 2: Performance Monitorのタイムライン

注:ショートカット [Alt + ,] と [Alt + .] でカーソルをフレーム単位で移動できます。

例えばCPU消費量のスパイクやグラフ上のボイス数の急増が見られた場合、カーソルをその特定の瞬間に移動して当該フレーム内でトリガーされたすべてのアクションおよび合計CPU消費量を確認することができます。

img3

図 3:スパイクしているCPU(例)

Wwise Profilerのパラメータ

私たちのゲームでは以下のパラメータが重要な指標となり、基本的にモニタリングしていました:

  • CPU - Total:オーディオ処理に関連する全体的なCPU使用を追跡
  • CPU - Plug-in Total:ゲームで使用するオーディオプラグインだけに起因するCPU消費量を測定
  • Number of Voices (Physical):同時再生中のアクティブなオーディオボイス合計数を提示
  • Number of Streams (Active):アクティブなオーディオストリーム数を表示
  • Spatial Audio - CPU:Spatial Audio処理だけに関連するCPU使用量を追跡
  • Spatial Audio - Emitters Processed:Spatial Audio計算の対象となるオーディオエミッター数を表示
  • Total Reserved Memory:オーディオアセット用に予約されているメモリ量を反映
  • Total Media (Memory):メディアアセットの合計メモリ消費量を測定

上記パラメータのうちTotal Media (Memory)とTotal Reserved Memoryはメモリ消費量を追跡し、それ以外はCPU消費量を監視します。

Unreal Editorを使ったプロファイリング

残念ながらUnreal EditorにはWwiseのオーディオデータが明確に表示されません。メモリやCPU消費に関するデータをWwiseがUnreal Engineに送信しないため、その点でプロファイリング能力に限界があります。Unreal InsightsLow-Level Memory TrackerはUnreal Editor側から見たCPUやメモリのプロファイリングに有効なツールですが、Wwiseのアセットやプロセスに特化した情報は提供されません。

そこで誤ったサウンド実装で起きるエラーを特定するためにOutput Logをそのまま活用します。Wwise Profilerから出たログメッセージとUnreal EditorのOutput Logを比較するのです。Output Logに表示されたWwiseエラーにはプレフィックス「LogAkAudio」が通常付くため、フィルターで簡単に問題を見つけることができます。QAチームが問題を報告する際にエラーを含むログファイルを添付してくれることで、私たちはさらに調査をすすめることができます。

QA部門に協力してもらう

もちろん問題報告の最も効率的な方法はQAチームがWwise Profilerを使いゲームをテストし、該当する.profファイルをバグレポートに添付することです。このアプローチはQAチームがWwise Profilerの使い方を知っていることが前提です。私たちにとってサウンドパフォーマンスのテストを担当する専門のQAスタッフがいたことはこの上なく貴重であり、みなさんも上司と相談してQA担当者の広い責任範囲にサウンドプロファイリングを追加してもらうことを検討するとよいと思います。

最適化

この章ではアセットパフォーマンスの向上を狙ったさまざまな最適化技術を考えます。サウンド設定の各種調整、アセット再生方法の変更、アセットを必要に応じて効率的にロード・アンロードしてリソースを節約するしくみなどを検討してゆきます。

アセット自体における工夫

私たちが作成したサウンドであるアセットのソースを検討する際、さまざまな最適化テクニックを駆使することができます。サウンドのフィデリティを維持しながらリソース消費を減らすために、状況に応じてさまざまなサウンド特性を調整してゆきます。アセットの品質を妥協せずにリソース消費を減らすことが目的であり、ちょうどよいバランスが必要です。

ディスクからストリーミングする

ディスクからファイルをストリーミングする設定はオーディオのメモリ負荷を軽減できる効果的で簡単な方法です。この方法が適しているのはループするアンビエンス、音楽、ダイアログなどのように、長くてタイミング重視でないファイル(映像と完璧に一致させる必要のないファイル)です。一方、ガンショット、衝撃音、足音などのように映像と密接に紐づいている一発限りのサウ ンドをストリーミングしてしまうと、メモリではなくディスクから読み込む時にサウンドに遅延が生じてしまう可能性があり不適切です。

ストリーミング設定したファイルのメディアはSoundBankに一切含まれません。これらのファイルを開いてディスクから直接読み込むのはWwise Stream Managerの責任です。対象プラットフォームのストリーミングの帯域幅によっては多数のサウンドをストリーミングして同時再生することが可能であり、メモリ負荷を大幅に軽減することができます。

サウンドをストリーミングに設定することは非常に簡単です。そのサウンドまたはサウンドの親のGeneral SettingsタブでStreamボックスにチェックを入れるだけで、ストリーミングオプションを有効にできます。

img4

図 4: SFXオブジェクトのStreamオプションを有効にする

ストリーミングするサウンドをもう少し映像と同期させたいと感じた場合は、Zero latencyボックスにチェックを入れます。ファイルの冒頭部分が少しだけメモリにロードされ、残りの部分をストリーミングしている最中にこの部分を遅延なく再生できます。事前にロードする部分の長さはPrefetch length時間で決まります。

  • Source Starvationエラー

前述の通りソーススターベーションエラーが発生するのはストリーミング帯域幅が限界に達し、 Managerがディスクからデータを読み込めなくなった時です。サウンドの欠落やその他の問題が引き起こされる恐れがあるため、サウンドをStreamに設定する際は起こりうる問題に配慮してください。

サウンドソースを短くする

長いオーディオファイルの話題が出たところで、場所を多く占めているものの最初から最後までコンテンツがほぼ同じオーディオファイルについてもう1度考えてみます。ルームトーンや安定した風のループなどのベースアンビエント音によくあることで、このようなサウンドを「ベッド」とも呼びます。何らかの理由でこのループ音をディスクからストリーミング設定にできない場合、サウンドの長さを短くすることによりメモリ使用量を大幅に減らすことができ、特にファイルサイズ縮小よりも品質を優先したコンバージョン設定の場合に有効です。

img5

図 5: 長さを短くしたベースアンビエント音

アンビエント音を短くする面白いアプローチとして、1つのサウンドの短いセグメントをいくつかまとめてRandom Containerに入れる方法があります。サウンドをContinuousかつInfiniteのLoopプレイモードでトリガーすることにより、コンテンツのバラエティを確保してメモリ消費を削減することができます。ただしセグメント間でクロスフェードを行うため生成されるボイス数が1つのオーディオファイルを再生する時の2倍となり、CPU負荷がやや増える可能性があることに留意してください。

img6

図 6: ループするRandom Containerにクロスフェードのトランジションを設定

Conversionを設定する

オーディオファイルにコンバージョン設定を適用することによりプラットフォームごとの最終的なファイル形式や、コンバージョンの品質が決まります。ファイル形式は元の.wavファイルを変換する時に使うコーデック、チャンネル数、サンプルレート、コーデックの品質設定などで決まります。

適切なコンバージョン設定を選択することによりゲームのメモリやCPU負荷をさらに最適化することができます。出荷時のビルドは一般的にロスレスのファイル形式とする必要はなく、Vorbisのような圧縮フォーマットであれば品質を大きく損なうことなく優れた圧縮率を提供してくれます。エンコードされたファイルは再生前にデコードする必要がありますが、デコードプラグインによってはCPU負荷がほかより重くなることがあるため注意が必要です。

: あなたにとって最大の懸念点がゲームのCPU使用の最適化である場合、一部のオーディオファイルにPCMを採用することも検討してみてください。圧縮フォーマットと異なりPCMファイルは圧縮されないため、再生時のデコードが不要です。ただし圧縮ファイルと比べてファイルサイズが非常に大きいため、PCMは短くて繰り返しの多い、ゲーム中に頻繁に使用するオーディオファイルで採用することをおすすめします。

多くのサウンドではコンバージョン設定が同じとなるため、ShareSetを使いConversion Settingsをプリセットとして整理することができます。

私たちのゲームでバランスがよかったのは、すべてのプラットフォームでShareSetプリセットのDefault Conversion Settingsの品質設定を4にしてVorbisを使った時でした。第8世代プラットフォームのハードウェアデコーディングに対応するWEM OpusやATRAC9などの選択肢もありましたが、私たちはVorbisのパフォーマンスに満足しており第8世代のハードウェアに統合するための時間をかけたくありませんでした(VorbisのWwise実装はAudiokinetic社が全プラットフォーム向けに高度に最適化済みです。)。さらにVorbisは第9世代プラットフォーム上のハードウェアデコーディングにも対応しており、さらに有利な選択肢となりました。

ShareSetプリセットのDefault Conversion Settingsは、以下の通りProject SettingsのSource Settingsタブで設定します:

img7

図 7: Project SettingsウィンドウのSource Settingsタブ

Wwiseがサポートしている利用可能なコーデックの比較表を以下にまとめましたため、採用するコーデックを検討する際に参考にしてください:

ファイル形式

圧縮率

CPU

メモリ

一般的な使用例

制限事項

PCM

1:1

非常に低い

高フィデリティを必要とする音。

なし。

ADPCM

4:1

アンビエントサウンド、SFX。

ループは64サンプルバウンダリに限定。

Vorbis

3-40:1

中~高

中~非常に低い

ダイアログ、音楽、アンビエント音、SFX。

ほかの形式と比較してメタデータのオーバーヘッドが多少大きいため、非常に小さいサウンド(数10ミリ秒より短いもの)では避けた方がよい。シークにシークテーブルが必要。

AAC

3-23:1

高(iOSのハードウェアアシストデコーダを使用する場合は、低)

中~低

バックグランドミュージック(インタラクティブでないもの)。

メタデータのオーバーヘッドが非常に大きい。セットアップ時間が長い。サンプルアキュレート再生に適さない。

WEM Opus

10-60:1

高(ハードウェアアクセラレーション非使用時)、非常に低い(ハードウェアアクセラレーション使用時)

中~非常に低い

ダイアログ、シンプルな音楽、アンビエント音、SFX。

プラットフォーム機能によって制限が異なる。

 
表2: 利用可能な全コーデックの比較表(Audiokinetic社ドキュメントより)

注: AACフォーマットはWwise 2022.1で非推奨となりました。詳細はマイグレーションノートをご覧ください。


  • Sample Rate Automatic Detection

Sample Rate Automatic DetectionはサウンドファイルのコンテンツをWwiseエンコーダで分析し、そのコンテンツに最適なサンプルレートを自動的に選択するというオプションです。例えば低周波数コンテンツを中心に構成されサウンドには低サンプルレートが選択され、オーディオフィデリティを大幅に損なうことなくファイルサイズが小さくなります。ただし『Scars Above』ではサンプルレートの自動検知機能を使いませんでした。というのも低周波メインの音が多く含まれておらず、すべてのサウンドを必ずプラットフォームのネイティブレートである48kHzと同じサンプルレートにしたかったためです。これは48kHzにアップサンプリングする際のエイリアシングアーチファ クトの防止に役立ちます。また16kHz以下のサンプルレートを使用したサウンドを変換する時にVorbisエンコーダーでは音質が悪くなる可能性があることも考慮していますが、これはこのコーデックが高いサンプルレートを念頭につくられているためです。

モノラルに変換する

ゲームのメモリ負荷、ディスクサイズ、CPU負荷を軽減するために効果的なのはサウンドのモノラル変換であり(適切な場合)、そのためにはConversion Setting ShareSetプリセットのチャンネル構成をMonoにします。

img8

図 8: Conversion Settings ShareSetプリセットエディタ

マルチチャンネルのファイルはシングルチャンネルのファイルに比べてメモリフットプリントが非常に大きくなります。例えば2チャンネルのステレオファイルが占めるスペースはモノラルファイル版の2倍です。

さらに大事なのはマルチチャンネルファイルのゲーム中の音源としての動きです。オーディオの1つのチャンネルはゲームの1つのVoiceに相当します。このルールはAudio Objectにも適用され、1つのチャンネルは1つのAudio Objectを意味します。マルチチャンネルサウンドにプラグインを挿入した場合、1つのチャンネルに対してプラグインのインスタンスが1つ作成されます。この影響は階層内のすべての子に、オーバーライドしない限り広がります。

多チャンネルのオーディオファイルがコストアップに繋がることが一目瞭然です。VoiceやAudio Objectの数が増えるほど、これらのサウンドに割り当てるプラグイン、コンバージョン、ミキシングなどに必要なCPUリソースが増えます。

: 私からのアドバイスはプラグインをActor-Mixer階層のアセットではなく、バスの処理に対して設定することです。こうすることによりバスに送られるサウンド(およびサウンドのボイス)の数に関係なく、1つのバスチャンネルに対してプラグインインスタンスが1つ作成されます。処理やプラグインの最適化については本書後半で詳しく説明します。

以下の条件の1つにでも該当するサウンドは、モノラルに変換することをご検討ください:

  • 内容がどのチャンネルにおいても同じ、または非常に似ているサウンド
  • 使用する減衰プリセットの減衰半径が非常に小さいサウンド
  • 使用する減衰プリセットのSpread値が非常に低いサウンド

モノラル変換に適していることが特に多いサウンドとしてボイスオーバー、レベル内の小さなエミッターが出すサウンド(小さいエネミーや小動物など)、特定のUIサウンド、フォーリーサウンド(大きさや距離によります)、レベル内の小さなインタラクションなどです。

ファイルのDefault Conversion Settingsプリセットをオーバーライドしてモノラルに変換するためには、サウンドまたはその親のConversionタブを開きOverride parentオプションをチェックし、適切なShareSetプリセットを選択します。

img9

図 9: Conversion Settingsプリセットをオーバーライドする

モノラル変換に設定されているすべてのオブジェクトの概要を確認するためには、以下のようにConversion SettingsのShareSetプリセットのリファレンスリストを確認します:

img10

図 10: Conversion Settings ShareSetのプリセットエディタのリファレンスボタン

このボタンをクリックするとウィンドウが開き、当該ShareSetプリセットに対するリファレンス一覧が表示されます:

img11

図 11: Vorbis monoプリセットに対するすべてのリファレンスを表示したReference Viewウィンドウ

Blend Containerを接着する

私たちがWwiseプロジェクトで取り入れた最適化手段の1つがBlend Containerの統合(接着)です。私たちはソースの出力、例えばBlend Containerの出力を収録して1つの新しいオーディオファイルにし、ソースをこの新しいファイルに置き換えるプロセスを「グルーイング」(接着する)という用語で表現するようになりました。この処理の成果としてすべてのBlend ContainerがSFXオブジェクトに置き換わりました。

ゲームの制作段階で多数のサウンドをインポートしましたが、Blend Containerは複数のサウンドレイヤーを1つのコンテナにまとめて整理する便利な方法を提供してくれました。おかげでイベント内の1つのPlayアクションをトリガーするだけでコンテナを簡単にテストすることができました。Wwiseにレイヤーを取り込んだことによりレイヤーのミックスをさらに改善することができ、これらのレイヤーが1つの親の中にあるおかげで簡単にトリガーしたり処理方法を変えたりできました。

しかしBlend Containerをレイヤーの整理のためだけに利用するのは宝の持ち腐れにも思えます。Blend Containerの主な目的はBlendトラックを作成することであり、コンテナ内の子オブジェクト間のトランジションを正確に定義し、さまざまなゲームパラメータを使いトランジション部分の詳細を制御することができます。

Blend Containerはトリガーした時に複数の子が同時に再生されるため、Blend Containerに起因するCPU負荷に注意する必要があります。例えばGunshot Blend ContainerにレイヤーのImpact、Tail、Foley、Mechanicsに対応するそれぞれの子コンテナがある場合、このBlend Containerをトリガーすると4つのサウンドが同時に再生されます。

img12

図 12: Blend Containerの中にある4つの子(例)

Gunshotイベントの各レイヤーがステレオ再生用に2チャンネルで設定されていると仮定すると、Gunshotイベントをトリガーした時に8つのフィジカルボイスが使われます。複数のGunshotイベントを矢継ぎばやにトリガーするとボイス数がすぐに増大します。さらにBlend Containerにプラグインが挿入されている場合は、Gunshotイベント1つに対し挿入したプラグインごとに8つのインスタンスが生成されます。

このBlend Containerを接着するとボイス数は8から2に減り、メモリやCPUの負荷が大幅に削減されます。

:Blend Containerは一般的にリソースを消費しますが、それでも1つのBlend Containerから複数のSFXオブジェクトをトリガーした方が、SFXオブジェクトごとに1つのPlayアクションを使用し、複数のSFXオブジェクトを1つのイベント内で別々にトリガーするよりも効率的です(ありがたくもAndyGBassがこれをテストしてくれました)。

Blend Containerの子コンテナをRandom Containerとし、各Gunshotイベントがレイヤーの違うミックスを生成するように設計した場合、このBlendコンテナを必要な回数だけ接着することにより、フィデリティが維持され最適化された状態のGunshotサウンドの必要なバリエーション数を実現することができます。

Blend Containerを接着する最善の方法はWwise Authoring Toolの出力をそのまま収録することです。Master Audio BusにWwise Recorderプラグインを挿入して適切に設定することにより、Blend Containerをトリガーするたびにディスクの個別.wavファイルに録音できます。

img13

図 13: Master Audio BusにWwise Recorderプラグインを挿入する

img14

図 14: Wwise RecorderのEffect Editorウィンドウ

Blend Containerの再生が終わるとその.wavファイルが作成されます。次の録音を開始する前に録音し終わったファイルを別の場所に移動するか、適切な名前を付けることが大切です。こうすることで次に再生した時に前の録音を上書きしてしまうことを防ぎます。

充分な数のバリエーションが生成できたらRandom Containerを新しく作成し、すべての録音ファイルをここにインポートします。イベントにあるBlend Containerは新しいRandom Containerに換えます。録音を終えた後にWwise RecorderプラグインをMaster Audio Busから削除するか、バイパスさせることを忘れないでください。

img15

図 15: 生成したWwise RecorderのバリエーションがRandom Containerに入っている

使わなくなったBlend Containerは将来的にミックスを修正して一連のプロセスを繰り返したいこともあるため、プロジェクトに残しておくと便利だと思います。「_Glued」というフォルダに入れて除外することを提案します。新しいバリエーションを生成したいと思った時は簡単に再度組み込むことができ、Wwise Recorderプラグインをもう1度挿入して必要な変更を加えて新たに録音します。

img16

図 16: 除外されたBlend Container

エフェクトの処理の最適化

Wwiseのプラグインエフェクトを使用してサウンドを処理する時に多くのCPUを消費し、メモリリソースも多く消費することがあります。一方でWwise環境内で処理を最適化して、パフォーマンスを向上させる方法がいくつかあります。以下のセクションで最適化方法について詳しく考えてゆきます。

  • エフェクトをレンダリングする

前述の通りWwiseのオーディオオブジェクトにプラグインエフェクトを追加すると、オブジェクトとその子の各チャンネルごとにインスタンスが1つ作成されます。これらのプラグインが実行する処理はゲームプレイ中にリアルタイムで計算されます。ゲームパラメータを適用して動的に変化させる必要のないエフェクト処理がある場合は、レンダリングするという選択肢もあります。

Renderオプションを有効にすることによりSoundBankにサウンドをパッケージングする前に、エフェクトをサウンドにベイクすることができます。これで実行時の処理負荷を節約できます。ただし前述の通りレンダリングしたエフェクトはゲームパラメータ(RTPC)で変化させたりバイパスしたりすることはできません。

img17

図 17: Container Property EditorのEffectsタブでRenderオプションを選択する

レンダリング可能なすべての挿入エフェクトのリストを取得する簡単な方法があります。Integrity Reportを生成すると(このウィンドウを開くデフォルトのショートカットは[Shift + G])、レンダリングを使用して最適化することのできるエフェクトのインスタンスすべてがプラットフォームごとに一覧で、以下のメッセージと共に表示されます:

「The effect could be rendered to save CPU, since no parameter will change in game.」(このエフェクトはゲーム中にパラメータが変化しないため、レンダリングすることによりCPUを節約できます。)

img18

図 18: Integrity Reportウィンドウ

このウィンドウでエフェクトを含むオブジェクトのProperty Editorを開き、Renderオプションを有効にすることができます。これでエフェクトがIntegrity Reportに(レンダリング問題に関しては)表示されなくなります。

  • バスにエフェクトを挿入する

一般的にレンダリング不可のエフェクトを使用する場合のベストプラクティスはActor-MixerやInteractive Music階層においてオブジェクトではなくバスに挿入することです。バスのチャンネル数が固定されているためで、バスにエフェクトを挿入した場合はバスのチャンネル数分だけエフェクトがインスタンス化されます。一方、Actor-Mixerオブジェクトにエフェクトを挿入するとオブジェクトの子オブジェクトの各チャンネルに対してエフェクトがインスタンス化されます。どちらの方法もサウンドは最終的に同じく聞こえますが、前者の方がエフェクトのインスタンス数が少なく済みます。

例外はバスに送られるオブジェクトのチャンネル数よりもバスのチャンネル数の方が多い場合です。SFXオブジェクトよりもバスの方がエフェクトのインスタンスが多くなるため、SFXオブジェクトにエフェクトを挿入する方が望ましいです。この場合も例外があり、リバーブは1チャンネルあたりのパフォーマンスコストが無視できるほど少ないため、AUXバスに適用することが最善であることが多いです。

  • 処理をGeneral SettingsからEffectに移す

リソースをさらに節約するためにバスやオブジェクトの特定パラメータを実行時に処理能力を消費する「エフェクト」としてとらえることができます。Bus Volume、Voice Volume、Pitch、Low-pass filter、High-pass filterなどのGeneral Settingsタブにあるパラメータはゲームプレイ中にCPUリソースを消費しますが、ゲームパラメータによって変化しないのであれば専用エフェクトプラグインに置き換えてレンダリングすることが可能です。

img19

図 19: General Settingsタブのパラメータの処理

Low-pass filterとHigh-pass filterはどちらもWwise Parametric EQのインスタンス1つで置き換えることができ、PitchはWwise Pitch ShifterやWwise Time Stretchプラグインのインスタンスで置き換えることができます。VolumeやMake-Up GainはWwise Gainプラグインエフェクトで置き換えることができます。

InsertedEffects

GIF 1: General Settingsパラメータの代わりにWwiseプラグインを挿入する

こちらのリンクはAudiokineticドキュメントのLPF 値とHPF値のCutoff Frequencies対照表です:

Wwise LPF値、HPF値のカットオフ周波数

: プロジェクト内でこのような変更をたくさん行った場合、すべてのパラメータを調べてプラグインに置き換えるには時間がかかるかもしれないことを考慮してください。

アセットの再生方法を最適化する

本セクションではサウンドアセットの再生方法、停止方法、ボイスやAkComponentの考え方、サウンドの再生とトリガーの最適化を図る手順などについて説明します。

ボイス

ゲームで再生する個々のサウンドはそれぞれが1つのボイスを占有します。イベントに関連付けられているオーディオソースはランタイムにイベントがトリガーされると「ボイス」となります。これらのボイスは計算で存在が実現し、さまざまな動作の適用が可能となります。計算の内容は主にボイスに対して行う処理の量に依存します。計算を最小限に減らして必要な時だけに実行することによりCPUリソースを大幅に節約できます。

ゲームで現在再生中のすべてのボイスの一覧を表示するためには、以下の通りWwise Profilerでゲームを接続してVoicesタブを開きます:

img20

図 20: Advanced ProfilerのVoicesタブ


 

  • Virtual Voice

アクティブ状態にあり音を出すボイスのことをPhysical Voiceと呼びます。Physical Voiceはフレームごとに以下の計算を行います:

  1. オーディオファイルのデコード
  2. サウンドのリサンプリング(および使用する場合はPitch Shiftの適用)
  3. エフェクトの処理、フィルターの処理
  4. Volume計算

Physical Voiceに関連付いているアクタが遠くにおりPhysical Voiceが聞こえない場合などは、これをVirtual Voiceに設定することができます。Virtual VoiceはVolume以外のすべての計算が省略され、Volume計算だけはサウンドが再び聞こえるようになった時にその時の音量が分かるように続けられます。Physical VoiceをVirtualにすることによりCPUリソースを大幅に節約できるため、確実に音が聞こえないサウンドはアクティブにし続けないことがゲーム最適化の鍵となります。

Virtual Voice Behaviorはサウンドアセットごとに設定し、以下の通りProperty EditorのAdvanced Settingsタブで定義します。

img21

図 21: Property EditorのAdvanced Settingsタブ

Virtual voice behaviorオプションでボイスがVolume Thresholdを下回るボリュームとなった時のボイスの動作を設定します。

以下の選択肢があります:

  1. Continue to play:ボイスをアクティブ状態で維持し、通常の再生を継続します。
  2. Kill voice:ボイスを完全に停止し再生を止めます
  3. Send to virtual voice:ボイスをVirtual Voiceにし、ボリューム計算以外のほぼすべての計算を省略します
  4. Kill if finite else virtual:終わりのあるサウンド(ループしないサウンド)はVirtual状態に送られた時点で完全に停止します。ループするサウンドはVirtual Voiceとなります。

私たちは一般的にアセットのデフォルト値をKill if finite else virtualに設定することが多かったです。ある閾値よりもボリュームが低くなった時に何らかの対応が必要なケースでは、ほとんどの場合にこれを適用しました。無限でないサウンドは離れた時に聞こえなくなるため「キル」(消去)し、ループ音はバーチャル状態にします。

すべてのバーチャルボイスに関して、私たちはPhysical Voiceに戻る時の設定をPlay from elapsed timeとしました。Wwiseがボリューム計算を続け、さらにボイスがバーチャルになった時間の長さも追う設定です。ボイスが再びアクティブになった時は経過したタイミングに合わせて再生が復活し、ゲーム内の時間がシームレスに経過したように認識されます。

  • Volume ThresholdとMax Voice Instances

WwiseがPhysical VoiceをVirtual状態に送信するタイミングを判断するために、Volume Thresholdを指定する必要があります。Volume ThresholdはサウンドをVirtual Voiceに安心して変換できる最小音量レベルを表し、これよりボリュームが小さいと変換されます。

以下の通り閾値を各プラットフォームのProject SettingsのGeneralタブで定義します:

img22

図 22: Project SettingsウィンドウのGeneralタブ

私たちはVirtual Voiceの動作としてVolume Thresholdに「-40」が適していると判断しました。

Max Voice Instancesは許容するPhysical Voiceの最大数をプラットフォームごとに定義するパラメータです。Physical Voice数がこの制限を超えた場合、プライオリティ設定に基づき追加のボイスが即座にバーチャルに送られます。私たちはゲーム内のボイス数を制御しやすくするため、このパラメータに比較的余裕のある値を設定しました。

ボイスのプロファイリング中に特に観察すべきパラメータは、Number of Voices (Physical) とNumber of Voices (Total)の2つです。Number of Voices (Physical)はゲームで実際に同時再生中のPhysical Voice数を表し、Number of Voices (Total)は登録されているPhysical VoiceとVirtual Voiceの合計数を表します。

img23

図 23: Wwise ProfilerのNumber of Voicesパラメータ

私たちはプラットフォームごとの一般的なルールとして、下表の通りPhysical Voice数とTotal Voice数の上限を設定しました:

 

 

Physical Voices (Max)

Total Voices (Max)

第8世代

30

500

第9世代

40

600

PC

40

600

表3: プラットフォーム別のVoice上限

Virtual VoiceはPhysical Voiceに比べて計算要件がはるかに少なくなりますが、過度にゲームでVirtual Voiceを使用することは避けた方がよいです。Virtual Voiceが消費するリソースは少ないのですが、やはりVolumeやElapsed timeを計算する必要があります。さらに重要なことにVirtual VoiceはEmitterとして扱われるため、Spatial Audioに送信した時はDiffractionパスの計算にかかわってきます。

  • Playback Limit

サウンドがトリガーされる回数が予測不可能な状況や、同じサウンドの過剰なインスタンス数が同時に再生される状況においては、WwiseでサウンドにPlayback Limitを適用することができます。

Playback LimitオプションはProperty EditorのAdvanced Settingsタブにあり、Limit sound instances toの数を設定できます。制限の範囲は自由に選ぶことができます。Game ObjectまたはGlobalを選択し、サウンドをアクター単位で制限するのかグローバルにゲーム全体で制限するのかを選択できます。

img24

図 24: Random Containerのサウンドインスタンスを制限する

私たちはサウンドインスタンス数の制限を主にEnemyサウンドに利用し、特に激しい戦闘シナリオでは同じタイプの敵が複数スポーンされて似たようなサウンドが大量にトリガーされるため、頻繁に利用しました。再生制限を適用することによりゲームのパフォーマンスが最適化されただけでなく、過剰な音の重複が減り全体のミックスが改善されました。

AkComponent

AkComponentはUnreal EngineでアクティブなWwiseイベントを表すもので、USCeneComponentから派生します。

サウンドデザイナーの観点ではAkComponentをゲーム内のサウンドソースやスピーカーとして捉えることができます。ゲーム内に存在するこれらスピーカーは音を発したり、アクタにアタッチされたり、場合によってはアクタと共に動いたりします。

ゲームのAkComponentの効率的な管理はオーディオパフォーマンスの最適化に非常に重要な段取りです。アクティブなAkComponentは毎フレーム実行するさまざまな計算があり、登録したAkComponentの数が極端に多い場合はCPU負荷が非常に大きくなります。

サウンドのポストを最適化するために、イベントで毎回新しいコンポーネントをスポーンするのではなく、可能な限り既存のAkComponentsを利用します。ゲーム内のすべてのAkComponentを把握して必要に応じて既存のAkComponentにサウンドをポストすることにより、全体のCPU負荷を減らすことができます。

AkComponentの理想的な候補となるアクタは、常に存在しゲーム内を移動するPlayer Character、NPC、Enemy、Projectileなどです。AkAmbientSoundなど一部のアクタにはデフォルトですでにAkComponentがアタッチされているかもしれません。

  • AkComponentの代わりにPost Event at Locationを使用する

WwiseのPost Event at Location関数でAkComponentを使わずにイベントをポストすることができます。この関数でイベントのデータをインプットとしてトランスフォームし、割り当てられたID番号で仮のWwise Game Objectを登録し、トランスフォームの位置および向きにおいてイベントをポストします。

AkComponentを作成してそれを通してイベントをポストする場合と比べ、Post Event at Location関数を使用する方が負荷が非常に軽くなります。一般的にWwise Game Objectの持つデータはごくわずかですが、AkComponentに対しポストしたサウンドと動きが似ています。主な違いはWwise Game Objectに対してポストしたイベントはポスト後にトランスフォームを変更できないこと、別のアクタにアタッチできないこと、Wwise Switch、State、Callbackを活用できないこと、スコープがGame Objectであるゲームパラメータによって変更できないことなどです。

このような制約がありますが、その場所からイベントをポストできることはさまざまなサウンドにとって大変便利です。位置を有し、発した後に忘れてもよいようなワンショットやループをポストする時によく使われます。位置を持たず特定のアクタや場所に紐づいていない2Dサウンドもこの関数でポストすることができ、これらのサウンドはトランスフォームデータを無視します。

『Scars Above』ではPost Event at LocationをUIサウンド、Musicトラック、ゲームワールド内のインタラクション、Impactイベント(敵に対する特定Hit音など)、ループ音を発する静的なアクタなどに頻繁に使用しました。スコープがグローバルである機能的なイベント、例えばグローバルステートを設定するイベント、グローバルミックスを変更するイベント、バスの一時停止や一時停止解除を担うイベントなどもPost Event at Location関数に向いています。

Post Event at Location関数を使用する時は以下の点に注意する必要があります:

1. サウンドに適した位置を獲得するために、その場所を提供するコンポーネントやアクターを検索します。Get World Location関数やGet Actor Location関数などを使用して位置データを取得できます。 

img25

図 25: アクタのロケーションを使用して位置を有するサウンドをポストする

2. BeginPlayでサウンドをポストする場合、イベントをポストする前にSpatial Audioアクタがロードされていることを確認してください。こうすることによりサウンドをSpatial Audioネットワーク内に正しく配置することができます。AkComponentsと異なりGame Objectにポストしたイベントは、Spatial Audioを考慮してSpatial Audioアクタがロードされた時に自分の位置を更新するようなことはしません。

3. ループサウンドをAkComponentではなくGame Objectにポストする場合、このループサウンドの停止も対応する必要があります。AkComponentを破棄する場合(親アクタがアンロードされた時など)、それを経由するサウンドはすべて停止されます。ところがPost Event at Location関数で作成された一時的なGame Objectは、このゲームロジックに紐づいていません。サウンドを停止するためには該当するGame Objectを登録解除するか、手動でイベントを停止する必要があります。Game Objectイベントを停止する最も簡単な方法はExecute Action on Playing ID関数を呼び出し、Game Object IDをインプットすることです。

img26

図 26: 位置のないUIサウンドをポストし、停止する

アニメーション通知

アニメーションでイベントを直接ポストする手法は、ゲームでサウンドをトリガーするためによく使いました。ほとんどのサウンドは同じAkComponentにポストされましたが、場合によっては多数の同時通知が複数のサウンドの同時再生をトリガーすることもあり、結果的に大量のPhysical Voiceが生成されました。

サウンドを小分けにしたり複数レイヤーに分割したりすることにより高度のフィデリティを得ることができ、動きとの同期を制御しやすくなります。しかしこのようなサウンドを連続してポストすることにより過剰な数のイベントがトリガーされてしまい、Wwiseの呼び出しが複数回発生し、似たようなアニメーションがいくつも同時に再生される場合などはこれが特に顕著です。

ブレンドスペースでこの問題がさらに悪化する可能性があり、多数の通知のあるアニメーションを2つ合わせることにより、さらに多くのイベントがトリガーされかねません。

img27

図 27: 多くのサウンド通知のあるアニメーションモンタージュ

このような時はアニメーション1つあたりの数多くの通知が本当に必要であるかを検討し、少ない数のオーディオイベントで同等の品質またはそれに近いレベルを達成できるのかを考えます。アニメーションの同時発生サウンドを減らしてアクティブボイス数を削減することにより、CPUの負担が軽減されます。

最適化のためにアセットを整理する

アセットの整理はプロジェクトのパフォーマンスとワークフロー全体を向上させるために極めて重要です。アセットを適切なSoundBankやサブレベルに入れることにより、アセットのロードやアンロードのタイミングを制御できるようになります。リソースの使用をより効率的に管理でき、常に必要なアセットだけをメモリにロードできます。

プロジェクトを整った状態に保ち余分なアセットを排除してゆくことも重要です。不要なアセットを削除することにより無駄を省くことができるだけでなく、メモリやCPUの消費を最小限に抑えられます。

SoundBank

SoundBankにサウンドを分けて入れることはゲームのオーディオメモリーを管理するためのすばらしい方法です。多くの場合はすべてのサウンドを常にロードする必要がなく、サウンドをグループに分けて必要な時だけロードすることはメモリ負荷を減らす最善の方策の1つであると同時に、サウンドがいつ、どのようにロードされて再生されているかを明確に把握するためにも有効です。

さまざまな種類のサウンドを適切にロードすることができるよう、私たちは以下のカテゴリに分類しました:

  1. Main:ゲーム全体を通して使用するイベントを含みます。
  2. Player:すべてのPlayerイベントを含みます。
  3. Enemies:敵のタイプごとに1つのバンクを設定し、その敵用のすべてのイベントを含みます。
  4. CharactersCommon:共通Characterイベントをすべて入れました。
  5. Levels:LevelまたはSublevelごとに1つのバンクを設定し、そのLevelまたはSublevelのイベントをすべて含みます。
  6. LevelsCommon:共通Levelイベントをすべて含みます。
  7. Music:すべてのMusicイベントを含みます。
  8. Voice:すべてのダイアログイベントを含みます。
  9. EmotionalResponses:すべてのPlayer Emotional Responsesイベントを含みます。
  10. Cinematic:すべてのCinematicイベントを含みます。
  11. HapticFeedback:すべてのHaptic Feedbackイベントを含みます。

 

  • 自動ロード設定のSoundBank

自動ロードに設定されたバンクは、そのバンクに属するイベントがゲーム内で参照された時に自動的にロードされます。Main、Player、MusicなどのSoundBankは常に参照されるため、ゲーム中は常にメモリに入っています。EnemyのSoundBankは各エネミータイプがいつ、どこで参照されたのかを追跡し、各エネミータイプ専用のSoundBankを自動ロードシステムで動的にロード・アンロードしました。

私たちのプロジェクトのSoundBankはLevels SoundBank以外をすべて自動ロードに設定しました。Levelsはアクタやイベントがいつ、どこでロードされるのかを厳密にコントロールしたいと考えました。そこでLevels SoundBankのロードをWorld Compositionのレベルストリーミングシステムと結びつけ、このシステムで必要に応じて各レベルをロード・アンロードできました(Levelストリーミングについてはこのセクションの後半で説明します)。

  • Common SoundBank

私たちはさまざまなCharacterやLevelに共通するイベントを保存するために複数のCommon SoundBankを作成し、自動ロードに設定しました。Common SoundBankの利点は共有されるメディアが1度だけロードされ、複数のSoundBankで重複しないため、メモリ負荷が軽減されることです。一方、Common SoundBankは特定のCharacterやLevelに縛られないため、自動ロードやストリーミングに比べて長い間ロードされたままとなります。

共有されるイベントを使う時にCommon SoundBankの代わりに、異なるSoundBankに重複するメディアを入れることができます。この場合、同じイベントが複数のバンクにロードされている期間が発生する可能性がありますが、バンクを適切に整理することによりロードされている時間をCommon SoundBankよりもはるかに短い時間とすることができます。

Common SoundBankは複数の異なるキャラクタやレベルが同時に参照するイベントを格納するために使用すると最適です。例えばいくつかの共通するサウンドを使う複数の敵グループに、同じ場所で遭遇した時などです。もう1つの方式は再利用するイベントが複数のバンクに同時にロードされないことが分かっている場合に適しており、Level SoundBankなどでよくあるケースです。プロジェクトのニーズや特定の状況に合わせて共通SoundBankや特定SoundBankをいつ、どのようにロードするのかはサウンドデザイナーが決めます。

  • Event-Based Packaging、Auto-Defined SoundBanks

『Scars Above』ではすでにレガシーシステムとなったSoundBankシステムを使い開発しましたが、今後はすべてのプロジェクトにおいて改良されたAuto-Defined SoundBanksに切り替える予定です。Wwiseバージョン2022.1で導入されたAuto-Defined SoundBanksはEvent-Based Packagingのコンセプトを発展させたものであり、レガシーシステムを置き換えることになっています。Auto-Defined SoundBanksでは根本的な考え方として各イベントを自己完結したミニSoundBankととらえ、そのイベントが参照された時だけロードし、再生が終わるとアンロードします。

『Scars Above』の制作中にEvent-Based Packagingへ切り替えようと試みましたが、この機能を実装する過程で多くの問題に遭遇しました。Event-Based Packaging機能は技術的に非常に期待できるものですが実際にはまだ充分に洗練されていなかったため、『Scars Above』ではレガシーのSoundBanksシステムにとどまることにしました。

Level Streaming

Level Streamingを使用することにより実際にレベルに必要なアクタだけをメモリにロードすることができます。同時にロードされるアクタ数が減り、貴重なオーディオスレッド時間を使用するEmitterやGame Objectの数を減らすことができます。

Listenerが動くたびにレベル内のSpatial Audioに送信される全エミッターに関し、回折パスの計算を行う必要があります。毎フレームで行われるため何百ものエミッターがロードされている場合は、Spatial AudioのCPUスパイクが頻発します(図3参照)。

現在アクティブなエミッターの数はいつでもWwise Profilerで観察できます。私の分析ではエミッター数(ProfilerのSpatial Audio - Emitters Processedの値)を第8世代プラットフォームでは150以下、第9世代やPCプラットフォームでは200以下に抑えることが推奨されます。

エミッター数を減らす最善の方法はマップをさらに細かいサブレベルに分割し、必要な時だけそれをロードすることです。アクタを特定のSoundサブレベルにグループ分けすることにより、レベルに属するアクタをロードするエリアについてコントロールできます。Playerがある指定エリアに入った時にそのサブレベルに属するアクタをロードし、Playerがこのエリアを出た時にアクタをアンロードします。私たちが『Scars Above』でレベルのLevel Boundsに基づきロード・アンロードするために使用したシステムは、World Compositionと呼ばれるものです。

  • World Composition

World Compositionは大きなマップを管理するためのシステムであり、マップをさらに小さいサブレベルに分割してStream Layerとして整理します。各サブレベルに独自のLevel Boundsボックスがあり、Stream Layerに応じてUnrealの単位で定めたストリーミング距離値が割り当てられます。サブレベルがロードされる範囲がLevel Boundsボックスを中心にこの距離で決まります。例えばLevel Boundsボックスのストリーミング距離が1500ユニットである場合、PlayerがBoundsから1500ユニット以内(またはボックス内)にいる時にこのレベルがロードされます。

このシステムでマップが実質的に管理しやすい広さに分割され、そのエリアに必要なアクタだけがロードされます。Virtual Voiceシステムにサウンドを送信することによりCPU処理の一部を解放できますが、アクタ自体はロードされたままであり、そのゲームオブジェクトはVolumeやSpatial Audioの計算のために登録されています。メモリにあるオブジェクトの数が多すぎるとCPUスパイクが発生する恐れもあります。

  • レベルをどう分割したか

レベルをより小さいサブレベルに分割する際、マップ内のアクタが属する特定エリアやゾーンごとにアクタをまとめる方法が最も自然です。ゾーンとはあるレベル内で音響的にも視覚的にも特徴があり、アセットの整理や命名において個別のエリアとして扱うことのできる部分を指します。

各ゾーンに専用のSound Sublevelがあります。ゾーンの複雑性や大きさに応じて、ゾーンをさらに細かいレベルに分けた方が効率的な場合もあります。

img28

図 28: 『Scars Above』の沼地生物群系とSound Sublevelsのバウンダリ


 

  • Level Bounds

World Compositionを有効にした後にレベルストリーミングに使う各サブレベルのLevel Boundsを定義します。レベルのLevel Boundsアクタのインスタンスが、World Outlinerに表示されます。これらのアクタは透明なボックスとして表示され(ボックスという基本形状は変更できません)、各サブレベルがカバーするゾーンが決まります。バウンダリ設定の際にそのサブレベルに属するすべてのサウンドやアクタが、Level Boundsボックスに包含されているのかを検討することが重要です。

バウンダリを決める最善の方法はレベル内のすべてのアクタを選択し、それらがカバーするエリアを観察することです。この時に一般的にAkAmbientSoundアクタのAttenuation半径を見ますが、これがレベル内で最も広い範囲をカバーする傾向にあるためです。

img29

図 29: サブレベルのすべてのオーディオアクタを選択

次に選択したアクタがLevel Boundsに包含されるように、アクタの減衰半径の球体を考慮しながら調整します。

img30

図 30: サブレベルのすべてのオーディオアクタを包含するLevel Boundsボックス

 

  • Stream Layer

サブレベルが適切にストリーミングされるように、サブレベルをStream Layerにアサインします。Levelsウィンドウでサブレベルを選択して適切なStream Layerを選択します。

img31

図 31: Sound Sublevelを適切なStream Layerにアサインする

World CompositionウィンドウでStream Layerを選択すると、そのレイヤーにアサインされたすべてのレベルのバウンダリを平面図として見ることができます。

img32

図 32: Stream Layer「Sound 10m」にアサインされたサブレベルが表示されたWorld Compositionウィンドウ


 

  • AntiLevelStreaming

もうお分かりかもしれませんが、ロードするアクタの領域を単純なボックス形状で区分けすることは、サブレベルのロード・アンロードの最適化として最善ではありませんでした。非常に単純なボックス形状に制約されたため、私たちはサブレベルのバウンダリを正確に定義することができませんでした。Level Boundsアクタのボックス形状が原因でマップ内の特定の領域でレベルを除外することができず、複数のレベルが重複して同時にロードされてしまう領域が発生しました。

たとえ正確にLevel Boundsを配置できたとしても、マップ内で多くのアクタが密集する領域では同時にロードされるアクタが多すぎる状況が発生する可能性もあります。

このような問題に対処するためにプログラミングチームがWorld Compositionを補完するかたちで、AntiLevelStreamingというカスタムソリューションを実装してくれました。

私たちはマップにAntiLevelStreamingアクタインスタンスを追加してLevel Boundsと非常によく似たバウンディングボックスをつくり、このボックスを割り当てたサブレベルに入った時は逆にレベルストリーミングから除外する(つまりアンロードする)ようにしました。このシステムをWorld Compositionと並行使用することにより、メモリにロードされるサブレベルが多すぎた領域のレベルストリーミング問題を解決することができました。

img33

図 33: AntiLevelStreamingActorと追加したSoundサブレベル


 

  • Spatial Audioのストリーミング

残念ながらSpatial Audioアクタをストリーミングするという選択肢はありませんでした。Spatial Audioのボリュームのロード・アンロード、特にAkAcousticPortalsのロード・アンロードが多くの再計算を必要とすることが私たちのテストで判明し、これがCPUに大きな負担をかけました。

テストでは実行時にSpatial AudioのボリュームやAkAcousticPortalを動的にロード・アンロードすることが、CPUパフォーマンスに大きな影響を与えることが分かりました。Spatial Audioアクタがストリームインまたはストリームアウトするたびに、すべてのアクティブなアクタのために回折パスを再計算する必要がありました。このため、ほかのオーディオアセットと合わせてSpatial Audioアクタをストリーミングすることは現実的ではありませんでした。

問題に対処するために私たちはすべてのSpatial Audioアクタを含む、永続的なSpatial Audioサブレベルを作成することにしました。レベルのストリーミングにかかわらずこのサブレベルは常にロードしたままとしました。

ところが過剰なSpatial Audioジオメトリのため、制作の最終段階でかえってほかの問題が発生しました(ある時点では1つの永続的サブレベルに数千ものSpatial Audioアクタが存在していました)。このサブレベルはさらに2つの小さいサブレベルに分割することとし、ゲームの離れたゾーン間のトランジション時のみにロードすることにしました。このトランジションはローディング画面で処理されるため、プレイヤーエクスペリエンスに影響することなく、必要なSpatial Audioサブレベルを適切にロード・アンロードする時間を確保できました。

使用していないアセットを除く

長期プロジェクトでは機能が不要になったり大幅に変更されたりしながら、何度もイテレーションを行いチームとして納得のゆくソリューションにたどり着きます。このような機能のためにつくられたサウンドは使えなくなり、新しいサウンドを制作する必要が出てくるかもしれません。

1つのプロジェクトに多くの人が携わっている場合、サウンドの変更点やバージョンをすべて把握することは必ずしも現実的でなく、サウンドデザイナーが知らないうちに一部のサウンドが不要になることがあります。メモリリソースを最適化するために、使わないイベントを特定して取り除くことが大切です。

AkEvent .uasset が使用されていないかどうかを判断する最も簡単な方法は、そのリファレンスを確認することです。イベントのリファレンスが自分のSoundBankだけである場合、実装が間違っているか不要となったイベントである可能性が高いです。その時は不使用イベントをSoundBankから、Unreal EditorとWwiseの両方にあるUnusedフォルダに移動することを推奨します。不使用イベントでゲームのメモリを消費することを防ぎ、万が一必要になった時にアクセスできる状態にします。

img34

図 34: リファレンスが自分のSoundBankだけのAkEvent

SpatialAudio

Spatial Audioは大きなトピックであるため本書では掘り下げません。ただしSpatial Audioについては前回の記事で詳しく取り上げ、Spatial Audio最適化のベストプラクティスにも言及しました。

前の記事の最適化のセクションへのリンク:

>Spatial Audioの最適化

完全に最適化されたSpatial Audioを達成するために必要な詳細がこのセクションにあります。

Spatial Audioの最適化のための主な手順は以下の通りです:

  • 三角形の数に注意しながらできるだけシンプルなジオメトリとする
  • ボリュームの数、特にAkAcousticPortalの数を最小限に抑える
  • すべての凸形状のボリュームのSurface Reflectorを無効にする
  • 各フレームで処理するエミッター総数に注意する
  • Spatial Audioの初期化設定をプラットフォームごとに調整する

Spatial Audioについてさらに知りたい方はこのドキュメントに目を通し、Audiokinetic公式ドキュメントを参照することをおすすめします。

微調整(ほかのソリューションを使い果たした時)

これまでの対策で問題が解決しなかった場合、ゲームの望ましい状態を達成するために検討できるその他の手順をいくつか紹介します。

Stopイベントの代わりにExecute Action on Event/IDを使用する

サウンドの種類によっては専用のStopイベントを作成して実装する必要があります。

一方でブループリントの既存関数Execute Action on EventまたはExecute Action on Playing IDを利用する方法もあります。これらの関数はStopやPauseなどのアクションを提供し、サウンドの停止や一時停止を担うイベントの代わりに使用することができます。専用イベントの代わりにこのようなUE関数を使うことにより、当該イベントをSoundBankから完全に除外できます。限定的な効果とは言え、プロジェクトのメモリ負荷を軽減します。ゲームのさらなる最適化に貢献します。

可能な場合はActor-Mixerをフォルダに置き換える

Wwiseプロジェクトでサウンドを整理するためだけに使用するActor-Mixerがあり、Actor-Mixerに対して実際の変更や修正が行われない場合は、単なるフォルダに置き換えて同じ結果を得ることができます。計算する階層が1つ減ることにより、これらのサウンドのために必要な計算が減ります。

このアプローチはStopイベントの置き換えと同様、ほかの最適化テクニックと比較してさほど大きな改善はないかもしれませんが、少しの最適化だけでも役に立つ場面もあります。

Random Container内のバリエーションを減らす

あるプラットフォームで限界に達した場合はこのプラットフォームで一部アセットを除外し、メモリ負荷を軽減することができます。Random Containerで簡単に実行でき、1つのサウンドのバリエーションを特定のプラットフォームだけで少なくし、余裕のあるほかのプラットフォームでは元のバリエーション数を維持します。

Random Containerのバリエーションを除外するためにはそのバリエーションのInclusionオプションのリンクを解除し、最適化したいプラットフォームでこのサウンドを除外します。こうすることにより特定のプラットフォームでメモリ使用量を減らしつつ、ほかのプラットフォームでは元のバリエーションを維持できます。

まとめ

最後までお読みいただきありがとうございます。ゲームのオーディオを最適化することは優れたパフォーマンスと効率的なメモリ使用を実現する上で非常に重要なステップです。ここで取り上げたさまざまな最適化テクニックを採用することにより、プレイヤーの総合的な満足度を高め、スムーズでシームレスな視覚的および聴覚的体験を楽しんでゲームプレイに完全に没頭してもらうという私たちの願いに近づけることを期待しています。

サポートとフィードバックを提供してくれたMad Head Gamesサウンドデザイナー仲間のDimitrije、Teodora、Selena、Marko、Petar、そしてEA DiceのNikolaに大変感謝します。オーディオプログラマーのArtem、Mad Head GamesのQAチームのOlja、Lazar、Stefan、Borislavにも心から感謝します。AudiokineticのJulie、Masha、Maximilien、Damianからは、たくさんのフィードバックと共にブログというかたちで私の考えを発表する機会をいただき、大変ありがたい思いです。

役に立つリンク集

Wwise-251技能認定コース:

https://www.audiokinetic.com/ja/courses/wwise251/

Profiling公式ドキュメント:

https://www.audiokinetic.com/ja/library/edge/?source=Help&id=profiling

Wwise Profilerパラメータ概要:

https://www.audiokinetic.com/ja/library/edge/?source=Help&id=performance_monitor_settings#profiler_counters

ボイススターベーションについて

https://www.audiokinetic.com/ja/library/edge/?source=Help&id=ErrorCode_VoiceStarving

ソーススターベーションのトラブルシューティングについて:

https://www.audiokinetic.com/ja/library/edge/?source=SDK&id=streamingmanager_tips.html#streamingmanager_tips_troubleshooting_sourcestarvation

WwiseのCPU最適化ガイドライン:

https://blog.audiokinetic.com/ja/wwise-cpu-optimizations-general-guidelines/

CPU使用の最適化:

https://www.audiokinetic.com/ja/library/edge/?source=SDK&id=goingfurther_eventmgrthread.html

コンバージョンに関する、コツとベストプラクティス:

https://www.audiokinetic.com/ja/library/edge/?source=Help&id=versions_tips_and_best_practices

私の記事のSpatial Audio最適化のセクション:

https://blog.audiokinetic.com/ja/wwise-spatial-audio-implementation-workflow-in-scars-above/#optimization

ミラン・アンティッチ

リード兼テクニカルサウンドデザイナー

Mad Head Games

ミラン・アンティッチ

リード兼テクニカルサウンドデザイナー

Mad Head Games

セルビア・ベルグレードのスタジオMad Head Gamesのリードサウンドデザイナー兼テクニカルサウンドデザイナー。カジュアルゲームやビッグバジェットゲームのサウンドデザインとオーディオシステム作成で豊富な経験を有し、特にUnreal Engineで開発されたAA・AAAプロジェクトでWwiseを活用することを得意とします。

LinkedIn

コメント

Replyを残す

メールアドレスが公開されることはありません。

ほかの記事

Planet Coaster - 群衆オーディオ: 追加レイヤ (PART 3)

Part 1. 拡張できるサウンドへ Part 2. Crowd Soundboxシステム Part 3. 追加レイヤ PLANET COASTER - 群衆オーディオ: PART...

13.9.2017 - 作者 プラネットコースター (Planet Coaster)

Pagan Onlineのダイアログ管理を、AIで効率化するまで

29.10.2019 - 作者 二コラ・ルキッチ(Nikola Lukić)

Wwise+GMEゲーム音声ソリューション:様々な音声利用方法を解放し、プレイヤーに「リアルサウンド」を提供

13.1.2022 - 作者 Tencent Cloud

『アサシン クリード ヴァルハラ』のサンドボックスミュージックシステム

『アサシン クリード ヴァルハラ』の音楽制作は大規模な取り組みとなりました。これは『アサシン...

12.7.2022 - 作者 アレクサンドル・ポワリエ(Alexandre Poirier)

レーシングエンジン音をREVで作成

30.8.2022 - 作者 徐巍

1人称視点のホラーゲームを単独で開発する

経歴 アレッサンドロ・グッツォと申します。私は1人称視点のホラーゲーム『The Land of Pain』と『The Alien...

14.11.2023 - 作者 アレッサンドロ・グッツォ