ADX2 + UE4でオープンワールド系ゲームを想定したBGMの実装

# はじめに
アンリアルエンジン4とサウンドミドルウェア「ADX2 for UE4」を使って、オープンワールド系のゲームを想定したBGMの実装をしてみます。

https://twitter.com/sleepyslowsheep/status/1481539926814384128?s=20

起伏に富んだ広大なフィールドを用意すれば、それだけプレイヤーがロケーションに滞在する時間も長くなります。
ずっと同じ曲調のBGMを流すだけでは、飽きを与えてしまうでしょう。

そこで、プレイヤーの行動やシチュエーションの変化に応じてBGMが変化する、「インタラクティブミュージック」を実装してみましょう。
「システムがプレイヤーの遊び方を監視し、うまく雰囲気を作ってあげる」イメージで、ゲームとBGMの雰囲気をあわせて上げる感じです。

次の画像はゲーム内での街から街への移動の雰囲気を想定したものです。
![A00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/bb2fd448-bc2a-0499-fe55-d146f0b30fce.png)
色が変わっていく横線のように、各シチュエーションをブレンドしてBGMが移り変わるようにします。
街から出たばかりは冒険の始まりを予感させるように静かな雰囲気で、街を離れるにつれて本来のフィールド曲に遷移していきます。
途中で雨が降ってくれば静かで物悲しいフレーズをブレンドしていき、馬に乗ればパーカッションを追加し躍動感のあるBGMへと変化させていきます。
ダンジョンとの距離が近くなるごとに神秘的なBGMを混ぜ、空気の変化を表現します。
また別の街が近くなればBGMは徐々に静かになっていき、旅の終わりの安息を演出します。


当記事ではUE4.26.1を使用します。基本的にブループリントのみでの実装を想定しています。
ADX2はインディー向けの「LE版」であれば、無料で使用できます。
https://game.criware.jp/products/adx2-le/

記事執筆時点のADX2 for UE4のSDKバージョンはv1_29です。

# 前提
ADX2 for UE4の導入や基本的な使い方は以下の記事にあります。必要に応じて参照してください。
ADX2 for UE4の導入で、一歩上のサウンド表現を（導入編）
https://qiita.com/SigRem/items/4250925f6d66a4fd287a
ADX2 for UE4の導入で、一歩上のサウンド表現を（実践編）
https://qiita.com/SigRem/items/c089b71c42e898980a46


# 実装

## AtomCraftでBGMを構成する

### マテリアルの用意

設定したシチュエーションのとおり、ベーシックフレーズ+5フレーズを用意しました。数秒程度の簡単なBGMです。
![A01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/a8925b6c-baca-6467-1e80-5004218cc1fb.png)

マテリアルツリーにインポートした後、それぞれ選択してインスペクターにて「ループ情報の上書き」を「True」にしておきます。これでフレーズがループ再生されるようになります。
![A04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/5fd50174-2b9a-11f3-b462-26724f5da74e.png)

### キューの作成

新規にキューを作成します。
ワークユニットツリーのキューシートを右クリックし、「新規オブジェクト」→「キュー『ポリフォニック』の作成」です。
![A02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/043de8a8-e306-3955-f26f-64aa39217d5a.png)

作成したキューに、トラックごとにマテリアルをドラッグアンドドロップで突っ込んでいきます。
![A03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/a58289ad-1cae-970f-7b32-72e5e82b14b5.png)
試しに再生して、ループが有効か試してみるといいかもしれません。


### AISACコントロール

5つのシチュエーションを想定して、このキュー（楽曲）が6つのパラメータを持つものとして実装してみます。
今回設定するのは
- ニュートラル状態を示す**「通常度」**
- 天候変化時にBGMを変化させる**「悪天候度」**
- 速く移動している間躍動感を追加する**「移動速度」**
- 遺跡などのダンジョンやランドマークの近くに行くと高まる**「ダンジョンの近さ度」**
- 敵との戦闘状態で使われる**「緊迫度」**
- 視界の狭まりとともにBGMにエフェクトをかける**「霧の深さ」**
です。
このうち「霧の深さ」だけはハイパスフィルターのかかり具合に影響し、他のパラメータはトラックの音量変化に使用されます。

これらは**「AISACコントロール」**機能によりグラフ化され、UE4ではブループリントやC++、またはシーケンサーなどから操作することが可能です。

AISACコントロールについて基礎的な知識は次の記事にあります（当記事でも使い方を解説します）。

**ADX2 for UE4でインタラクティブミュージック（AtomCraft編)**
https://qiita.com/SigRem/items/8af9b7d8445477906741

### AISACコントロールを設定する

#### 「通常度」のAISAC

