はじめに
アンリアルエンジン4とサウンドミドルウェア「ADX2 for UE4」を連携させ、
現在の状態に応じてループ楽曲の曲調、テンションを変化させていく「インタラクティブミュージック」を実装する方法です。
UE4+ADX2で、ゲームの状況に応じてBGMが盛り上がったり旋律が変化するやつを作りました。
— Sig @3DAct開発中 (@sleepyslowsheep) August 4, 2020
動画では横に動くと旋律、縦に動くと曲のテンションが変わります。
実装法は記事に全部書きました。音源、サンプルプロジェクトも公開してるので気軽に試せるはず!https://t.co/45z8vsiPtA
#UE4 #UE4Study pic.twitter.com/3H0AY9GdRO
準備にあたるAtomCraftでの工程はこちらにあります。楽曲素材、サンプルプロジェクトも下記の記事の冒頭にありますので参照してください。
ADX2 for UE4で変化するループBGMを再生する(AtomCraft編)
https://qiita.com/SigRem/items/a6ccd57a2881e8a43905
AtomCraftでの実装については
ADX2 for UE4の基本的な使い方はこちらの記事で。
ADX2 for UE4でインタラクティブミュージック(AtomCraft編)
https://qiita.com/SigRem/items/8af9b7d8445477906741
ADX2 for UE4でインタラクティブミュージック(ブループリント編)
https://qiita.com/SigRem/items/b53dc03d01c5f8fe1465
当記事ではUE4.23+「ADX2 LE v2.10.05」を使用します。
前提
「ADX2 for UE4 LE」を使用します。導入や簡単な使い方は以下の記事にあります。
ADX2 for UE4の導入で、一歩上のサウンド表現を(導入編)
https://qiita.com/SigRem/items/4250925f6d66a4fd287a
ADX2 for UE4の導入で、一歩上のサウンド表現を(実践編)
https://qiita.com/SigRem/items/c089b71c42e898980a46
やること
実装
UE4でインタラクティブミュージックを実装する
なにはともあれまずはここから。使用するプロジェクトで、CRIWARE Pluginが有効になっていることを確認します。

準備
エクスプローラからコンテンツブラウザに、AtomCraftで書き出したacfファイル、acbファイルをドラッグ&ドロップして追加します。

ツールバーの「Edit」→「Project Settings」を開き、

「Atom Config」に今回使用するacfファイルを指定します。ここが編集できない場合、一度UE4のエディタを再起動してみてください。変更したら、必要に応じてまた再起動します。

今回、見た目がわかりやすいよう2つのメッシュを用意しました。
左はボタンとなる土台、右はあたり判定となるボックス型のメッシュです。

ブループリントでオーディオマネージャを作る
BGMを再生し、直接Aisacコントロールなどを行う「オーディオマネージャ」とでもいうべきブループリントを作成します。
コンテンツブラウザの適当な場所でブループリントクラスを作成します。
親クラス(Parent Class)は「Actor」を選択します。

分かりやすい名前にリネームしておきましょう。

ダブルクリックして開き、Componentsパネルの「+Add Component」から「Atom Component」を追加します。

これが音源となります。Detailsパネルで鳴らしたいBGMのキューを指定します。

一度メインのビューポートに戻り、作成したアクターをドラッグ&ドロップで配置します。この時点で普通に音が鳴ると思います。

Aisacコントロールの初期値設定
オーディオマネージャを開きます。
Atom Componentを選択し、

「AISAC Control」の場所にある+ボタンを押します。

ふたつ項目を追加し、下の画像のように設定します。これでふたつのAisacコントロールの初期値が0になるため、「ミドル」「ハイ」テンションで追加されるトラックの音量も0になります。

この状態でゲームを再生すると、静かなバージョンのBGMになっているはずです。
オーディオマネージャにフレーズ、テンションの変化機能を実装する
オーディオマネージャの機能として、外部からターゲットとなるテンション・フレーズを受け取り、その際にAtom Componentへとブロック再生やAisacコントロールの変更を反映するというものを実装します。

オーディオマネージャとなるアクターのイベントグラフを開き、ふたつの変数を追加します。変数の型は両方ともIntegerです。

テンション、フレーズが変化した際に発火するイベントを作成します。
イベントグラフの空欄で右クリックし、「Add Custom Event」でカスタムイベントを作ります。

「ChangePhrase」などの名前をつけておきます。

Set Next Block Indexで次に再生するブロックを指定することで、受け取った再生フレーズを反映させます。

「テンション」は0~2の値を受け取り、それぞれ「ロー」「ミドル」「ハイ」テンションの場合へと分岐させます。
Int型変数の値によって分岐させるには、Switch on Intを使います。

Aisacコントロールの値を変化させてテンションを変化させます。Set Aisac by Nameで値を指定します。

