ADX+UE5で、キャラクターボイスに距離に応じたエフェクトをかける

# はじめに
アンリアルエンジンとサウンドミドルウェア「ADX for UE」を連携させ、キャラクターボイスに距離に応じたエフェクトをかける実装方法です。
シングルプレイのゲームにおけるNPCの台詞や、マルチプレイのゲームにおけるラジオチャットなど、ゲームにおいてキャラクターが声を発する場面において、
- 近距離では普通に声が聞こえる
- 遠距離では無線通信のように声が聞こえ、ノイズが乗る
- さらに距離が離れると声がくぐもっていき、聞こえづらくなる

といった演出を実現します。



3つめの要素を省けば、ゲームのレベル上のどこにいてもキャラクター同士が通信可能なシチュエーションができあがるでしょう。


# 前提

当記事では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でボイスとノイズを構成する

まずはAtomCraftを使い、キャラクターボイスとノイズのサウンドを登録します。

### マテリアルのインポート
マテリアル（音声素材）をインポートします。
次の画像では、ボイスとノイズでマテリアルフォルダーを分別しています。
![A01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/9f112759-b7b6-aabb-6bb2-29d12a76efdf.png)


### キューの作成

インポートしたマテリアルをキューシートにドラッグ＆ドロップし、そのままキューを作成します。
![A02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/6f0bcbf1-72c2-c51e-132c-d9c2facfa24a.png)
それぞれのマテリアルについてキューを作成します。
ここも必要に応じてキューシートを分けても構いません。
![A03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/8382c076-4886-b4f9-61e1-1334ac0c29aa.png)
ノイズは無線通信中常に再生しておきたいので、**マテリアルを選択した状態で**インスペクターにて、ループ情報の上書きを「True」、ループタイプを「ループ」に設定しておきます。
![A04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/9a7fccd3-f99b-fe47-8317-ea4a0c1c7a6c.png)


### カテゴリの作成

ゲーム中では声がくぐもるエフェクトをキャラクターボイスのみにかけたいので、「カテゴリ」機能を使用します。
プロジェクトツリーの「カテゴリ」を展開し、任意のカテゴリグループを右クリックし「新規オブジェクト→カテゴリの作成」を選択します。
![A05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/828152d1-83d2-5702-1ff3-9237df406711.png)
「Voice」という名前のカテゴリを作成しました。
![A06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/31484986-74fb-ebcd-a9dd-6048a1694d09.png)
キャラクターボイスのキューを選択し、インスペクターのカテゴリ編集ボタンを押します。
![A07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/d5c08680-bd36-f877-3d45-8e8215a4d555.png)
「Voice」にチェックをつけてOKボタンを押します。
![A07b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/ea671129-d87d-91be-47f3-4c78cd31a8bb.png)


### Aisacコントロールの設定

エフェクトをかけるため「Aisacコントロール」機能を使用します。
複数のキューにAisacコントロールを適用するため、「グローバルAISAC」を作成します。
プロジェクトツリーの「グローバルAISAC」を右クリックし、「新規オブジェクト」→「AISACの作成」を選択します。
![A08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/7f629e07-ebd4-cfca-7f31-0e6310a3a510.png)
AISACグラフタイプを「バンドパス - Cof高域」を選択します。高域をカットして音声をくぐもらせる効果があります。
![A09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/54510df1-3080-8efb-1f55-bef5b99e2489.png)
線形のシンプルなグラフを作成します。AISAC値は最小でも「0.1」以上にしておきます。
![A10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/0860968b-4902-3367-5473-db29b0186c4f.png)
グローバルAISACはキューにドラッグ&ドロップすることで「リンク」され、プレビューなどが機能するようになります。
![A11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/962e36e0-58c5-d0dd-7b52-9ce2e1a65256.png)
リンクした状態でキューを再生し、AISACコントロールのスライダーを操作するとエフェクトがかかり、音がくぐもって聞こえるはずです。
![A12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/3b990252-8e3f-1643-eb45-92bd5cbcce5b.png)

Aisacコントロールの「デフォルトコントロール値」にチェックをつけておきます。
![A15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/c529188c-7996-795c-edf0-df6dd72e5bf7.png)




### キューシートのビルド
ここまでできたらキューシートをビルドします。
![A13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/0f4ea7c0-5573-0725-e4f7-8e41980fd3ec.png)
![A14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/bcb44ecc-5451-fa07-368d-a583d5d53875.png)


## UE5でボイス演出を実装する


### キューシートのインポート

ビルドしたacbファイル、acfファイルをコンテンツブラウザにインポートします。
![B01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/6be48515-0638-b20f-31d3-d667fbeda67e.png)
![B02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/cdfc7e6b-5081-b86a-ad82-301874f3c23b.png)
プロジェクト設定を開き、
![B03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/71f8406f-cd33-afc8-ee6f-70e7027af1f5.png)
CriWareタブのAtomConfigにインポートしたacfファイルを設定します。
![B04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/24949f35-9f59-b8c4-3175-08c5f969433a.png)

テスト用のキャラクターをレベル上に配置します。
![B05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/66664bbe-14ee-29f9-173c-c52164e62c2b.png)
場所が分かればいいので、アニメーションするスケルタルメッシュを置けばOKです。
![B06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/bcb6a65d-1d86-65f2-7da3-e82e2c86b58f.png)



### ボイス用BPを用意する

