UE5+ADXで、水深に応じて水中の音を変化させる

# はじめに
アンリアルエンジンとサウンドミドルウェア「ADX for UE」を連携させ、「**水中の環境音や効果音を、水深に応じて変化させる**」実装を行ってみます。

水面近くでは音が少しだけくぐもって聞こえ、深く潜るにつれて、段々と音が鈍く重く聞こえるようになっています。
![A00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/9706aac2-7b1f-af6b-d6b3-325ec4d7d758.png)

カテゴリ＋グローバルAISACを使用して実装します。


# 前提

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

なお、ADX2は「ADX」へ名称が変更になりましたが、ツール構成は変更ありません（2がないから古いほう、というわけではありません）。

記事執筆時点のADX for UEのSDKバージョンはADX LE UE SDK(1.31.00.01)です。

ADX for UEの導入や基本的な使い方は以下の記事にあります。必要に応じて参照してください。
**ADX for UEの導入で、一歩上のサウンド表現を（導入編）**

https://qiita.com/SigRem/items/4250925f6d66a4fd287a

**ADX for UEの導入で、一歩上のサウンド表現を（実践編）**

https://qiita.com/SigRem/items/c089b71c42e898980a46


# 実装

## AtomCraftでサウンドを用意する

### マテリアルのインポート

まずは効果音素材を用意します。
今回は最小構成として、環境音となる「BGS_UnderWater」、水中での効果音例である「SE_Impact」のふたつのサウンドを用意しました。
![A01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/69791630-fce2-450c-b06a-3f5388e7528e.png)
環境音の「BGS_UnderWater」については、インスペクターにてループ情報の上書きを「True」、ループタイプを「ループ」と設定して、再生され続けるようにします。
![A02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/e1d8c08f-50b4-00f7-7f6a-5a4865b801fc.png)


### キューの作成
キューシートを選択し、「新規オブジェクト」→「キュー『ポリフォニック』の作成」を選択してキューを新規に作成します。
![A03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/cf375b52-bd54-2f99-a768-f6a40e0dad19.png)
ふたつのマテリアルに対し、それぞれにキューを作っておきます。特別な設定は必要ありません。
![A04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/19a30cbd-8e08-c711-03c0-457245ced94c.png)



### AISACコントロールの作成

環境音のキューを選択し、トラックリストの空欄にて「新規オブジェクト」→「AISACの作成」を選択してAISACコントロールを作成します。
![A05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/d5fbda1b-7f75-419b-b7e8-bf1d31fdffeb.png)
AISACの追加ウィンドウが出るので、AISAC名とグラフタイプを設定します。グラフタイプは音を鈍くする「ピッチ」を使用します。
![A06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/68b1a89e-77b4-e632-ba6d-f2c36712c119.png)
右肩下がりの簡単なグラフを作成します。
設定できたら再生しながら上部のスライダーを動かし、ピッチが下がることを確認してください。
![A07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/faf3d9c0-496b-895f-f066-b225b9289899.png)
ふたたびトラックリストの空欄にて、AISACを作成します。今度はグラフタイプを「バンドパス - Cof高域」とし、高音を聞こえにくくしていきます。
![A08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/7f0e2ae6-714d-589e-6a41-7f128213e3a0.png)
ここでも右肩下がりのグラフを作ります。
スライダーが最大の「1.0」まで到達するとギリギリ聞こえるくらいの塩梅で調整してみましょう。
![A09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/ffcd9e40-6020-7aa4-da00-2868240d3bc9.png)


### グローバルAISACの設定

スライダーを動かし調整して、聞こえ方に満足したら、作成したAISACコントロールを「**グローバルAISAC**」に変換します。これにより、他のキューにもこの聞こえ方が適用できるようになります。
（水中では一括して効果音にAISACコントロールを適用したいため）。

ワークユニットツリーでAISACコントロールを選択し、右クリックして「グローバルAISAC化」を選択します。
![A10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/36f8bb08-19fa-f912-54c8-8ac076747cb6.png)
プロジェクトツリーに「Aisac_Pitch」というグローバルAISACが作られ、元のAISACコントロールは名前に「Link」とつきました。
これはキュー側のAISACが、グローバルAISACの設定と同期されていることを表します。
![A11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/7522aa84-776c-4919-b5bc-1ebda41ac315.png)
もうひとつのAISACコントロールについても同じようにグローバルAISAC化します。
![A12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/a494ffd0-3e1e-a19e-df58-ae27d783faab.png)
こちらも名前に「Link」がつきました。
![A13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/99f05a6d-8f50-6c37-61e0-ca0d94fde9c9.png)
プロジェクトツリーのグローバルAISACを、他のキューにドラッグアンドドロップすると同じように「LinkAisac」が作られ、この効果音にAISACコントロールがかかった場合の聞こえ方を確認できるようになります。
![A14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/daac7094-add5-0d45-e0eb-008526b67e6c.png)
![A15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/d7d529ee-b326-da32-7546-deb80c4b1e6c.png)



### カテゴリの設定

これらのAISACコントロールの影響を受けるキューを、「カテゴリ」機能で指定します。
プロジェクトツリーの「カテゴリ」フォルダを開き、カテゴリグループの中にあるカテゴリを任意の名前にします。
または、新規にカテゴリを追加してもOKです。
![A18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/5e01ea00-d625-d9a5-2298-2ebc3095e950.png)
キューを選択し、インスペクターにて「カテゴリ」をクリックします。
![A19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/da1724c1-b2fa-a859-bebb-9df93029c1c5.png)
カテゴリの編集ウィンドウが開くため、作成したカテゴリを適用します。
![A20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/3ebdd2ff-db77-b88e-e302-232460b6c066.png)
**環境音、効果音のキューそれぞれに対して、忘れずにカテゴリを設定**しておきましょう。

水中で再生される効果音には一括してこのカテゴリを設定し、逆に水深の影響を受けないであろうUIなどのサウンドには別のカテゴリを設定すると良いでしょう。
また、BGMについては通常エフェクトをかけませんが、深く潜ることを印象づけたい場合などには、要所でAISACコントロールを使っても面白いかもしれません。




### キューシートのビルド
ここまでできたら、UEに持っていくためキューシートをビルドします。
![A16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/bfe25079-1f70-7d8c-78b6-fc40f00207a3.png)
![A17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/74b16f68-309f-d232-77c9-ec74b0acfd75.png)





## UEでサウンドを再生・変化させる

### キューシートのインポート
ビルドしたacbファイル、acfファイルをコンテンツブラウザの任意の場所にドラッグアンドドロップしてインポートします。
![B01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/e0d895d6-472c-c91f-ff1c-4827b68542c9.png)
必要なキューがすべてインポートされていることを確認します。
![B02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/5ace3e85-3918-6981-fcb8-d718e1c116f7.png)
プロジェクト設定を開き、
![B03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/e386a266-015a-191d-9701-01922f8ac53d.png)
CriWareタブのAtomConfigを今回インポートしたacfファイルに設定します。
![B04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/6830208b-f85e-95cf-a2e0-5e6b8b69f778.png)


### サウンドの再生

ひとまず、通常の再生を試してみましょう。
レベル上にインポートしたキューをドラッグアンドドロップして配置します。
![B05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/a26c26ba-2e7f-7631-81db-24d185320886.png)
環境音ではない、効果音のキューに対しては自動で再生されないようDetailsパネルにて「Auto Activate」のチェックを外します。
![B06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/d2fd5e67-95b5-e591-b52e-d0b49c9498b2.png)
レベルブループリントを開きます。
![B07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/7daf7847-56e6-29e6-cdf8-462ce69d3678.png)
自動で再生されないよう設定した、効果音のキューのリファレンスノードを配置します。
（レベル上でキューを選択しておき、ブループリントグラフ上で右クリック→「Create a Reference to 『キュー名』」)
![B08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/86a7371c-1133-1362-91e1-68f0ed676960.png)
「1」キーを押したら**Play**ノードで再生されるようにし、ゲームを再生してテスト再生してみましょう。
![B09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/d9bd82a6-8ed7-61f7-8143-56192fe87826.png)


