ゲーム内でどのようにバンクを管理するかを決定する前に、次のドキュメントに慎重に目を通すことを強くお勧めします。
可能な限り柔軟性を保ち、ほとんどすべてのタイプのゲームの要件を満たせるよう、Wwise はゲーム内でバンクを管理するための新しいアプローチを導入しています。この新しいアプローチは、元のメソッドの有用性を無効にせずに更なる制御力と柔軟性を与えてくれるので、ゲームの要件をよりよく管理できるようになります。
既存の従来のメソッドに比べ、3つの主な改善点があります:
このアプローチの主な利点は、メディアコンテンツを複数のメモリバンクに分割できることです。 例えば、単一のイベントを使用して、ゲーム全体のためのミュージックが開始したとします。従来のメソッドでは、バンクに当該イベントを追加することにより、ゲームの終了時にのみ再生する曲のためのプリフェッチを含めた、すべての対応するメモリ内サウンドとプリフェッチされたメディアが自動的に追加されます。ゲーム全体のための全メディアを格納することは、非常に非効率的だと思われます。一方、新しいアプローチでは、ミュージックメディアを複数のバンクに分割し、サウンドが再生される可能性がある場合にのみロードすることにより、メモリをより効率よく管理することができます。
また、メディアを複数のバンクに分割することにより、ロードされるメディアに優先順位をつけることができます。例えば、メモリに制限がある環境では、最も重要なメディアのみをロードしたいでしょう。非クリティカルなメディアは、十分な空き領域があった場合にのみメモリにロードされる別のバンクに格納しておくことができます。従来では、クリティカルおよび非クリティカルなメディアファイルの両方が同じバンクに含まれていました。バンクが大きすぎてメモリにロードできないと、クリティカルなものも含めていずれのサウンドも再生することができませんでした。
以下のセクションでは、バンクを生成し、これをゲームに統合するために使用できるさまざまなメソッドについて説明します。単一ゲーム内では、1つのメソッドまたは異なるメソッドを組み合わせて使用することができます。全てのゲームは異なるため、ゲームの特定の要件に応じて(1つまたは複数の)メソッドを選択します。
バンクを作成する時に行う選択は、ゲーム内でオーディオアセットを管理するのに要求される作業量に大きな影響を与え、ゲームのパフォーマンスに直接影響を与えます。サウンドデザイナーとオーディオプログラマーの両方が、このドキュメントを慎重に確認し、利用できる可能性を認識しておくことを強くお勧めします。共同作業することで、ゲームの特定のニーズを満たす戦略を考え出すことができるはずです。いずれのソリューションも機能しますが、メモリ使用量、I/O アクセス、ゲームへの統合の容易さなどを考慮して戦略を選択する必要があるということを覚えておいてください。どのメソッドにも利点と欠点があるので、ほとんどの状況では、メモリ使用量と統合の容易さのバランスが問題となるでしょう。
このメソッドを使用する場合、すべてのイベントコンテンツ、サウンド構造データ、メディアファイルは1つのバンクに格納され、同時にメモリへロードされます。
このメソッドは以下の場合に適用可能です:
もちろん、ほとんどのゲームには、一般的に無駄なメモリがありませんが、このテクニックには非常に使いやすく維持が簡単であるという大きな利点があります。Wwiseプロジェクト全体を最小限の時間でまるごとゲームに統合する場合には、このテクニックを使用することができます。
メソッドの実行方法:
Wwise 側
|
Note: デフォルトで、Event(イベント)、Structure(構造)、Media(メディア)の各列のチェックボックスが、全て選択されます。このメソッドでは、すべてを1つのバンクに含めるので、これは適切です。 |
|
Tip: 個々のプロジェクト要素ではなくワークユニット全体を1つのバンクに追加することの利点は、ワークユニットのコンテンツに加えられた変更を反映するためにバンクの再編集をする必要がないことです。これは、Wwiseがサウンドバンク内の要素とプロジェクト内の要素間にアクティブなリンクを維持するためです。プロジェクト内のワークユニットに変更が加えられると、サウンドバンクも自動的に更新されます。このアプローチを使用する場合、Generate ボタンをクリックするだけで、サウンドバンクの新規セットを生成することができます。 |
|
Note: 新しいプロジェクトには、デフォルトのワークユニットのみがあります。新たにワークユニットが作成された場合には、これらのワークユニットを必要に応じてバンクに追加しなければなりません。 |
ゲーム側
今回の例のゲームのサウンドバンクは1つだけなので、ゲームを初期化する時にこれをロードするだけです。もちろん、サウンドエンジンがまず正常に初期化されている必要があります。
... // Initialize the sound engine here. ... // Load the Init bank and the "All in one" bank. AkBankID bankID; // not used in this sample. 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 ); } ...
このメソッドに関する補足事項
長所:
短所:
|
Tip: このメソッドは、迅速簡単にオーディオをゲームに統合するための良い方法ですが、ゲームのメモリをより効率的に使用するメソッドへの切り替えは、プロジェクトの最終段階まで先延ばしにしないようにしてください。 |
このメソッドは以下の場合に適用可能です:
このメソッドは、すべての再生可能サウンドが、プレイヤーのゲーム内での現在位置によって駆動されるシングルプレイヤーゲームに適しています。オーディオコンテンツを複数のバンクに分割することで、1つ目のメソッドより効率的にメモリを管理することができ、さらにゲームへのオーディオ統合も比較的簡単にできます。
メソッドの実行方法:
まず、バンクを分割する方法を決定する必要があります。 例えば、次のようなバンクの分割が可能です:
Wwise 側
ゲーム側
ゲーム側では、適切なタイミングで適切なバンクをロードします。例えば、ゲームの開始時に汎用バンクをロードし、プレイヤーのゲーム内での実際の位置によって他のバンクをロードすることができます。ゲームによっては、レベル間の遷移を可能にするために、1度に1つ以上の「レベル」をロードするための十分なメモリを必要としますのでご注意ください。
... // Initialize the sound engine here. ... // Load Init bank and Common bank. AkBankID bankID; // Not used in this sample. 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 ); } ... // And at various places in the code, based on the actual needs: 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 );
このメソッドに関する補足事項
長所:
短所:
このメソッドは以下の場合に適用可能です:
メソッドの実行方法:
ゲームは非常に複雑になる場合があり、サウンドのトリガーは、ゲームのテクスチャ、時間帯やゲームオブジェクトの動き、オンラインマルチプレイヤーゲームでは、他のプレイヤーのアクションなど、様々な要因に基づく可能性があります。例えば、イベントベースまたはオブジェクトベースの環境では、他のゲームオブジェクトの近接性に基づいてサウンドがメモリにロードされます。それぞれのゲームオブジェクトは、それが特定の範囲内に入ったり、単に存在するとロードされるバンクのリストを持つことができます。
また、スイッチとステートは、再生するサウンドを決定することができます。当該サウンドを再生するイベントがバンクへ追加されると、すべての再生可能性のあるメディアも自動的にバンクへ追加されます。例えば、"Play_Footstep" という単一のイベントがあるとします。このイベントは、現在の地面のテクスチャに基づいて適切なサウンドを再生しますが、これはスイッチを変更することにより指定することができます。これは、うまく機能するのですが、ゲームプレイがロンドンの建物内で起こっている場合に、砂地の足音サウンド "footstep_sand.wav" や冬場の足音サウンド "footstep_winter.wav" をメモリ内に待機させておくのはメモリの浪費になり得ます。
この場合のメモリ浪費を回避するには、イベントと/またはサウンド構造をバンクに追加し、バンクに含められるべき対応サウンドを指定します。例えば、異なるテクスチャ上の足音サウンドを使用する場合、次のようなバンクを作成することができます:
Wwise 側
先の例を再作成します:
3つの異なるテクスチャがあるとします(雪、砂、コンクリート)。Wwise では、"ground_texture" スイッチに基づいて、3つのランダムコンテナの1つを再生するスイッチコンテナがあります。3つのランダムコンテナのそれぞれには、特定のテクスチャ用の足音サウンドのバリエーションが4つ入っています。
|
Note: ランダムコンテナをドラッグ&ドロップする代わりに、サウンドを個別にドラッグ&ドロップすることもできます。 しかし、コンテナの使用の利点は、コンテナ内のコンテンツに変更があった場合に手動で変更を加えなければならない代わりに、コンテナ内のすべてのサウンドを自動的にバンクへ追加できることです。 |
これで、4つのバンクができました。再生されるオーディオに関連するイベントと構造データを含むバンクと、特定の地面テクスチャに関連付けられたメディアのみを含む3つのバンクです。
ゲーム側
// Load Init and the EventBank AkBankID bankID; // Not used in this sample. 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 ); } ... // And at various places in the code, possibly based on the location: 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 );
このメソッドに関する補足事項
この手法を用いて実行可能な事は多数あり、上記はそのうちの1つの非常に具体的な例にすぎません。各バンクに含めるサウンドやイベントを1つ1つ決めることができるので、各バンクのコンテンツを完全に制御することができます。ゲーム内の各サウンドに対して個別のバンクを作成できますが、すべての新規サウンドは、ゲーム内の適切な場所でバンクをロードするための新しいコードを必要とするため、これを維持するのは非常に困難になります。各ゲームのゴールは、粒度と統合の容易さの間の良いバランスを見つけることです。
|
Tip: サウンドを1つずつ個別にロードする方法に関心がある場合は、LoadBank の代わりに PrepareEvents の使用を検討してみてください。 |
長所:
短所:
このメソッドは以下の場合に適用可能です:
準備済みイベントとは?PrepareEvent 関数を呼び出すと、システムはイベントを分析し、当該イベントに関連するすべての構造体とメディアがメモリにロードされていることを確認します。もしも、このロードが行われていない場合、システムは、欠落している情報をディスクから自動的にストリーミングします。イベントは、明示的に準備解除されるまで準備済みの状態になります。
メソッドの実行方法:
このメソッドでは、少なくとも2つのサウンドバンクを作成する必要があります。1つはメディア用、もう1つはイベント用です。構造部分は、これら2つのバンクまたは完全に別のサウンドバンクの一部のいずれかにすることができます。
PrepareEvent メカニズムを使用するサウンドバンクを構築する場合、唯一の基準は、すべての必要なイベント/構造体/メディアが少なくとも1つのサウンドバンクに入っていなければならないということです。ただし、構造体を複数のバンクに分割することで、より効率的にメモリの管理ができるということを覚えておいてください。
イベントの準備に先立って、(LoadBank() を使用して)イベント自体をSoundBankからメモリへロードしておかなくてはなりません。イベントには、イベントの準備に必要な依存関係についての情報が含まれているためです。
Wwise 側
|
Note: 単一バンク内に含まれる構造体データは、ランタイム時には分割できません。したがって、AKAoundEngine::PrepareEvent 使用時に、別のバンクからの構造体データが要求された場合、そのバンク内のすべての構造体が一度にロードされます。このため、プロジェクト内の構造体の内容を複数のバンクに分割して、メモリにロードされた不要な情報の量を最小限に抑えることができます。 |
ゲーム側
// Initializing the sound engine. AkInitSettings initSettings; AkPlatformInitSettings platformInitSettings; AK::SoundEngine::GetDefaultInitSettings( initSettings ); AK::SoundEngine::GetDefaultPlatformInitSettings( platformInitSettings ); // Set the required settings ... // Set PrepareEvent related settings initSettings.bEnableGameSyncPreparation = false; // Not used in the current sample. // Allocate a memory pool into which prepared media will be loaded. // If this is not done, prepare operations will fail. { // (Optional) Give the memory pool a name. This can be very useful for profiling. AK::MemoryMgr::SetPoolName( initSettings.uPrepareEventMemoryPoolID, L"PrepareEventPool" ); } AKRESULT eResult = AK.SoundEngine.Init( initSettings, platformInitSettings ); if( eResult != AK_Success ) { // Handle error. } // Load Init bank and the event/structure bank. AkBankID bankID; // Not used in this sample. 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 ); } ... // And then, at various points in the code: const char * pEventsNameArray[1] = { "My_Event_Name" }; // Preparing an event: eResult = AK::SoundEngine::PrepareEvent( Preparation_Load, pEventsNameArray, 1 ); // 1 is the array size // Unpreparing an event: eResult = AK::SoundEngine::PrepareEvent( Preparation_Unload, pEventsNameArray, 1 ); // 1 is the array size
このメソッドに関する補足事項
AK::SoundEngine::PrepareEvent への呼び出し(コール)は、I/O 関数呼び出しであると考える必要があることに留意してください。先の例では、ブロック関数を使用しました。AKSoundEngine::PrepareEvent 関数の他のオーバーロードを使用して、これらを非ブロック関数にし、別のコールバックを使用して完了通知を復活させることができます。
長所:
短所:
このメソッドは以下の場合に適用可能です:
このメソッドは基本的には先のメソッド(メソッド4:イベントを準備)と同じですが、イベントが準備される時にロードされるメディアをより細かく制御することができます。このメソッドでは、準備済みイベントと現在アクティブになっているゲームシンクの両方に関連付けられたメディアのみがメモリにロードされます。
次の2つのイベントを持つ単純なプロジェクトがあるとします:"Play_Maincharacter_FootSteps" と "Play_Monster_Footsteps"。それぞれのイベントは、移動するキャラクター下の地面のテクスチャに基づいて異なるランダムサウンドを再生するスイッチコンテナを再生させます。スイッチグループ名は "GroundTexture" で、以下の3つのステートを含んでいます:"Snow"、"Concrete"および"Sand"。
Wwise 内のスイッチコンテナ階層は、以下のようになります:
Switch_Container_Footstep_Main_Character
および
Switch_Container_Footstep_Monster
この例では、メモリにロードされる可能性のあるサウンドが18(3 サウンドより成る6つのグループ)あります。
メソッド4:イベントを準備 メソッドを使用することもできますが、イベントごとにメモリへロードされる6サウンドより細かい粒度レベルを得ることはできません。より細かい粒度レベルを得るために、 メソッド3:メディアの微細管理 メソッドを使用することも可能ですが、この単純なプロジェクトのために6つの異なるバンクを作成する必要があります(実際のプロジェクトでは、バンク数は急激に増加します)。そして、モンスター出現時には、どのようなテクスチャが可能であるか確認し、適切なバンクをロードする必要があります。
当メソッドでは、どのイベントとゲームシンクを使用する可能性があるかを指定するだけで、適切なメディアのみがロードされます。更に簡素化するために、全てのメディアを単一のバンクにグループ化することもできます。
メソッドの実行方法:
Wwise 側
ゲーム側
|
Note: 単一バンク内に含まれる構造体データは、ランタイム時には分割できません。したがって、AKAoundEngine::PrepareEvent 使用時に、別のバンクからの構造体データが要求された場合、そのバンク内のすべての構造体が一度にロードされます。このため、プロジェクト内の構造体の内容を複数のバンクに分割して、メモリにロードされた不要な情報の量を最小限に抑えることができます。 |
// Initializing the sound engine. AkInitSettings initSettings; AkPlatformInitSettings platformInitSettings; AK::SoundEngine::GetDefaultInitSettings( initSettings ); AK::SoundEngine::GetDefaultPlatformInitSettings( platformInitSettings ); // Set the required settings. ... // Set PrepareEvent related settings. //////////////////////////////////////////////////////////////// // The flag bEnableGameSyncPreparation is set to true to activate // the prepare gamesync mechanism. When set to false, the media // associated with all game syncs is loaded and there is no need // to call AK::SoundEngine:PrepareGameSyncs. // // When set to true, no media that is game sync dependent will be // loaded unless the game sync is activated by calling AK::SoundEngine:PrepareGameSyncs //////////////////////////////////////////////////////////////// initSettings.bEnableGameSyncPreparation = true; // Allocate a memory pool into which prepared media will be loaded. // If this is not done, prepare operations will fail. { // (Optional) Give the memory pool a name. This can be very useful for profiling. AK::MemoryMgr::SetPoolName( initSettings.uPrepareEventMemoryPoolID, L"PrepareEventPool" ); } AKRESULT eResult = AK.SoundEngine.Init( initSettings, platformInitSettings ); if( eResult != AK_Success ) { // Handle error. } // Load Init and the event/structure bank. AkBankID bankID; // Not used in this sample. 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 ); } // ... At this point, // the two events are loaded, but not prepared. No media is currently loaded. const char * pNameArray[1]; // Prepare the main character footstep event. pNameArray[0] = "Play_Maincharacter_FootSteps"; eResult = AK::SoundEngine::PrepareEvent( Preparation_Load, pNameArray, 1 ); // 1 is the array size // ... At this point, // one event has been prepared, but no media has been loaded yet. // Now since concrete is always available in the game. pNameArray[0] = "Concrete"; eResult = AK::SoundEngine::PrepareGameSyncs( Preparation_Load, in_eType, "GroundTexture", pNameArray, 1 ); // ... At this point, // the 3 sounds, Sound_Concrete_main_1, Sound_Concrete_main_2, and Sound_Concrete_main_3 are loaded. // Now, let's say that the main character enters a land where there is snow. pNameArray[0] = "Snow"; eResult = AK::SoundEngine::PrepareGameSyncs( Preparation_Load, in_eType, "GroundTexture", pNameArray, 1 ); // ... At this point, // 3 more sounds just got loaded : Sound_Snow_main_1, Sound_Snow_main_2 and Sound_Snow_main_3 // Then let's say that a Monster suddenly appears. pNameArray[0] = "Play_Monster_Footsteps"; eResult = AK::SoundEngine::PrepareEvent( Preparation_Load, pEventsNameArray, 1 ); // 1 is the array size // ... At this point, // 6 more sounds just got loaded ( Sound_Concrete_Monster_1.2.3 and Sound_Snow_Monster_1.2.3 ) // And now our player decides to run away from the monster, and the monster goes after him. // They run so far that they arrive at a place where there is no snow anymore. pNameArray[0] = "Snow"; eResult = AK::SoundEngine::PrepareGameSyncs( Preparation_Unload, in_eType, "GroundTexture", pNameArray, 1 ); // ... At this point, // The 6 sounds that were related to snow ( Sound_Snow_Monster_1.2.3 and Sound_Snow_main_1.2.3 ) are unloaded from memory. ...
|
Note: AK::SoundEngine::PrpareEvent と AK::SoundEngine::PrepareGameSync を呼び出す順序は重要ではありません。ステートが変化するたびに、イベントとゲームシンクのクロスマッチによりメディアプールが更新されます。 |
このメソッドに関する補足事項
長所:
短所: