ADX+UE5で作る、ゲーム中会話パートのボイス再生機能（基本実装編/新バージョン対応）

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



本記事ではシチュエーション別に対応したメッセージとボイスを再生するまでの実装の基礎について扱います。

再生するほか、ボイスを再生し終わったらページ送りを促すアイコンの表示や、オートで会話を送る処理などは「機能追加編」の記事にて扱います。
https://qiita.com/SigRem/items/f3dc003632eae6d4f7b7

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

記事執筆時点のADX for UEのSDKバージョンは2.00です。

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

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

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

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

# 実装

## AtomCraftで素材を準備する

まずはADX用サウンドオーサリングツール「AtomCraft」でボイス素材を登録・編集していきます。


### マテリアルのインポート

ボイスとなる音声素材を用意します。「マテリアルツリー」にドラッグアンドドロップして配置します。
![A01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/4d28819e-da81-27f6-c49a-0f775fb60795.png)
画像ではサンプルとして6つの音声をインポートし、「Voice_A」というフォルダに格納しました。
フォルダの追加は「マテリアルルートフォルダー」を右クリックし、「新規オブジェクト」→「マテリアルフォルダーの作成」から可能です。
![A02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/274ac1b7-9b9a-4ddf-96d5-9b754eb768f2.png)



### キューの作成

新規にキューを作成します。
「ワークユニットツリー」のキューシートを右クリックし、「新規オブジェクト」→「キュー『ポリフォニック』の作成」を選択します。
![A03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/439d1d4b-db48-fbed-2073-fff2ece75053.png)
「ランダムでキャラクターが喋るトーンや台詞を変える」などの特殊な演出をしたい場合、用途に合わせて「ランダム」などの別のキュータイプを選択してもよいでしょう。
![A04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/867150f1-3270-a847-43bd-5102905ddc47.png)
作成されたキューにマテリアルを配置します。タイムライン上で再生タイミングを合わせ、正常に再生されることを確認します。


### 3D音声の設定

2Dで展開されるビジュアルノベルなら距離による減衰などの要素は不要ですが、3D空間上で音声を再生したい場合は簡単な設定が必要です。

**キューを選択し**、インスペクターにて「パンのタイプ」を「3Dポジショニング」に変更します。
![A05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/7e4bd87b-570a-3ebe-fe88-ca623146851c.png)
ゲーム内での聞こえ方を確認するには、セッションウィンドウを使用します。
ツールバーの「表示」→「セッションウィンドウ」で開きます。
![A06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/151dbde7-8096-a4a2-f92f-18bf1d91b009.png)
ウィンドウ下部のリストに確認したいキューをドラッグアンドドロップして登録します。
![A07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/4fd899b8-87ee-47b0-dc0a-b9f127a1f3c7.png)
上部の「3Dポジショニング」をクリックし、3Dポジショニングの確認機能を呼び出します。
![A08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/e9e0ceca-d2ec-d9e1-121e-34f95e1880df.png)
「プレーヤーに3Dオブジェクトをアタッチ」にチェックを入れ、緑色の領域をドラッグすると音源の移動がテストできます。
![A09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/52ed44d3-51c4-7e93-2bfb-60872c5f9e9a.png)



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

設定が完了したらキューシートをビルドします。
![A10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/59470add-81f4-040d-005d-5660c3d3b8c2.png)
![A11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/422817ba-8177-7d0e-b6a3-32170eee597e.png)
これでAtomCraft側の実装は完了です。


## UE5でボイス再生機能を実装する

UE5のエディタを起動します。

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

ビルドしたキューシートをインポートします。
acb、acfファイルをコンテンツブラウザの適当な場所にドラッグアンドドロップします。
acfファイルインポートの際に出てくるダイアログではどちらも「Yes」を押します。これにより、プロジェクトにacfファイルが適用されます。
![B01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/7f88fddd-c1e6-6dbc-3c3f-319c6bf84bcb.png)
![B02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/c976aa84-1e41-018a-a937-91468b6a32b8.png)
コンテンツブラウザにAtomConfigとAtomCueSheetアセットが登録されます。
![B03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/6f7eed43-bfe6-9a10-221b-9f77cbe5b768.png)
AtomCueSheetアセットをダブルクリックして開き、キューが再生できることを確認します。
![B04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/482c7aa4-e46c-c2b1-cd01-25ee96220ccd.png)


