ADX+UE5で作る、ゲーム中会話パートのボイス再生機能（追加機能編）

# はじめに
ADXアンバサダーとして記事を書いておりますSigと申します。
この記事ではアンリアルエンジンとサウンドミドルウェア「ADX for UE」を連携させ、ゲーム中の会話パートにおけるボイスの再生及び再生待ち機能などを実装してみます。

普通にボイスを再生するほか、ボイスを再生し終わったらページ送りを促すアイコンの表示や、オートで会話を送っていく機能を追加します。

実装は基本機能編に処理を足していきますので、まずはそちらの記事をご覧ください。
https://qiita.com/SigRem/items/4ddb00da98062021344b

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

# 前提
「ADX for UE LE」を使用します。導入や簡単な使い方は以下の記事にあります。
ADX for UEの導入で、一歩上のサウンド表現を（導入編）

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

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

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

# 実装

### メッセージ、ボイスを登録しておき連続で再生する

複数のメッセージとそれに対応するボイスを変数に記録しておき、プレイヤーが決定ボタンを押した際に次のメッセージ、ボイスを再生する機能です。

「再生リスト」としてメッセージ、ボイスをそれぞれ変数配列に格納し、ボタンを押すごとに次の配列を参照していく実装になります。
![A00_a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/c558ce03-0730-4d59-98a2-f9f1ab1d6f1b.png)

専用の構造体（Structure）を作成します。これは複数の変数をひとつにまとめておけるものです。
「1メッセージ」という単位にテキストとボイスをまとめてしまいましょう。
コンテンツブラウザの適当なところで右クリックし、「Blueprint」→「Structure」を選択します。
![A02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/fb8f5f8a-1ff1-484b-8376-e87c4a9fe62b.png)

適当に名前をつけます。
![A03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/25e27d95-6a73-445f-87c1-d4b409a29213.png)

ダブルクリックすると、構造体を編集するウィンドウが開きます。
![A04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/012ae7e5-a0cd-4ac7-86c0-236be093ccdf.png)

「+Add Variable」ボタンを押して、構造体に変数を追加します。
![A05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/5dead1e8-7fa0-4f94-a980-8aa0f49c8123.png)

ふたつの変数の型を、それぞれ「**Text**」「**Sound Atom Base**」にします。色が画像と一致しているのを確認しておくとミスがないかもしれません。
![A06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/e5f12e57-172d-4825-9137-5eb00c923169.png)



構造体が用意できたところで、基本実装編で簡単に処理を作った「BP_TalkManager」を開いて改変していきます。
![A07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/1740f665-b84e-4236-ab6d-e91e857b8f77.png)


「My Blueprint」パネルのVARIABLES欄で、「+」ボタンを押して変数を追加します。
![A08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/13cca99f-32a5-4ff7-afcb-bbc10de61ef5.png)

新しい変数の型を先ほど作成した「MessageList」にします。名前もつけておきましょう。
![A09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/ce015448-26dc-49fc-970b-ec26d66c32ea.png)

「Variable Type」の右にあるアイコンをクリックして「Array」（配列）に変更します。9つのキューブが集まっているアイコンです。
![A10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/cb9ad883-f579-4cc5-9281-8705d02b0518.png)

![A11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/1eac1364-da43-4435-a38c-47aeda608af4.png)


カスタムイベント「NewMessage」の処理を切り離します。**ピンにマウスカーソルをあわせてAlt+左クリック**で線を切ることができます。
![A12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/7f3ef4ff-1556-42a4-93a2-7ed405f8499e.png)


メッセージ送り用のカスタムイベント「NextMessage」を作成します。
![A13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/c5d7673f-0eb7-4740-b0aa-6714b389891a.png)

一連の処理は「NextMessage」の近くに移しておきます。
![A14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/558e197f-cd14-4799-8104-2c9544c7487c.png)


変数リストからドラッグし、**Get MessageList**ノードを配置します。
![A15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/734c2ffe-fcda-4d48-bd42-d9f3f4f54156.png)

線を引き出して**Add**ノードを追加します。
![A16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/340375ee-89a0-4b81-9703-7ac9da75e8df.png)

**Add**ノードの下側のインプットピンを引き出して**Make Str_Message**を配置します。
ここに「Str_Message」が持つふたつの変数のそれぞれの要素を代入することができます。
![A17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/898eacce-5713-4c6d-b1e5-4af73b1c5138.png)

カスタムイベント**NewMessage**から線をつなげます。
これで新しいメッセージ、ボイスを受け取ったら「Str_Message」の配列に追加されるようになります。
このイベント単体ではメッセージ表示やボイスの再生は実行されません。

![A18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/905402d1-7243-44a3-8ecb-171381cb7d51.png)


カスタムイベント**NextMessage**側に同じように**Get MessageList**を配置し、線を引き出して**Get**ノードを置きます。これは配列内の指定されたインデックスを参照するものです。一番上のインデックスを参照したいので、番号は「0」にしておきます。
![A19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/b561a986-04a5-4fe8-9cdb-68a8302d134d.png)