「通常状態」のトラックを右クリックし、「新規オブジェクト」→「AISACの作成」を選択します。
![A05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/dcc73c56-2064-7933-b737-091e52b84524.png)
名前を「Aisac_Neutral」として追加します。グラフタイプは「ボリューム」のままで大丈夫です。
![A06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/c91ceaf9-89b7-bd4f-e7f0-ee9f22cd8efb.png)
AISACタブに切り替わるので、トラックの「ボリューム」を選択してからグラフを編集します。
右肩上がりのシンプルなグラフでOKです。
![A07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/d11530a3-ca79-4f37-7336-467121eda37e.png)
グラフができたら下部からタブを切り替えてタイムラインに戻ります。
![A08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/93d49585-4b32-9779-216f-75dde6a6f0f5.png)
Aisacコントロールの動作チェックのため、トラックをベース部分と通常状態以外0にしておきます。
![A09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/f4904709-2f01-f0aa-7556-cc22be5d44c4.png)
AISACタブでテスト再生し、上部のスライダーを動かすと通常状態のトラックの音量が変化するはずです。
![A10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/a608db9d-197d-93a0-e33a-1377e8f5cddc.png)
テストが終わったら、各トラックのボリュームを戻しておきましょう。


#### 「悪天候度」「移動速度」「ダンジョンの近さ度」「緊迫度」のAISAC

同じように、各トラックにAISACを追加していきます。
**コントロールのIDを被らせない**ように注意してください。同じIDが存在すると、ひとつのパラメータを変動させた際に連動して音量が変わってしまいます。
![A11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/b9ba8505-d1ce-ce1c-1200-5f035b06ca94.png)
![A12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/162ba7ea-b456-f15a-1b6e-e0f054fd3537.png)
![A13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/4505d1f6-a81f-02d6-d722-98afff0a3c35.png)
![A14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/cc32e2a3-a8d3-4233-6156-720c47320601.png)
![A15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/e5a32166-eb5f-89e4-969b-a65cb8f519f3.png)

各AISACの「ボリューム」を選択し、右肩上がりのグラフを作ります。グラフの一番右を最大まで上げてしまうと音量が大きすぎることがあるので、最大でも「1.0」にとどめておくのがいいでしょう。
![A16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/4cd1d799-f4d6-eb32-800f-aa0db63652f2.png)

#### 「霧の深さ」のAISAC

霧の深さによってBGM全体にエフェクトをかけたいので、ハイパスフィルターのAISACはトラックに対してかけることになります。
トラックの一番上、キュー自体を選択して、トラックリストの空欄で右クリックし「新規オブジェクト」→「AISACの作成」をします。
![A17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/36c00c9c-46e3-3d3c-3891-e9b446b1dcf1.png)
IDを被らないものに変更し、グラフタイプを「バンドパス - Cof高域」にします。
高域の音をカットしてくぐもった音に変化させます。
![A18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/76f1526c-8f07-f4bc-5ec9-4082b337df0a.png)
紫色のグラフが現れます。これだけは数値が大きくなるにつれて下がっていくように設定します。
![A19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/2de23ebf-52bd-310e-1134-58c1b6b63f9d.png)


### キューシートのビルド

ここまでできたら、スライダーをいろいろ動かして遊びながら動作確認してみましょう。
![A20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/a550d976-eed4-0ffe-af43-fdbd42254700.png)

動作が大丈夫そうならキューシートをビルドします。
![A21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/a92d8edb-31d3-a583-78bb-f85e2fca1cb0.png)
![A22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/5e9965dc-9def-cd4b-27d9-42d982073314.png)




## UE4での実装

### セットアップ

ビルドしたキューシートをUE4にインポートします。
![B01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/26ba30ec-bec2-9c77-6ad6-f09178f51392.png)

プロジェクト設定を開き、CriWareタブ→「Atom Config」にacfファイルを設定しておきます。
![B02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/bf73d084-8368-8712-64eb-137465653bb7.png)
![B03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/3577bf32-74fc-3167-a10e-0d459f684d6d.png)

コンテンツブラウザからキューをドラッグアンドドロップしてレベル上に配置します。
![B04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/049e20dd-4584-c7b4-b8c2-d5105bc45b27.png)
ゲームを再生すると、自動でBGMが再生されます。AISACコントロールのパラメータを設定していないため、全部入りのBGMが聞こえてくることがあります。
![B05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/5d947288-d8c0-efd6-cee9-a1cc04f199fd.png)

AISACコントロールの初期値を設定することもできます。レベルに配置したキューを選択し、Detailsパネルの「AISAC Control」の項目にある「+」ボタンを押してAisacコントロールを追加します。
![B06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/d8998a17-4f7f-64d5-85f4-d0690b5cf561.png)
必要なぶんだけ項目を追加したら、「AisacControl_00」などの番号を振って初期値を設定します（『Name』はAtomCraftでつけた名前とは関係ないようです）。
![B07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/a7341e5a-beb4-e5fa-53ad-2a6eb5c3817a.png)
画像のように設定した場合、ベースのフレーズ+通常状態のフレーズだけが流れるようになります。

では、ここから各シチュエーションに応じたBGM変化を実装していきます。


### 街が近い状態のBGM

街が近い状態では、距離に応じて通常状態のフレーズの音量を上げていく設計とします。

まずはトリガーボリュームなどを使い、プレイヤーが街へ近づいたことを検知して処理を有効にします。
![B08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/d70bd999-7a2c-6abe-3db3-6b0a54fcbc0d.png)