### メッセージ表示ウィンドウを用意する

メッセージとボイスを呼び出す実装から始めます。
サンプル用にメッセージウィンドウを表示するだけのシンプルなウィジェットを用意します。
コンテンツブラウザの適当な場所で右クリックし、「User Interface」→「Widget Blueprint」を作成します。
![B05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/a83a0f30-bd1d-de9d-0db7-a15a78fb41f0.png)
親クラスには「User Widget」を選択します。
![B06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/03b2a1c8-cd25-0e26-2458-3d0de9fe57f8.png)
ダブルクリックしてUMG（UIエディタ）を開きます。
![B07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/eaaeb260-7dc7-43cf-b5d0-463286830459.png)
まずはPaletteパネルから「Canvas Panel」を配置します。
![B08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/814d4cc6-ac0a-d209-63f6-829a61ad36f3.png)
「Image」と「Text」を配置して、簡単な会話ウィンドウを作ります。
![B09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/99e664a0-e166-d593-4ebb-33005f49cb2b.png)

表示するテキストを自由に変えられるように処理を組みます。
テキストを選択します。
テキストブロックの名前を設定し、「Is Variable」にチェックを入れて変数として参照できるようにします。
![B13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/e69f47ba-6247-c458-cf77-907eea6af0b8.png)
「Graph」タブを押してグラフに移ります。
![B19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/94ed8a05-936d-163d-995a-c6a1a5df13ca.png)

カスタムイベントを追加します。
![B14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/95c82cf1-af19-3d48-ca50-6ea8015ca25a.png)
![B15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/3be6d4de-0b44-a513-a777-87185e4f512b.png)
変数化したテキストブロックを**Get**し、そこから線を伸ばして**Set Text**ノードを配置します。
![B16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/49b364ae-6c2b-0aec-3ce0-cb1996f986b9.png)
**Set Text**ノードのピンクのインプットピンから線を伸ばし、カスタムイベントノードまで持っていきます。
![B17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/54168ac5-dc7c-06bf-63e4-a3294486645c.png)
カスタムイベント側に「Text」というパラメータが作られました。外部からこのカスタムイベントを通し、テキスト内容を設定できるようになりました。忘れず線をつないでおきます。
![B18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/82aef634-9214-4ef0-e831-237bf73507fe.png)





### ボイスを再生する（メッセージ表示）

今回はメッセージ表示を管理するアクター「BP_TalkManager」を作成して、レベルブループリントからイベントを呼び出す方法をとります。

新規にアクターを作成します。
コンテンツブラウザで右クリックし、「Blueprint Class」を作ります。
![C01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/a3c96639-992e-e193-0913-1191c749a434.png)
親クラスは「Actor」を選択します。
![C02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/6bc0a74d-ba81-25ad-ea14-ae418c4f725d.png)


分かりやすいよう「BP_TalkManager」と名前をつけました。
![C03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/b77157f8-b00d-8edf-9015-536a060fb421.png)

イベントグラフを開き、**Event Begin Play**からUIを画面に表示するノードを配置します。
**Create Widget**ノードを配置し、表示するWidgetBlueprintを指定します。
ここでは先ほど作った「UI_TextMessage」です。
![C04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/418aa0b3-9e69-3836-6dfd-7acfba582a55.png)
**Add to Viewport**ノードをつなげて画面に表示します。白ピン、青ピンがつながれていることを確認してください。
![C05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/4974637e-16f1-74b6-f68b-f3b428a4ddb8.png)


**Create Widget**の青いアウトプットピンを右クリックし、「Promote Variable」を選択します。
![C06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/0dd0b3fc-32c5-d843-b87c-e80a4c68bac8.png)
ウィジェットが変数として登録されます。変数に名前をつけ、**Set Talk Widget**ノードを末尾につなげます。
![C07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/29cba9be-cbca-50ce-ae1b-dbfc105e2683.png)



「BP_TalkManager」内に新規にカスタムイベントを作成します。
![C08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/1ab4ce88-8362-e54f-a3ea-0582d34edcfe.png)
![C09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/2001b1b1-91bd-ee96-81d9-375608d4f80a.png)