ノードをコピペして、下の画像のように数値を入力します。
Switch on Int(白い線、Get Atom(青色の線)と繋ぐのを忘れてコンパイルエラー起きがちなので注意してください。

これで簡易的ではありますが、オーディオマネージャの機能は実装できました。
フレーズチェンジャーを実装する
ブループリントを新しく作ります。オーディオマネージャと同じく、親クラスは「Actor」です。
「BP_PhraseChanger」と名前をつけておきました。

外観となるボタンのメッシュを追加します。「+Add Component」ボタンから「Static Mesh」コンポーネントを選択します。

Detailsパネルで土台となるメッシュを指定します。

配置したメッシュの子となるように、当たり判定用メッシュも同じように置きます。
画像ではコンポーネント名をリネームしています。

当たり判定用メッシュのみ、Detailsパネルでプレイヤーをブロックしないよう「Collision Presets」にて「OverlapAll」を選択します。

当たり判定用メッシュを選択し、右クリックして「Add Event」→「Add OnComponentBeginOverlap」を選びます。

これにより、メッシュ内に侵入した際のイベントが発行されます。
イベントグラフに移動すると、既にイベントノードが配置されているはずです。

このアクターが「テンション」と「フレーズ」の情報を持てるよう、ふたつの変数を追加します。変数の型はオーディオマネージャと同じくInteger型です。テンションの高さとフレーズのID情報を、それぞれ整数で持つことになります。
変数リストの横の目玉のマークをクリックし、「公開(Public)」されている状態にします。これにより、レベルに配置した後に、個別に変数をいじることができます。つまり、複数配置された同じアクターであっても、変数の初期値を別々に持つことができます。

さらに、オーディオマネージャと通信できるよう**「BP_AudioManager」型の変数**を持っておきます。これにより、レベル上に置いた特定のオーディオマネージャアクターと直接変数などがやりとりできるようになります。
この変数を作るには、まず適当な型で変数を作成し、その後Detailsパネルで「Variable Type」を「BP_AudioManager」に変更します。「Reference」と書いてある青色のものです。


下の画像のように、3つの変数が作られていればOKです。
3つめの変数についても、目玉マークを押して公開設定にします。

処理を書いていきます。
画像のようにノードを配置します。侵入したアクターがプレイヤーキャラクターかどうかを判別し、一致した場合は「True」が返されます。

BP_AudioManagerの「CurrentTension」変数に、このアクターが持つ「Tension」変数の値を代入します。
まずGet AudioManagerノードを置き、そこから線を伸ばしてSet CurrentTensionを呼び出します。


「CurrentPhrase」変数も同じように代入します。

値が変わった際にオーディオマネージャ側で処理を起こしたいので、「BP_AudioManager」が持つカスタムイベントである「ChangePhrase」イベントを呼び出します。

これでフレーズチェンジャーも一通り実装が完了しました。あとは配置だけ!
レベルに配置する
フレーズチェンジャーをレベルに配置します。複製して12個、画像のように並べてみましょう。

12個すべてを選択し、Detailsパネルの「AudioManager」にシーン上に置かれている「BP_AudioManager」を指定します。
もし対象が複数ある場合、ひとつを除いて削除しておくと取り違えがなく安全です。

ひとつひとつにテンションとフレーズ情報を入力していきます。左下のブロックではテンションとフレーズ共に「0」を入力し、縦にいくにつれテンションを、横にいくにつれフレーズの数を増やしておきます。


ここまでできたら再生してみましょう。

横のブロックに移動すると次に再生されるフレーズが変わり……

上に1段移動するとパッド、ベース、ドラムが追加され、

最上段ではさらに、リードと追加のドラムが鳴り出します。

フレーズ、テンション共に変化していれば成功です。
テンションの変化をシームレスにする
現状ではテンションが変わった際、すぐに楽器が増減してしまい急激な印象を与えるので、これをゆっくりとシームレスに変化するように改善してみます。
「BP_AudioManager」に下の画像のように変数を追加します。追加分の変数は「0~1」の値を扱うので、float型です。

まず現在のテンションに応じて「TargetAisac」変数を変えておき、「CurrentAisac」変数を段々と変化させて実際のAisacコントロールの値とします。
Set Aisac by Nameだったところを「Target Aisac」変数の代入処理に変えます。

Event Tickで実際のコントロール処理を行います。もしイベントノードがない場合、新しく作ってください。
代入する値にFInterp Toノードを通します。これにより、毎フレームカーブを描いて目標の値に近づく数値が取得できます。

ふたつの「CurrentAisac」変数に同じ処理を行います。

最後に、代入された「CurrentAisac」変数の値をさらにSet Aisac by Nameノードに渡し、実際にBGMに変化を起こします。

「BP_AudioManager」の全体のイベントグラフはこんな感じです。

これでシームレスに変化するテンションの変化が実現できました。
BGMが最後まで再生されてしまった場合にループさせる
今回はBGMにループ処理をしていないので、もし最後まで再生されてしまった場合はSet Next Block Indexノードが効かなくなってしまいます。
BGMをループさせたい場合、Get Sequence Positionで現在の再生位置を取得し、最後のブロックに差し掛かったらSet Next Block Indexで最初のブロックを指定してあげると良さそうです。
