UE4+ADX2で、ランダムに繋がるBGMをシームレス再生する

# はじめに
アンリアルエンジン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フレーズ用意しました。
![A01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/078cd16d-5ef1-e514-1812-d8e96bb0cc22.png)


### キューを作成する
キューを作成します。基本的にタイプは**「ポリフォニック」**です。

![A02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/b979c2f8-0bf4-040a-b07d-f8baa4fe3d71.png)
キュー名に対応したマテリアル（音源）をトラックにドラッグアンドドロップします。
![A03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/63f07ee3-8b55-816b-c4fb-bc0721146623.png)
すべてのキューに対し対応するマテリアルを入れていきます。
![A04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/675eb1ae-23ad-a08f-baf9-88bb2693137f.png)



### キューシートのビルド
完了したらキューシートをビルドします。
![A09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/dd1f6fc2-09ed-cf65-b9f4-71812b3a1bb5.png)
![A10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/b997ea07-0b02-8592-6b24-1b57653723af.png)


## UE4でBGMをランダムに繋げる

### キューシートのインポート
ビルドしたキューシートをインポートします。
UE4のコンテンツブラウザにacf, acbファイルをドラッグアンドドロップします。
![B01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/85e0649c-fbc4-7762-c06a-f0992bc754b1.png)
ツールバーの「Edit」→「Project Settings」を開き、acfファイルを今回出力したものに変更します。
![B02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/62dc2fc4-fff7-c558-a01b-2aa19bc63673.png)
![B03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/46e1e474-2443-6c7e-670c-7a14d5d41496.png)


### ブループリントからBGMを再生する
BGMを再生するためのアクターを作成します。
コンテンツブラウザで右クリックし、「Blueprint Class」を選択。
![B04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/43a05683-34b5-d425-622c-95da3e9faa7c.png)
「Actor」クラスを親にして作成します。
![B05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/efbf8b3b-9307-1e36-bbf0-9048b454b2be.png)
名前は「BP_RandomMusic」としました（分かりやすければ何でも大丈夫です）。
![B06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/b1a63ade-d7e9-13fd-cc1a-299494c0779d.png)
ダブルクリックして開きます。
「+Add Component」から、「Atom Component」を追加します。
![B07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/23196de3-d009-da5d-9874-d589ed51e797.png)
「シームレス連結再生」の機能を使うためには、Atom Componentを選択しておき、Detailsパネルで「Use Playlist」のチェックボックスをTrueにします。
![B08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/f2cd2f51-b99b-ed36-f31d-0c913d7b5e73.png)
イベントグラフに移ります。
**Event BeginPlay**から、**Set Sound**でイントロとなるキューを指定します。
**Play**ノードで再生を開始します。
![B09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/352f0a7b-4a99-6c8f-d440-62bef62b0fdb.png)
アクターをレベルに配置して再生すれば、イントロ部分のフレーズが自動的に流れるはずです。

### シームレスにキューを繋げる
キューの再生終了時に繋げてキューを再生するには、**Enqueue Sound**ノードを使います。
![B10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/b9aeda85-1dd6-41af-a82b-9ecd467f4205.png)
**Enqueue Sound**ノードを重ねるとプレイリストに追加していく要領で、次に繋がるキューが蓄積されていきます。**Set Sound**ノードでプレイリストをリセットすることができます。

### 再生終了前に次に再生するフレーズをランダムに選ぶ

次に再生するキューを格納するための変数を作ります。
**Enqueue Sound**キューの「New Sound」キューを右クリックし、「Promote to Variable」をクリックして変数化します。
![B11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/4f29a4eb-65c6-0a8c-c65c-ccb2b5102cf0.png)
名前は「Next Cue」としました。
![B12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/1b2c45b8-f050-0a99-4d75-0be3497fc2dd.png)
変数のタイプが「Sound Atom Cue」になっていることを確認し、Default Valueに適当なフレーズを設定しておきます。
![B13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/d725923a-9265-e713-b85f-b62d28adad6a.png)
次に、再生するフレーズを選ぶ処理の時間と、時間のカウントをするための変数をそれぞれ追加します。
どちらもFloat型変数です。
![B14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/6699e164-648a-bcc4-eec0-ce69f758024b.png)
「Sound Atom Cue」型の変数から線を伸ばし、**Get Duration**ノードを置くことでキューの再生時間（長さ）が取得できます。
![B15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/49899a50-af62-d0db-87a2-afc0d08884ac.png)
今回は「再生終了時間 - 3秒」のあたりで次のキューをランダムに選ぶことにします。予定時間を「Next Phrase Time」に格納します。
![B16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/de006e93-6ec8-2505-eb9c-4455a0493ec5.png)

次に再生時間をカウントし、実際にフレーズを選ぶ処理に移ります。
Tickイベントから処理を開始し、Countを加算していきます。値が「NextPhraseTime」を超えたらカウントをリセットし、フレーズのランダム処理を走らせます。
![B17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/f84fa10a-f6e4-3f30-d3a2-a36bc7c42038.png)
まず次のフレーズを確定→その後次のフレーズの再生時間を取得という流れを作りたいので、**Sequence**ノードを使います。
![B18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/142be953-45ee-10f6-d13c-3274d46b1300.png)
「Then 0」からは「Next Cue」にランダムな値を設定し、
![B19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/a491d34f-cab1-21ca-add5-6092346b825c.png)
「Then 1」からは**Enqueue Sound**でシームレス連結再生機能にアクセスし、「Next Phrase Time」を再計算します。
![B21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/1cf6b475-5b2f-4251-a3ba-3094fe7e8d0a.png)



### 一定数の再生で曲の展開を変える

一定数演奏したら、曲の展開を変える処理です。
Int型の変数「PhraseCount」を作り、フレーズを選ぶごとに加算します。
![B22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/49d26ac2-e010-23f5-e6b6-a11022fd7f35.png)
![B23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/a05dbe5e-b161-a353-22fc-4f2cb2db3712.png)
**Branch**ノードで、一定再生数ごとに分岐を作り、サビやアウトロが再生されるようにします。
![B24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/a810e3f6-8429-d57f-d567-59a04e3e9aa8.png)





## 補足

扱うキューの数が多い場合、データテーブルを使って管理・取得するのもいいかもしれません。