**Get Talk Widget**を配置し、そこから線を伸ばしウィジェット内のカスタムイベントを呼び出します。
![C10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/0a1632f4-2820-d281-5c7e-48b355be6214.png)
カスタムイベントのピンクのインプットピンをドラッグし、「BP_TalkManager」側のカスタムイベントにつなげることで、外部からメッセージを代入することができるようになります。
![C11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/25c14e71-5e1c-c65f-40dd-a3776b1ab486.png)
![C12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/b504b8ed-e540-dece-6b3a-eff2a14d8dee.png)

これで、レベルブループリントや外部アクターからカスタムイベントを経由することで、メッセージウィジェットに新たなテキストを表示することができるようになりました。


### ボイスを再生する（音声再生）

「BP_TalkManager」にAtomコンポーネントを追加します。
Componentsパネルの「+Add Component」から「Atom Component」を選択します。
![D01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/4a068648-cfa3-ddad-ba53-5d04c90e2c39.png)
カスタムイベントの後ろに**Get Atom Component**を配置し、そこから線を伸ばして**Set Sound**ノードを配置します。
![D02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/e5874497-b0ae-f8bb-dc0d-1034c4d86c17.png)
**Set Sound**ノードのインプットピンから線を伸ばし、カスタムイベント上でドラッグアンドドロップします。
![D03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/36b91da3-88b3-5a08-6c77-b27be80d3566.png)
カスタムイベントに再生するサウンドを代入することができるようになります。
![D04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/adb9a71a-46a5-b054-3831-22f9fc27a4e8.png)
**Get Atom Component**から線を伸ばし、一番最後に**Play**ノードを配置します。
![D05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/c2adcad2-c2dd-9665-66a3-b51628865fc1.png)




### ボイスを再生する（イベント呼び出し）

レベル上に「BP_TalkManager」を配置します。
![E01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/37d7f3f4-7b20-7f20-1137-d6edb420fa18.png)


レベルブループリントを開きます。
![E02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/ee5d04ff-1d60-be08-fda4-542aee176654.png)
**「BP_TalkManager」を選択した状態で**、イベントグラフで右クリックし「Create a Reference to BP_TalkManager」を選択します。
![E03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/97ff5a97-2534-d3cb-d17e-bb27dce08e9c.png)
「BP_TalkManager」のリファレンスノードが作られます。
![E04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/3775cd12-4b98-8f41-a751-0f37cfb8563e.png)
**Input Key**イベントを使い、キー入力に応じてメッセージが表示されるようにします。
![E05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/95786a33-943d-0e7c-52f4-9d5a8496bc3e.png)
**Get BP_TalkManager**から線を伸ばし、カスタムイベント**New Message**を呼び出すことでメッセージ表示、ボイス再生が同時に実行されます。
![E06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/fa64fdc6-369f-2db9-07ab-26f6de143c30.png)

ボイス再生のためにはキューシートを指定し、キューを取得する必要があります。
レベルブループリントに新規に変数を作ります。
型は「AtomCueSheet」タイプです。コンパイルし、DefauntValueに今回ビルドしたキューシートを指定します。
![E07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/a3b80089-d94a-e2d3-1cc9-82efbf096cd6.png)
**Get**ノードを配置し、そこから**GetSoundCue**ノードで再生したいキューのインデックスを指定すればOKです。
![E08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/b5818e39-17f9-7e9e-193c-5e7043cfeefd.png)

**GetSoundCue by Name**ノードを使えば、キューを名前で指定することも可能です。
![E10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/dcd60b1e-b6d8-3daa-9be6-ff982c21cd02.png)

ゲームを再生し、キーを押せば対応したメッセージとボイスが流れます。
![E09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/739d365f-a51f-9994-5348-efba4de06d60.png)


ゲームのシチュエーションに合わせたてテキスト機能の基礎が完成しました。
「追加機能編」記事では、ボイスを再生し終わったらページ送りを促すアイコンの表示や、オートで会話を送るような処理を書いていきます。

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


## 補足

ここでは実装の簡略化のためにキューをあくまで単発で呼び出しています。
大量のボイス再生シーンを用意する場合はデータテーブルなどの機能を使用したり、イベントをキューとして記憶しておき、順番に実行するなどの処理にすると使い勝手が良くなります。
次に再生するメッセージやボイスを記憶していく実装は「追加機能編」の記事で解説します。