ADX+UEで、動的マテリアルからアプローチするサウンド

# はじめに
アンリアルエンジンとサウンドミドルウェア「ADX for UE」を連携させてサウンドを再生する際、UEの機能である「マテリアル」からサウンドに影響を与えてみようという試みです。

マテリアルインスタンスのパラメータドリブンでAisacコントロールを行ったり、またその結果によってマテリアルインスタンスのパラメータに変化を及ぼしたりといったリアルタイム処理が可能です。
物理挙動をベースにした衝突音を加味したり、マテリアルをデザインするアーティストによるサウンド設計など面白い挙動が再現できるかもしれません。

この記事では基本的なマスターマテリアルやマテリアルインスタンスの作成と、パラメータの数値を取り出しAtomサウンドに変化を与えるところまでを扱います。


# 前提

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


# 実装

## マスターマテリアルを作成する

最終的にマテリアルから派生する「マテリアルインスタンス」からパラメータを取得することになりますが、その大元となるマテリアル、「マスターマテリアル」を作ってみることにしましょう。
「マスターマテリアル」は親、「マテリアルインスタンス」は子という認識をするとわかりやすそうです。

コンテンツブラウザで右クリックし、「Material」を作成します。
![A01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/09a5aeb3-66d2-088e-63f2-4c1b121bc955.png)
わかりやすい名前をつけます。
![A02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/a720497d-5e03-b9fd-7cd1-9ae042d00296.png)
ダブルクリックするとマテリアルエディタが開きます。
![A03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/d307d5dd-3677-f3b4-bec9-a63022102c35.png)
「T」キーを押しながら空いている場所をクリックすると**TextureSample**ノードが作られます。これは指定したテクスチャを取得・表示できるものです。「RGB」から「BaseColor」につなぎます。
![A04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/aab03466-fa0e-b2d3-84d9-b7a926558e0c.png)
左下のDetailsパネルでテクスチャを指定すると、マテリアルの表面に貼り付けられます。
![A05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/12bbdc09-ba35-a641-d6d8-24088bb23454.png)
「1」キー（テンキーではない方）を押しながらクリックすると、float型として扱える変数が出現します（一応**Constant**という名前がついています）。
![A06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/3a91e92e-8c01-db4f-b0f2-823213812330.png)
同じように**TextureSample**と変数を追加しつなげていきます。
次の画像のように構成すれば、ひとまず最低限の機能をもつマスターマテリアルができそうです。
下の**TextureSample**では「Normal」につなげていますので、ノーマルマップを指定します。
![A07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/92e3fc32-2942-dd2e-1f2a-28e5df952ccc.png)
ノードを右クリックして「Convert to Parameter」を選択すると……
![A08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/2b32ed41-652e-b05c-b524-c75deb9874be.png)
ノードは「パラメータ」化され、マテリアルの子であるマテリアルインスタンスで画像や数値をカスタマイズできるようになります。さらに、**ゲーム内で動的にパラメータを変化させることも可能**になります。
![A09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/f1a69612-2050-907c-1f42-4906699bf64d.png)
4つのノードすべてをパラメータにコンバートします。
![A10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/6d993330-3f9b-523d-d10c-629b7760e574.png)
**Constant**ノードについては、Detailsパネルでデフォルト値、スライダーの最小値最大値を設定できます。
![A11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/4a4a5aba-cf77-a519-e4ee-f0a56d3563f4.png)
これでとりあえずの完成です。「Apply」を押して保存します。
![A12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/336e4b6b-6668-3c6b-a119-a95c2c355c53.png)



## マテリアルインスタンスを作成する

マスターマテリアルからマテリアルインスタンスを作成します。
![B01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/c110ca89-ed72-0d22-e0f9-1e60044c498e.png)
マスターマテリアルの性質を受け継いだ子、マテリアルインスタンスが作られます。
マテリアルに「M_（マテリアル名）」と名前をつけたのに対し、こちらは「MI_（マテリアルインスタンス名）」と名前をつけます。
![B02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/ae7e8461-3f31-e448-199d-9c5e8c3fe08c.png)
ダブルクリックして開きます。
マスター側でパラメータ化した数値が表示されていますので、チェックをつけてスライダーを操作するとマテリアルインスタンスの質感が変わっていきます。
![B03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/db45a3a2-8c94-450d-533f-01cf3e73c0d0.png)
パラメータ化したテクスチャについても同様に、チェックを入れることで別の画像を指定して差し替えることができます。
![B04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/4516b46a-d95a-e21f-8b8f-27b6384b3350.png)


## マテリアルインスタンスのパラメータを取得し、衝突音を変化させる

### オブジェクト用アクターを作成する

アクターを新規に作成します。
コンテンツブラウザの適当な場所で右クリックし、「Blueprint Class」です。
![C01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/5ea079d6-7853-7ece-9246-9a6d6b5952b5.png)
親クラスは「Actor」を選択します。
![C02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/30a616b2-e2dc-0c11-a202-2d135cfe5213.png)
ダブルクリックして開き、「StaticMesh」コンポーネントを追加します。
![C03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/eec9c988-b736-feb8-2833-d970eabaa6da.png)
StaticMeshコンポーネントをこのアクターのベースとしたいので、ドラッグして一番上に持っていきます。
「DefaultSceneRoot」コンポーネントに被せるイメージです。
![C04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/e611d6bd-162c-84a6-14ca-a9a528989f16.png)
適当にメッシュを指定します。
![C05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/c0a80ba4-5f08-22d5-4c04-ed027dc98e85.png)
「Simulate physics」にチェックをつけます。これで物理挙動がシミュレートされるようになります。
![C06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/4cf889d5-bd66-1a9c-78e7-1593ab910d29.png)

