はじめに
Jater (Ruohao) Xuによる『逆コーラップス:パン屋作戦』の開発経験についての3部構成のブログ、今回は最終回となります。
- 第1回のこちらの記事では、ゲーム中のシネマティックスをWwiseで駆動する方法について解説しました。
- 第2回のこちらの記事では、傾いた2Dトップダウンビューの特殊な減衰の問題をカスタム3Dオーディオシステムで解決した方法について解説しました。
WwiseのMeterプラグインを使ったアニメーションのリップシンク
Techブログ|パート3
このゲームでは、オーディオがゲームプレイのメカニズムを駆動する場面や要素が豊富です。私たちはWwise Meterプラグインの助けを借り、正確なオーディオデータをリアルタイムに取得し、ゲームエンジンに送り返すことにより複数のオーディオシステムを強化しました。
アニメをテーマとしたほかの多くのゲームと同様、『逆コーラップス』も豊かなストーリー性の対話が多く、会話の一部は戦闘ゲームプレイを発端としていますが、大部分は2Dナレーションであり、2人のキャラクターが画面の左右で掛け合うシーケンスが頻繁にあります。
上図の例はキャラクターのモンドが話している場面のスクリーンショットですが、本ゲームの2Dナラティブシステムが使われています。会話がトリガーされると、キャラクタースプライトの唇アニメーションが再生されます。この機能を駆動しているのが、Wwiseから取得したオーディオボリュームの情報です。
オーディオボリュームのデータを利用して音声と唇のアニメーションを同期させることができ、キャラクター間のやり取りの没入感やリアル感が高まります。このアプローチはプレイヤーのストーリー体験を深め、プレイヤーを引き込みます。
ボリュームデータをリアルタイムに取得するために使ったのがWwise Meterプラグイン(Wwise Meter (audiokinetic.com))であり、これはオーディオデータをWwiseからゲームエンジンに送信する非常に効率的で使いやすいプラグインです。下図はメインのスピーチバスに設定したメーターのセットアップです。
ゲームエンジンにデータを送り返す役目を担うSpeech_MeteringDataというRTPCを、Wwise Meterプラグイン内でリンクさせました。ゲーム内でトリガーされた台詞の出力ボリューム情報を、このRTPCがとらえます。値を-48から0までに制限し、オーディオボリュームの範囲を決めています。音声ボリュームがピークに達した時はこの値が0を超える可能性がありますが、典型的なミキシング設定において、そのようなシナリオを回避して数値を0未満とすることが一般的に推奨されます。
このような構成により、オーディオボリュームのデータを正確にキャプチャし、制御されたかたちでゲームエンジンに転送することができるため、さまざまなゲームプレイのメカニズムが実装しやすくなります。
以上がWwise側の設定です。ゲームエンジンがこのデータを使えるようにするためには、コードを数行追加し、ボリュームの範囲を検知し、その数字をゲームのアニメーションシステムが使えるデータに変換すればよいのです。アニメーションシステムやプラグインはゲームごとに異なるため、ここでは大まかなアニメーションコードを記載しました。
今回のゲームのアニメーションシステムは複雑ではなく、キャラクターの口にはOpenとClosedステートしかないため、単純に三項条件演算を使い発言中のキャラクターの口を開けるのかどうかを取得し、それにアニメーションを対応させればよいのです。(GetGlobalRTPC()の実装については上記の説明をご参照ください。)
bool bIsCharacterMouthOpen = (GetGlobalRTPC(“Speech_MeteringData”) > -48.0f && GetGlobalRTPC(“Speech_MeteringData”) <= 0) ?true : false;
ほかの多くのゲーム、特に3Dゲームなどではキャラクターのスケルトンのリグにジョイントやボーンが入っており、アニメーターが使う口の開き具合を変えるジョイント角度を私たちが調整することができます。これは浮動小数点数で表されることが多いです。例として、その数値をspeakingCharacter.SetMouthOpenness(float mouthJointAngle)で取得し、口の最小角度と最大角度を0度からspeakingCharacter.MaxMouthOpenness()度とした場合を考えます。
以下のようにパラメータモディファイアの出力値を抽出する小さなラッパー関数を作成し、この機能を使用する領域にオンデマンドで適用します。
public float GetGlobalRTPC(string rtpcName)
{
int rtpcType = 1;
float acquiredRtpcValue = float.MaxValue;
AkSoundEngine.GetRTPCValue(rtpcName, null, 0, out acquiredRtpcValue, ref rtpcType);
if(acquiredRtpcValue >= 0.25 && acquiredRtpcValue <= 16)
{
return acquiredRtpcValue;
}
else
{
return 1.0f;
}
}
上記の関数はRTPCをグローバルに設定するだけでなく、不正な値が検出された場合、設定予定のRTPCを無視してデフォルト値の1.0fにリセットします。
今回は以下の関数を使用して上のコードを改良します:
public float SetMouthOpenessByWwiseAudio()
{
float mouthOpennessToSet = 0.0f;
float retrievedMeteringRTPCvalue = GetGlobalRTPC(“Speech_MeteringData”);
if (retrievedMeteringRTPCvalue > -48.0f && retrievedMeteringRTPCvalue <= 0)
{
mouthOpennessToSet = speakingCharacter.MaxMouthOpenness() * Normalization(retrievedMeteringRTPCvalue, -48.0f, 0.0f));
}
speakingCharacter.SetMouthOpenness(mouthOpennessToSet);
}
実際この関数がWwise Meterプラグインから受領したオーディオボリュームデータに基づき、口の開き具合を厳密に設定します。オーディオボリュームに合わせ、口のアニメーションが正確かつ滑らかに同期します。
免責事項:この記事で使用されているコード・スニペットは、説明のみを目的とした再構築された汎用バージョンです。基礎となるロジックが正しく機能することは検証済みですが、特定のプロジェクト固有のAPIコールや関数は、著作権上の制約を受ける可能性があるため、例では省略しています。
コメント