ADX+UE5で、キャラクターのボーン単位でサウンドを付加する

# はじめに
アンリアルエンジンとサウンドミドルウェア「ADX for UE」を連携させ、「**キャラクターのボーンにサウンドを付加する**」実装を行ってみる記事です。

等身大のキャラクターの場合は効果音などはアクターやメッシュに追従させれば自然に聞こえますが、巨大なキャラクターや一人称視点のキャラクターの場合、音が発声されている部位に正確に音源を設置しなければ違和感が出てきてしまいます。
この記事ではボーンやソケットといった単位にサウンドを付加し、キャラクターの動きや位置に合わせた音作りをしていきます。


# 前提

当記事では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


# 実装

## UE5でサウンドをキャラクターに追従させる

### サウンドの用意

今回はキャラクターのボーンやソケットにサウンドが追従していることが分かるよう、ループするキュー（サウンド）を用意しました。もし「ADX for UE」で使用するキューが用意できていない場合、上記の「導入編」記事を参考にして作成してみてください。


### キャラクターにソケットを追加する

直接ボーンを指定してサウンドを付加してもいいのですが、UEには「**ソケット**」という便利な機能があります。これは基本的にボーン上に追加するもので、本来の位置から座標をずらしたりスケールを編集したり……という「追加ボーン」的な役割を果たします。サウンドだけでなく、当たり判定やエフェクトの付加を行いたい際にも大いに役に立ちます。

たとえば、キャラクターの頭に「ツノ」を追加したい場合はメッシュをHead（頭）ボーンに追従させますが、ツノのメッシュがおでこにくるように座標を手打ちで調整しなければなりません。
対して、ソケットは**視覚的に位置や回転を編集できる**ので、「ツノ用のソケット」を用意すれば、メッシュをソケットに追従させるだけで実現可能となります。
![A00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/47e04ca7-3f81-7754-0853-4d1356b3d774.png)

ソケットを追加するため、キャラクターのスケルタルメッシュを編集します。
コンテンツブラウザからキャラクターのメッシュをダブルクリックして開きます。
![A01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/bb18a620-9646-baaf-7823-56ae5ad2dbe7.png)

右上のボタンから、ボーンの編集に移ります。新しいウィンドウが開きます。
![A02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/bfb2dd6e-c2e1-773d-525e-58ea3ab8adff.png)
左部でボーンを選択すると、対応した部位がハイライトされます。
たとえば「index_03_l」では、左手の人差し指の先端が選択されることになります。
![A03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/481df364-ceea-4869-5ebb-ddf3c5b06973.png)
ボーンを右クリックし、「**Add Socket**」を選択してソケットを追加してみましょう。
![A04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/714c3a66-903b-a883-ad89-9af608c934c2.png)
ソケットを選択してF2キーでソケット名が変更できます。
![A05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/46d60fe6-0cbc-b5c1-290b-faa978f6bf0b.png)
これで人差し指の先にソケット「Socket_Index」が追加できました。
![A06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/75c5d3c4-b76e-198b-7cfa-9908ba7d303b.png)
ソケットはマニピュレータを直接操作して位置を調整できます。
さらにマニピュレータをドラッグし、人差し指のさらに先端にソケットを移動させました。
![A07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/17d5d63d-bddf-24f4-fbdc-5a6be3ff5945.png)


### ソケットにサウンドを追従させる

ソケットへの追従処理を簡易に実装してみましょう。
レベルブループリントを開きます。
![B00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/3f65d5b9-4913-cdfb-a789-5f1f5670743d.png)
キーボードの「1」キーを押したら、**Spawn Atom Sound at Location**でサウンドがスポーンされる処理を作ります。
座標などはそのままで大丈夫です。
![D00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/118cfc0d-f39e-deb5-2570-6ceee191f837.png)

この状態でゲームを再生し、音源の位置を視覚化するコンソールコマンド「cri.ShowSoundLocaiton 1」をコンソールに入力します。
![B04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/d7610011-7163-5cea-21cd-c1997beb40b8.png)
音源は座標「0.0, 0.0, 0.0」の位置にスポーンされています。ステージの外ですね。
![B05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/6674de2e-6a1f-c528-000c-df19dc305626.png)