**Get 0**ノードからさらに線を引き出し、**Break Str_Message**ノードを作ります。これは**Make**ノードの逆で、「Str_Message」から要素を取り出していくイメージが分かりやすいかもしれません。
それぞれのピンを対応するノードにつなげます。
![A20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/92c316c0-8128-4969-b288-63ca1fc395fa.png)

メッセージ送りをして新しいメッセージを表示したいので、これらの処理の前に前のメッセージを削除しなければなりません。
![A00_b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/6b3f84ae-ef9a-4d47-beef-268eefec714b.png)

**Get MessageList**から線を引き出して**Remove Index**ノードを使用します。



レベルブループリント側で、キー入力に応じて**NextMessage**イベントを呼び出すノードを追加します。
![A21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/dfeed3dd-740f-4620-9c3b-6e7ae2120549.png)
この例ではゲーム中に「1」「2」キーを押すとメッセージとボイスが蓄積され、マウスを左クリックすると順番に再生されていくようになります。

ところが、2つ以上メッセージを蓄積しないと文章が表示されなくなってしまいました。
これは、カスタムイベント**NextMessage**が強制的に0番のインデックスを削除しているためです。
これを解決するために少し改良を加えます。
カスタムイベント**NewMessage**に次のような**Branch**を追加します。
**Is Valid Index**ノードは配列変数「MessageList」の0番目が現在有効かを調べるものです。
![A22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/b89d179e-cf6c-450e-b287-4e01b7b1a6ec.png)

もし何も代入されていない場合、結果は「False」に分岐します。
「False」側では次のように処理をつなげます。
少し複雑になりますが、すべて正しく接続されているか確認してください。
![A23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/bb8a0741-5950-44de-8998-b1d61b37f77e.png)

これで0番のインデックスに何も入っていない（＝最初のメッセージである）場合、インデックスの削除が行われずメッセージが表示されるようになります。




### ボイス再生終了時にアイコンを表示する

ボイスを最後まで再生した際、ページ送りが可能なことを示すアイコンを表示します。

予めUIウィジェットにアイコンの画像を追加しておきます。
![B22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/57cf4588-bc14-9590-a37b-9674d7adc15b.png)
サンプルとしてカスタムイベントを追加し、その際にアイコンが表示されるアニメーションを再生するようにしました。
![B01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/05a65a9c-b950-42a2-9ee0-05ecce7686db.png)
また、メッセージ表示時にアニメーションを逆再生し、アイコンがフェードアウトするようにしておきます。
「Play Mode」をこのように設定します。
![B01b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/c5a2f633-9922-47ec-b308-d4bac7f93534.png)


アクター「BP_TalkManager」を開きます。
AtomComponentの**Get**ノードを置きます。
![D01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/1a6959f1-a0d2-4e63-91e7-a05c7cb0b59b.png)
線を伸ばし、**Bind Event to On Atom Sound Play State Changed**ノードを作ります。
これはサウンドの再生状態を監視し、変化があるとイベントを発火させるノードです。
![D02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/2a539ffd-f87b-4df9-a954-2637035d4c99.png)
![D03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/8f16a360-f901-492a-bc90-45de2c1f6847.png)

赤いインプットピンから線を伸ばし、カスタムイベントを作ります。
このイベントが実際に発火されます。
![D05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/7f317125-fd36-45d1-8d00-5a6128a6d6ff.png)
![D06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/3e7ee690-d28d-493d-a4f5-35ad444fd2ce.png)
**Switch**ノードで、検知された状態ごとに処理を分岐させます。
![D07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/932e9772-0e09-4290-9c01-722b4d0f73f2.png)
「Stopped」（音声が再生終了した状態）なら、ウィジェットのボイス再生終了時のイベントを呼び出します。
![D08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/0a7b8d9d-355f-47ad-bde5-3feccf024274.png)


再生終了時にアイコンが表示されるようになりました！
![B30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/29913b89-3ee7-4fc2-1806-a4cc5b3a731c.png)


### オート再生機能

ボイスを最後まで再生した際、次のテキストとボイスを再生します。
アイコン表示処理に少しノードを追加するだけで実現できます。

新規に変数を追加します。型は「Bool」です。
これは自動でメッセージ送りをするオートモードが有効であるかどうかを格納する変数です。
![C01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/1b5640fd-c057-460d-90a8-37c869e7f68f.png)

**Branch**ノードでオートモードかどうかを判断します。
![E01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/17a486df-e30b-42fe-9cb2-cdb48c21fa2e.png)


オートモードが有効な場合、**Delay**ノードをはさんでカスタムイベント**NextMessage**を呼び出します。
**Delay**で1秒程度待機をすることで、会話に「間」が生まれて自然な印象になります。
![E02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/3df613cd-1ad3-4c7d-aa48-dd0a99b34a6e.png)