はじめに
アンリアルエンジン4とサウンドミドルウェア「ADX2 for UE4」を連携させ、BGMのフレーズをランダムで選定してプロシージャルなBGMを再生します。
「シームレス連結再生」を使い、BGMの任意の部分をランダムに演奏します。これにより、イントロとアウトロは毎回同じなのに、中間のメロディが毎回違うBGMを再生することが可能になります。統一感を保ったまま、ユーザーに飽きにくいBGMを提供することができるでしょう。
当記事ではUE4.26.1を使用します。基本的にブループリントのみでの実装を想定しています。
ADX2はインディー向けの「LE版」であれば、無料で使用できます。
https://game.criware.jp/products/adx2-le/
前提
ADX2 for UE4の導入や基本的な使い方は以下の記事にあります。必要に応じて参照してください。
ADX2 for UE4の導入で、一歩上のサウンド表現を(導入編)
https://qiita.com/SigRem/items/4250925f6d66a4fd287a
ADX2 for UE4の導入で、一歩上のサウンド表現を(実践編)
https://qiita.com/SigRem/items/c089b71c42e898980a46
実装
AtomCraftでBGMにタイミング情報を設定する
BGMのフレーズをシステム側で選ぶにあたって、次のフレーズを確定するタイミング情報をAtomCraftを使って埋め込みます。
現在のゲーム状況を反映させるのであれば、遅ければ遅いほどシチュエーションがBGMに反映されることになるためベターかと思います。
今回はイントロ用、アウトロ用のフレーズと、曲前半用に4種、曲後半用に2種で計8フレーズ用意しました。
キューを作成する
キューを作成します。基本的にタイプは「ポリフォニック」です。
キュー名に対応したマテリアル(音源)をトラックにドラッグアンドドロップします。
すべてのキューに対し対応するマテリアルを入れていきます。
キューシートのビルド
UE4でBGMをランダムに繋げる
キューシートのインポート
ビルドしたキューシートをインポートします。
UE4のコンテンツブラウザにacf, acbファイルをドラッグアンドドロップします。
ツールバーの「Edit」→「Project Settings」を開き、acfファイルを今回出力したものに変更します。
ブループリントからBGMを再生する
BGMを再生するためのアクターを作成します。
コンテンツブラウザで右クリックし、「Blueprint Class」を選択。
「Actor」クラスを親にして作成します。
名前は「BP_RandomMusic」としました(分かりやすければ何でも大丈夫です)。
ダブルクリックして開きます。
「+Add Component」から、「Atom Component」を追加します。
「シームレス連結再生」の機能を使うためには、Atom Componentを選択しておき、Detailsパネルで「Use Playlist」のチェックボックスをTrueにします。
イベントグラフに移ります。
Event BeginPlayから、Set Soundでイントロとなるキューを指定します。
Playノードで再生を開始します。
アクターをレベルに配置して再生すれば、イントロ部分のフレーズが自動的に流れるはずです。
シームレスにキューを繋げる
キューの再生終了時に繋げてキューを再生するには、Enqueue Soundノードを使います。
Enqueue Soundノードを重ねるとプレイリストに追加していく要領で、次に繋がるキューが蓄積されていきます。Set Soundノードでプレイリストをリセットすることができます。
再生終了前に次に再生するフレーズをランダムに選ぶ
次に再生するキューを格納するための変数を作ります。
Enqueue Soundキューの「New Sound」キューを右クリックし、「Promote to Variable」をクリックして変数化します。
名前は「Next Cue」としました。
変数のタイプが「Sound Atom Cue」になっていることを確認し、Default Valueに適当なフレーズを設定しておきます。
次に、再生するフレーズを選ぶ処理の時間と、時間のカウントをするための変数をそれぞれ追加します。
どちらもFloat型変数です。
「Sound Atom Cue」型の変数から線を伸ばし、Get Durationノードを置くことでキューの再生時間(長さ)が取得できます。
今回は「再生終了時間 - 3秒」のあたりで次のキューをランダムに選ぶことにします。予定時間を「Next Phrase Time」に格納します。
次に再生時間をカウントし、実際にフレーズを選ぶ処理に移ります。
Tickイベントから処理を開始し、Countを加算していきます。値が「NextPhraseTime」を超えたらカウントをリセットし、フレーズのランダム処理を走らせます。
まず次のフレーズを確定→その後次のフレーズの再生時間を取得という流れを作りたいので、Sequenceノードを使います。
「Then 0」からは「Next Cue」にランダムな値を設定し、
「Then 1」からはEnqueue Soundでシームレス連結再生機能にアクセスし、「Next Phrase Time」を再計算します。
一定数の再生で曲の展開を変える
一定数演奏したら、曲の展開を変える処理です。
Int型の変数「PhraseCount」を作り、フレーズを選ぶごとに加算します。
Branchノードで、一定再生数ごとに分岐を作り、サビやアウトロが再生されるようにします。
補足
扱うキューの数が多い場合、データテーブルを使って管理・取得するのもいいかもしれません。