UE5+ADXで、ゲーム内UIのカーソル音に凝ってみる（改訂版）

# はじめに
ADXアンバサダーとして記事を書いておりますSigと申します。
この記事ではアンリアルエンジン5とサウンドミドルウェア「ADX for UE」を使い、UI（UMG）上のカーソル操作に伴うサウンドを再生する実装例を紹介します。
ボタンやキーの入力を取得してカーソルを操作する際、順送りか逆送り、またメニューの先端でのループ時にサウンドを変化させ、聴覚からのフィードバックを多彩にします。




# 前提

UE5.4+「ADX LE UE SDK(2.03.00)」を使用します（他バージョンにおいても、基本的に実装方法は同じです）。
基本的にブループリントのみでの実装を想定しています。
ADXはインディー向けの「LE版」であれば、無料で使用できます。
https://game.criware.jp/products/adx-le/

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


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

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

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

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



# 実装

## AtomCraftでサウンドバリエーションを作成する

UI上で再生するためのサウンドをAtomCraftで設定します。
ひとつだけサウンドを用意し、AISACコントロールによりピッチを変更して音を変化させます。
![A01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/402115bd-4bd1-3af8-6826-1ac433d432a3.png)



### AISACコントロールを追加する

まずはピッチを調整するためのAISACコントロールを追加します。
プロジェクトツリーの「AISACコントロール」のフォルダ内で未使用の「AisacControl」を選択し、F2キーでリネームします。
![A02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/ca897fe1-1606-bc50-08d7-960a00f95eeb.png)

今回は名前を「ButtonPitch」としました。
![A03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/2a0cc4c9-158d-7506-a49a-16b4e55b64c1.png)



### キューを作成する

カーソル音用のキューを作成します。キューのタイプは用途に合わせて変更してください。今回は単純に「ポリフォニック」で作成しました。
![A04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/74d5cb88-294a-66db-9a98-d490611cff8d.png)

キュー内にマテリアルをドラッグ・アンド・ドロップし、サウンドが再生されるようにします。
![A05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/45f8abf0-1d1c-41d9-7ca1-438f5dd18b2a.png)



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

キューにAISACコントロールを設定していきます。
**キューを**右クリックし、「新規オブジェクト」→「AISACの作成」を選択します。
![A06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/987ae928-63f5-51c0-a3db-3fbc9656e463.png)

AISACの追加ダイアログではグラフタイプを「ピッチ」に変更します。
![A07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/161a0657-413a-462d-10df-16b266bb0eef.png)

出現した「AISACリスト」の「ピッチ」を選択するとグラフが操作可能になります。
![A08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/0aa26ef5-c27a-ea83-85eb-cf2a60dead2b.png)

まずはグラフ上を適当にクリックして4つのコントロールポイントを追加し……
![A09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/1f7c1aa6-474f-8996-ae79-2ca57cc6f2b1.png)

右のインスペクターをタブ切り替えで「ポイントリスト」表示にし、パネルで数値を入力して微調整するといいでしょう。
0～0.3では低いピッチ（カーソル順送り）、0.301～0.7では通常のピッチ（汎用音）、それ以上であれば高いピッチ（カーソル逆送り）としました。
![A10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/23b39e84-aadb-d635-e045-1fd78f786043.png)

さらに端にコントロールポイントを追加し、カーソルが端でループした際の変化を追加します。
例ではさらに低いピッチとしました。
![A11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/4969349e-1d87-e3ce-0252-c6d26b00d626.png)

キューを再生しながら、上部のスライダーでAisacコントロールの動作を試してみましょう。段階的にピッチが変化しているでしょうか？
![A12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/6b8a4964-67d1-fba5-32d3-bc7fde969643.png)

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

ここまでできたらキューシートをビルドします。

![A13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/c9107424-02b9-ab22-3604-fd288f614480.png)
![A14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/7e5614d3-7769-222e-1357-c6fa16f2570e.png)

これでAtomCraftでの工程は完了ですので、UEに移りましょう。



## UEでの実装

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

ビルドしたキューシートをインポートします。
コンテンツブラウザ（コンテンツドロワー）の適当な場所にacf, acbファイルをドラッグ&ドロップしてインポートします。
その際のダイアログでは「Yes」を選択します。
![B01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/f6398e37-9dd6-2b27-8799-0431b1bbae0d.png)
2種類のアセットが作られました。
![B02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/2bbb78ce-51a0-aa28-fb5d-60ed3167d079.png)
キューシートを開き、キューが再生できることを確認します。
![B03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/3102e0ab-c8e3-3053-984a-a1c3404ed914.png)



### キー・ボタン入力を登録する

UE5の新しい入力システム「EnhancedInput」を使用する、ボタン入力を登録して使用します。

![B04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/0b427312-fa31-a05c-b0b5-eb1afb893c10.png)
![B05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/8eb10811-ab4e-956b-adad-19ab232bcd55.png)
![B06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/5d396c67-3f54-4309-02e3-ce0236108dbd.png)
![B07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/18a14b03-0ef4-1875-5215-2b3b3701f64e.png)
![B08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/fd2c558c-85fa-e502-eed5-cd49d78fc485.png)
![B09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/c67a62c6-38a4-6020-ca35-b10b8afdb103.png)
![B10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/5314337a-b193-3788-281e-f376f6d436fd.png)
![B11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/9f08f240-5f92-febf-e228-7543b19dcb9e.png)
![B12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/23cbf178-ea80-a575-3143-250b97a9712f.png)
![B13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/91acb919-896d-edbc-aafa-49dc213fe0e0.png)