### 水深の取得
深さをシミュレートするために、スロープのある簡単なレベルを作成しました。
![B10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/13e8e675-23bc-7060-f707-38d32f37cc26.png)
レベルブループリントのグラフにて、新規にfloat型変数「SurfaceZ」を作成します。
![C01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/6d68ebb2-482a-ef8a-20f7-4d11b23242eb.png)
水面のメッシュのZ座標を初期値として入力します。
![C02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/18f6f5c2-5a57-25fe-a9d3-637d7aca6ec9.png)
Tickイベントで処理を行います。
![C03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/4001fe4b-7b5c-c176-0270-146cdc0b7a42.png)
水深を算出するために、プレイヤーの座標を取得します。
**Get Player Character**→**Get Actor Location**ノードを使用します。
![C04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/1296c9e8-c263-6be5-7ba5-fb69754fb682.png)
**Get Actor Location**ノードの黄色いピンを右クリックし、「Split Struct Pin」を選択すると……
![C05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/aa496bb4-f7df-6a3f-fa3f-761ec002161d.png)
ノードのアウトプット（vector情報）が分割され、Z座標が個別で取得できるようになります。
![C06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/767da4a7-5697-a98d-35db-50f6b0f1658c.png)
水面のZ座標とプレイヤーのZ座標を比較します。
![C07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/54ad00b5-1897-637e-f76f-3a08a14cb792.png)
**Print String**ノードで現在の差分（＝水面からの距離）を文字として出力します。
**Print String**ノードのDurationは「0.0」としておくと、ログがたくさん出続けるのを防ぐことができます。
![C08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/22b990d4-4637-e643-1a7f-340467e248dd.png)
画面左上に水深が出るようになりました。
しかしこれはUnit単位の表示なので、もう少し分かりやすくしてみましょう。
![C09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/964705d9-1e8b-5706-4974-124e8acdbf6c.png)
1unitは1cmに相当するので、「0.01」をかけて1/100にし、さらに**Round**ノードで整数に変換します。
![C10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/2d1965b6-1bc2-7835-115d-e31834870dbe.png)
メートル単位で水深が表示されるようになりました。
![C11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/35246e76-88b4-6fc6-ae6d-4f2361818aa5.png)



### 水深に応じたサウンド変化

取得できた水深の情報を元に、サウンドを変化させていきます。
ノードを整理するため、**Sequence**ノードをはさみます。
![C12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/21572beb-429f-b071-93ef-ca1115660767.png)
float情報に対して別の値を出力するため、**Map Range Clamped**ノードを使用します。
水深は（unit単位で）「0.0～1400.0」とし、それに対して「0.0～1.0」の値を出力します。
これがAISACコントロールに渡す値となります。
![C13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/30ee84ac-8196-d08b-011b-fe8a077c83e6.png)
得られた値を**Set Aisac Control by Name**ノードに渡します。
カテゴリ名とAISACコントロール名も忘れずに設定します。
![C14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/fcf4b079-ffc4-bac8-63b1-230245114e08.png)
ふたつのAISACコントロールの値を変化させます。
![C15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/1c80bc7f-5774-1923-07bf-0a63b404ff3a.png)
これで浅い場所では音がクリアめに、深い場所では鈍くなる演出が実装できました！
![C16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/f1ceee72-aa86-6a13-c9f2-510027f0ff7a.png)

水中では必要に応じて、衝撃音などの再生時に泡の音を同時に再生してみるなどしてみると、臨場感が増して面白いかもしれません。

## 補足
AtomCraftにて、複数のグラフをひとつのAISACコントロールにまとめることも可能です。
「AISACリスト」にて、**AISACコントロールを選択した状態で**右クリックし、「グラフの作成」→（使用したいグラフ）を選択します。
![D01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/4b8f804b-9141-09ab-d892-a104ce949164.png)
AISACリストにグラフが追加され、異なる種類の変化をひとつのAISACコントロールで済ませられるようになります。
![D02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/d24434c4-73ce-0e08-c13b-671dae98f5eb.png)
エフェクトを別々にかけたい等の用途がない場合、こうしてまとめると便利になります。