### 衝突時にサウンドを再生する

イベントグラフに移動します。
物理挙動シミュレーションで衝突した時にイベントを発火するには、**Set Notify Rigid Body Collision**ノードで有効にしてあげる必要があります。
![C07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/b84ebdc6-22d6-a758-23c5-4bb933bdff34.png)
「StaticMesh」コンポーネントを右クリックし、「Add OnComponentHit」を選択。
![C08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/cd39bff4-a86c-8111-a554-048bef4e8ccd.png)
衝突時のイベントノードが現れます。
![C09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/ef4138cd-fb05-5621-85ed-14d1fe46bcfe.png)
今回は「衝突の強さが一定以上か判別し、衝突音を再生する」という方法をとることにしましょう。
「Normal Impulse」のベクター情報を**Vector Length**ノードに通すことによって大体の衝撃の強さが取得できます。
![C10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/e900923f-01db-9eb0-e560-2d55a0abf82a.png)
「Hit」アウトプットピンから線を伸ばし、**Break Hit Result**ノードで衝突結果から情報を取り出します。
![C11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/018d90d3-084c-1843-234c-12efb34ff4bd.png)
サウンドの再生には**Play Sound at Location**ノードを使用します。**同名のノードがありますが、必ず「Atom」カテゴリにあるものを使用**してください。
**Play Sound at Location**ノードで再生するAtomキューを指定します。
![C12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/c4f24a7d-8c65-2969-6e0d-e11d0654d5af.png)
次のようにノードをつなげば音が鳴るようになります。
![C13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/8b42d4c2-cca3-d06c-da01-22bda01b7cca.png)
レベルに配置してみましょう。落下時などの強い衝撃でサウンドが再生されるはずです。
![C14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/cffb3a1a-d066-ff08-53a7-78e8906b0382.png)


### パラメータを取得する

マテリアルインスタンスのパラメータを取得・操作するためには「ダイナミックマテリアルインスタンス」を作成・適用する必要があります。これはマテリアルインスタンスのさらにインスタンス（子）になるイメージです。
**Event BeginPlay**の処理のあとに、**Create Dynamic Material Instance**でダイナミックマテリアルインスタンスを作成します。
![D01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/b3477db8-5516-d334-8f78-a550234caa14.png)
次の画像のようにつなげば、現在のStaticMeshが持つマテリアル（もしくはマテリアルインスタンス）を元にダイナミックマテリアルインスタンスを作成できます。
![D02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/c2ac01a6-e9b7-79fc-9cdd-2f0fc85e6adc.png)
青いアウトプットピン「Return Value」を右クリックし「Promote to Variable」で変数化します。
![D03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/57448403-a610-9606-f116-44b0a117efe3.png)
適当に名前をつけます。
![D04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/4aca21cb-9341-d068-8d7c-f2c14e19cf71.png)
最後に、**Set Material**でStaticMeshのマテリアルをダイナミックマテリアルインスタンスで上書きすればOKです。
![D05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/1a817278-8c08-adcc-27a9-eb0168f4181c.png)

衝突時に、ダイナミックマテリアルインスタンスのパラメータを見てサウンドに変化を与えます。
AtomキューをAISACコントロールで操作したいので、**Play Sound at Location**から**Spawn Atom Sound at Location**にノードを差し替えます。
![E01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/50756602-8628-2383-bb1c-827526914aee.png)
再生が終わったら音源をレベルに残さないよう「Auto Destroy」にチェックがついていることを確認しておきましょう。
![E06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/0eb28f51-7e45-737e-3f85-6193f906941c.png)

ダイナミックマテリアルインスタンスから値を取得するには、**Get （型の名前） Parameter Value**ノード各種を使用します。パラメータ化したあらゆる値を取得できます。「Parameter Value」にはマテリアルインスタンスが持つパラメータ名を指定します。一致していないと正常に処理が行われませんので、よく確認しておきましょう。
また、このノードは通常の**Get**ノードと異なり白い線をつなぐ必要があります。取得タイミング（ノードが実行されたタイミング）での数値が返される点も留意しておく必要がありそうです。
![E02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/ee00297d-2e8e-ac5c-6a88-1d6a5c501341.png)
衝突時にこのオブジェクトが持つマテリアルの「Metalic」を取得します。**Get Scalar Parameter Value**は**Constant**ノードが持つFloat型変数を取得できます。
![E03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/daf400a1-6754-13d8-2114-3cd00efe00e4.png)
**Set Aisac by Name**でAISACコントロールに値を渡し変化を与えます。Metalicですので、数値が高いほど重い音（金属音）が響くようにAtomCraftで調整すると良さそうです。
![E04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/6c4a3452-0907-027e-cca3-7ed71d2c6faa.png)
また、「Roughness」の値を取得してさらに変化を与えることもできます。この場合は数値が高いほど乾いた音にするといいかもしれません。
![E05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/ed978b1c-2c2e-bda3-3305-e771c58abc73.png)



# 補足

## PhysicsMaterialから再生するキューを選択する

マテリアルインスタンスごとにPhysicsMaterialを指定できます。
![F01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/fcdc4473-008f-cf90-eef4-1367a12ac111.png)
PhysicsMaterialごと対応したサウンドを再生する方法は以下の記事にまとめてありますので、興味があれば参照してみてください。

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