### UIを準備する

操作するためのUIを準備します。

コンテンツブラウザ（コンテンツドロワー）の適当な場所で右クリックし、「User Interface」→「Widget Blueprint」を選択します。
![C01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/2841a328-ca6d-b33f-2200-1b0b038f9df5.png)
親クラスは「User Widget」を選択します。
![C02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/50b3e3b1-d658-a524-b98f-8b98dae57cac.png)
名前をつけ、ダブルクリックして開きます。
![C03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/4ac2985c-7fb3-4379-93af-a08b5139ff6b.png)

メニューが並んでいる簡単なサンプルを用意しました。
「Canvas」を下地に、「Image」「Text」を使用して作っています。
![C04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/bb2c3bee-06bb-e1fa-2daf-ffeca365bbd7.png)

テキスト「メニューA」の場所にあるシアンのバーがカーソルとなり、キー入力に合わせて動く想定です。


### ウィジェットの入力処理

UI側に入力を反映させる処理を書いていきます。ここの処理はいろいろな方法があり、プロジェクトによっても変わってくるところなので、ここでは簡単に試せる処理を書いていきます。
この記事では最終的に「レベルブループリントで入力処理を受け取り、UIにカスタムイベントとして伝達する」手段をとります。

UMGからウィジェットのグラフに移動します。
![D01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/3b3031d5-b562-0769-6030-2cba86db10a6.png)

新規にカスタムイベントをひとつ追加します。
![D02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/abace8bb-95cc-a7da-935d-c26e5ebab425.png)

Detailsパネルでカスタムイベントにパラメータを追加します。
![D03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/10ee9818-bd4c-49c9-f208-ad0983ffc434.png)

パラメータのタイプを「Integer」にし、適当な名前をつけます。これがカーソルの移動量になります。
![D04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/1f7443e6-fc19-6310-4d0d-c1e766e2536e.png)

ふたつのInteger型変数を追加し、現在のカーソル位置と項目の最大数とします。
![D05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/4a9dc5c3-6c6c-a2e9-7826-ae1784f0d36c.png)
変数「MaxCursor」を選択し、「Default Value」を「3」にしておきます。
選択肢が4つのため、カーソルの値は「0～3」で変動する想定です。
![D05b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/d33aba56-b712-2528-0c3e-f052b0231b91.png)

カスタムイベント「CursorMove」の入力を受け取り、カーソルの現在値に加算します。
![D06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/cf3d745b-d723-506c-54dd-854494296a1e.png)

**Clamp**ノードを噛ませると簡単に最小・最大数に制限がかけられて便利です。
![D07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/5049cf6c-adf6-dc73-bf20-52dfd05fcfb9.png)

**Event Tick**から、カーソル画像の表示位置をカーソルの現在値に合わせてずらす処理を追加します。
![D08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/daad46e0-637e-7f7e-5257-0e8a1d61e8b4.png)


**レベルブループリント**を開きます。
**Event BeginPlay**でウィジェットを追加します。
![E01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/17294f9a-45c6-b1de-f688-ab51be6136f8.png)

追加したウィジェットのアウトプットピンを右クリックし、「Promote to Variable」で変数として格納します。
![E02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/ada9c2dc-6b06-c29e-7362-c074d0c11a67.png)
変数に適当な名前をつけます。
![E03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/5b2335d0-67ef-16d5-abf0-7b58469fe691.png)

先ほどプロジェクトに登録したカーソル入力を取得し、上であれば「-1」、下であれば「+1」をウィジェットのカスタムイベント「Cursor Move」に渡します。
![E04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/2b8c158d-7b19-6cda-98a6-250a4c51fa2c.png)



UIが表示され、キー入力に合わせてカーソルが動くようになりました。
![E05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/e368b7bd-eda1-5811-d0cd-efda5f15e8ad.png)


## サウンドを再生する

実装したカーソル移動に合わせてサウンドを再生します。



### カーソル音を再生する

ウィジェットのカスタムイベント「CursorMove」の処理の最後に**Spawn Sound 2D**ノードを追加し、カーソル移動音を指定します。
同名のノードがありますが、「Atom」カテゴリのものを使用します。
![F01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/5fcbc2ac-1797-2806-13be-728133d88a7c.png)
再生するキューシートを選択し、キューを選択します。
![F02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/1f0705ba-8348-7adc-d34e-86efbde9a96c.png)
AISACコントロールを操作してカーソル音のピッチを変化させます。**Set Aisac Control Value**ノードを使用します。

![F03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/80e3ca45-abad-f61f-3012-f7b3964cb51c.png)

入力の値はカーソルの移動方向によって変わるので、**Select**ノードを使います。
少しややこしいのですが、**Select**ノードは入力(0～n)の値によって異なる出力を設定できます。
インデックス番号が0～になるので、カーソル移動量に+1をし、その値に合わせてインデックスを設定していきましょう。
難しければ次の画像の通りにノードを組んでみてください。
![F04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/5d9e67e5-1c4c-5b2b-bf15-47bae51d5d3e.png)




これで、カーソル操作時に移動方向によって音が変化するはずです。


### カーソルがループした際のサウンドを変化させる

カーソルが一番上もしくは下に来た際、カーソル位置を反対方向にループさせる処理です。
「CursorMove」の処理を大幅にリフォームします。
どちらにもループしなかった場合、通常のカーソル音処理を付け足して完成です。
![G01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/ac939f64-e735-0528-00b1-d6b8bcf0e973.png)