街との距離を計測するために、基準となるアクターを置いてしまうのがいいでしょう。
次の画像では「TargetPoint」アクターを使用しています。
![B09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/8e577428-afce-0d49-c2d7-2f4ab8802612.png)
**Get Vertical Distance To**ノードで2アクター間の距離を出すことができます。試しに**Print String**で画面に表示してみましょう。
![B10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/bc843a1c-1f61-4324-2b7d-06dd950451fe.png)
![B11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/91d775b9-210d-9b47-9500-30c911b87364.png)

通常の場合この数値はcm単位として扱われるので、50000（500メートル）離れたら街から離れ、通常のフレーズに移行するようにしてみます。

**Get Vertical Distance To**ノードで取得した距離を50000で割り、数値を0～1の間になるよう**Clamp**ノードで丸めます。これが「街から離れた度」と言っても良さそうです。**Lerp**ノードを使い、街からの距離に応じて**Set Aisac by Name**で通常状態のフレーズの音量を上げていきます。
![B12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/97820ce5-c0d6-015c-9787-594e2dd7749f.png)



### 雨天時のBGM

天候が崩れた場合の変化を起こすには、レベルブループリントや環境マネージャを担当するアクターに「雨の強さ」になる変数を持っておき、キューに数値を渡してBGMを変化させます。
![B15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/5a4f0c01-24b1-d037-eda7-f88a7a58298f.png)

![B16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/225bd15d-429e-4f28-c114-8857b6290d68.png)
画像ではTickイベントで処理を行っていますが、雨の強度を変化させるイベントがあればその際に一括で処理してしまったほうが負荷に優しいです。

ストーリー進行による悲しい場面は天候に関わらずAisacコントロールをしたり、嵐の場合はBGM自体をオフにするなどの演出をしても面白いかもしれません。


### 移動速度が速い場合のBGM

平原で馬に乗った状態や、キャラクターが全力でダッシュしている間BGMを躍動感のあるミックスに変化させます。

プレイヤーのアクターには予めキャストして変数化しておきます。
![B19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/13f058ea-dc5b-c2d4-6be7-b994d6548ad9.png)

**Get Velocity**で移動速度を取得できます。これだけだとジャンプした際に縦方向の速度も加わってしまいますので、**VectorLengthXY**を噛ませることでXY軸の移動速度だけを参照するようにします。
![B21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/65357f85-5a79-c3f9-d32e-b65472b2cb5d.png)

処理の全体図は次のようになります。
移動速度を2000で割れば、速度が2000以上のときに完全にフレーズが置き換わることになります。
![B20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/eb7ec529-7a0e-8640-b3d6-2d3f1a8e3aba.png)
立ち止まると通常状態のフレーズに戻るので、走っている間だけ専用のフレーズが聞こえるようになります。

立ち止まったり走った際に急激にフレーズが変わってしまうので、**FInterpTo**ノードを噛ませることで滑らかな変化になるかと思います。
**FInterpTo**ノードを使った際の処理の全体図は次のような感じです。
![B22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/bb7c29e1-de83-a1c0-740d-0f89c5dcf874.png)



### ダンジョンが近い状態のBGM

ダンジョンが近い場合の実装は街が近い場合とほぼ同じです。
![B13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/c6699b5b-4c3e-45e5-1557-63dcfa0417b2.png)
**Lerp**ノードの数値を逆にして、近づくほど専用のフレーズの音量が高まるようにしましょう。
逆に通常状態のフレーズは入れ替わるように小さくなっていきます。


![B17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/d110f025-bb51-c38e-4211-561476e07aa5.png)
![B18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/6d774802-bdec-bbda-1a5c-9baf161f27ab.png)



### 戦闘時のBGM

戦闘などの場合は決まったテンポでBGMを切り替えたいので、タイムラインノードを使います。
タイムラインノードが吐き出したFloat変数をAisacコントロールに渡してあげればOKです。
![B23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/2655519c-4262-33a8-4be3-010e3385999f.png)
タイムラインノードの中身は次のようになります。
![B24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/3ee71274-e747-51c0-c04a-4939352447af.png)


### 霧が深い状態のBGM

雨の場合と同じです。

霧の強さの変数を持っておき、
![B17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/efb0cc2b-2b3c-8069-f13c-d96f59ff95ba.png)
Aisacコントロールに渡します。
![B18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/8a9c3d5c-7de5-5911-7a06-62ad24339c3f.png)

最大まで霧が深まるとフィルターが大きくかかり、BGMがほぼ聞こえなくなります。
これもシチュエーションに合っていて良さげですが、少しだけBGMが聞こえる状態にしたい場合はAtomCraftでグラフを少しいじってみると解決します。
![B14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/e2ed113d-1b91-4936-d3af-95757861ef09.png)


### 補足
今回はAisacコントロールを細かく分けたので、シチュエーション別に実装していけば同時に処理を走らせることも可能なため「ダンジョンの近く+緊迫状態+霧が深い」状態といった場合にミックスされたBGMを作り上げることもできます。

戦闘状態はゲームの華なので、さらに細かく凝ったBGM変化についてもそのうちまとめてみたいと思います。