ボイスを再生するための簡単なブループリントアクターを作成します。
コンテンツブラウザを右クリックし、「Blueprint Class」を選択します。
![B07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/dd4cb5e5-ac3b-6018-776c-94bc58900166.png)
親クラスは「Actor」にします。
![B08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/5eb4cc4c-67e6-58ea-017d-8cbc0f806474.png)
アクターを開き、Atomコンポーネントを追加します。
![B08b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/ede9b8ff-1169-8863-e330-591d69a1c8e7.png)
再生するサウンドを指定します。
これは初期値として、ランタイムでサウンドを指定して喋らせていくといいでしょう。
![B09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/1b2e5ea1-b8fc-cbab-5f74-3f0f41ccd825.png)
テスト用に、DetailsパネルでLoop Settingsを「Loop」に設定します。
![B10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/6d2e8344-792c-2d6a-807c-c4a0075594d4.png)

アクターをレベルに配置し、キャラクターの口元に移動させます。
![B11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/34515479-e874-7bc9-1e56-5289093229f0.png)
ゲームを再生し、聞こえ方を確認します。
![B12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/4409d273-a0b7-3cb8-e0ae-b2c4ca40d427.png)

### ボイスの距離減衰を設定する

距離減衰用の設定データを作成します。
コンテンツブラウザを右クリックし、「Sounds」→「Sound Attenuation」を選択します。
![C01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/5448e295-5266-93bb-7a40-0ed1dffba920.png)
用途に応じて作っておくといいでしょう。距離減衰が適用されればいいので、今回は特に設定をいじりません。
![C02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/19f4a9f3-fa15-49cc-6a11-854d19ac6f5c.png)


AtomコンポーネントのAttenuation Settingsに作成したファイルを指定します。
![C05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/7576ba09-d8dc-59b0-bdda-89cb623dcfeb.png)
キャラクターの周りを移動すると、距離減衰がかかっているのがわかると思います。
![C06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/d3320549-6e5a-e1b5-ff13-f829948ffda3.png)


### 一定距離以上離れた場合にノイズをかける

キャラクター同士の距離を監視し、一定距離以上離れた際にモードを切り替えノイズ音を同時に再生します。
ノイズ用のAtomコンポーネントを追加します。
![C07pre_a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/310ad732-5c5a-7fce-93c7-73f6d0bdfd4b.png)
ノイズ音のキューを指定します。
![C07pre_b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/ff54aed0-2ba2-d02e-9ca7-f178d582e89e.png)
自動的に再生されないよう、「Auto Activate」のチェックをオフにしておきます。
![C07pre_c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/49858e41-7347-6d73-0e20-6626e2d5e490.png)

イベントグラフに移動し、変数を追加します。
- 「**bVoiceEffect**」: Bool型です。エフェクトをかける（＝一定以上離れたモード）かどうかを判別します。
- 「**ThresholdDistance**」: Float型です。どれくらい距離が離れたらエフェクトをかけるかの値です。
![C07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/7498bd72-a712-acf4-7e18-b3effeea33f7.png)
「**ThresholdDistance**」の初期値を指定します。ここでは「1000」としました。
![C08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/aab20e19-7209-899c-7157-1dc6e2a4acc2.png)

**Event Tick**から処理を開始します。
まずはエフェクトをかけているかどうかで距離を分岐します。
![C09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/f768db17-a815-8581-58f0-2de01f55035c.png)
「False」側ではまず、距離が一定以上離れているかを判別します。
![C10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/4a546279-18fc-e56f-c325-385324f89218.png)
離れていれば「bVoiceEffect」をTrueにし、ノイズ音を再生します。
![C11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/125e0e5b-eec6-54e6-28fd-5c223155c9f5.png)
「bVoiceEffect」がTrueの場合は逆に距離が近いかどうかを判別します。
![C12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/9614515f-37c8-6e48-df60-c616762ee928.png)
近ければ「bVoiceEffect」をFalseにし、ノイズ音を停止します。
![C13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/625fb569-83bc-a710-675b-f79192a2fae0.png)

イベントグラフの全体図はこんな感じです。
![C13b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/6e90252c-0026-db10-010a-f962b9f8266f.png)

この状態でゲームを再生し、キャラクター同士が離れると声にノイズがかかり、近づくとノイズが止みます。


### 距離に応じて声にエフェクトをかける

Aisacコントロールを使用し、距離が離れるごとに徐々に声が聞こえづらくなる実装をしていきます。
「VoiceEffect」がTrue、つまりキャラクター同士が離れている状態で、**Branch**ノードがFalse、つまり距離が離れているままの場合**Set Aisac Control by Name**ノードを使用してAisacコントロールを行います。
![C14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/a819c358-f94c-7540-490f-470951b7fc9d.png)
距離を取得し、「距離が10000ユニット以上離れるとエフェクトが最大になる」ようにします。
「距離1000～10000ユニット」を「Aisacコントロール値0.0～1.0」に変換するのに有用なのが**Map Range Clamped**ノードです。
「Value」に代入された値を「In RangeA～B」から「Out RangeA～B」の数値のスケールに置き換えた値が返されます。

![C17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/5b65a101-034a-3cb4-c185-b96bc92121cb.png)
この値を**Set Aisac Control by Name**ノードの「Value」につなげれば距離に応じたAisacコントロール値が得られます


キャラクターのボイスがどこにいても距離減衰なく聞こえる場合、ゲーム的な都合ですが違和感を覚えてしまうことがあります。軽い演出を加えれば「このキャラクターは通信手段があるんだな」という理由付けができ、違和感を解消できるでしょう。ゲーム性や設定に応じて調整し、実装してみてください。