レベルブループリントに戻ります。
今回は「プレイヤーキャラクターのボーンを取得し、人差し指のソケットに音源を追従」させます。
Atom Cueをボーンやソケットに追従させるには、**Spawn Atom Sound Attached**ノードが便利です。
![D01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/828ddc03-3128-a3e9-f660-cc6297e223ee.png)
対象ボーンやソケットを指定しなくても、コンポーネント単位での追従にも使用できます。

このノードを使う前に、まずは**Get Player Character**でプレイヤーキャラを取得し、**Cast To BP_ThirdPersonCharacter（アクター名）**ノードでクラスを照合し、コンポーネント等にアクセスできるようにします。
![D02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/0fa9bae5-af58-6389-6d22-7796e1e8470c.png)

**Cast**のアウトプットピンから**Get Mesh**で、キャラクターのスケルタルメッシュを取得します。
サードパーソンテンプレートのデフォルトキャラクターのスケルタルメッシュの名前が「Mesh」となっているためこのノード名になっていますが、
デフォルトのキャラクターではない場合、予めキャラクターのコンポーネントを確認しスケルタルメッシュの名前を把握しておくといいでしょう。
![D03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/af56fe96-e9c3-e80f-1397-09104fb598f4.png)

**Spawn Atom Sound Attached**ノードでサウンドをメッシュのソケットに追従させます。
「Attach to Comopnent」にはスケルタルメッシュの**Get**ノードをつなげます。
![D04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/62ef3f68-b376-0df5-2382-067b0e938b40.png)

**Spawn Atom Sound Attached**ノードの下部をクリックして展開します。
![D05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/09ff1f70-a64f-8aab-d812-1163697d3af7.png)

「Attach Point Name」に追従対象となるソケット名（あるいはボーン名）を入力し、
「Location Type」はきっちり追従させたいため「**Snap to Target, Keep World Scale**」（座標と回転を完全に追従させ、スケールはそのまま）にしておきます。
![D06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/3a3a317f-1701-63ae-cf61-97ed2d5f6a50.png)

ゲームを実行して確認してみましょう。まさにソケットを追加した位置にサウンドが追従するようになりました！
もちろん、キャラクターが移動したりアニメーションを再生してもソケットの位置にサウンドが常にくっついていきます。
![B10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/63ed11d5-c3f6-b77d-30cc-a754e2d1e088.png)

**Spawn Atom Sound Attached**ノードでスポーンしたキューは、青いアウトプットピン「Return Value」から再アクセスが可能です。
このピンを右クリックして「Promote to Variable」で変数化します。
![D07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/2b052907-8a1c-45eb-f3bb-5d36162c37e0.png)
変数に名前をつけておくと、後から各種操作を行うことができます。
![D08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/fc719c49-aeaf-41c2-fc7a-7888b532a7e8.png)
例えば、**Fade Out**ノードなどで追従させたサウンドをフェードアウトさせる、などといった用途に有用です。
![D09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/ba2e4848-95ec-9a09-77d9-3fd0e574c42a.png)




もしサウンドがコンポーネント単位ではなく、アクター単位で追従させたい場合は**Attach Actor To Component**ノードを使う必要があります。
と言っても「ノードがつながれば使える」ため、深く考えず使用できるノードを使用すればOKです。
![B11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/60315f17-013a-c28a-b63e-074071424f6a.png)




### ランダムなボーンやソケットにサウンドを追従させる

補足として、キャラクターのランダムなボーンにサウンドを追従させる処理です。
これは「巨大なメカが駆動したり、軋む音」などのシチュエーションに使うことができるかもしれません。

スケルタルメッシュを取得し、**Get All Socket Names**ノードですべてのボーン及びソケット名が取得できます。
![C01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/36cf7e7a-5bb2-b450-2e15-7660da9bac97.png)
ランダムなインデックスを無作為に選ぶため、乱数の最大値として**Length**ノードでボーン及びソケットの総数を取得します。
![C02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/08670e91-b3ba-bffc-b9b9-54084cde815a.png)
**Random Integer**でランダムにインデックスを選びます。
![C03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/7c4cdaa6-eb17-c9cb-83df-e6230978b2f6.png)
あとは先ほどと同じように、**Spawn Atom Sound Attached**ノードでサウンドを追従させます。
線のつなぎ忘れに注意してください。
![E01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/dcbb042b-1219-6234-d576-f9fd24599d5c.png)


今度はキーボードの「1」キーを押す度に、ランダムなボーンやソケットにサウンドが追従するようになります。
![C05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/02ac892f-a7b0-b830-357a-fac3749bbd8d.png)