ADX for UEで、オーディオデバイスの変更を検知し、ポーズ（処理を一時停止）する

# はじめに
アンリアルエンジンとサウンドミドルウェア「ADX for UE」を連携させ、PCのオーディオデバイスが切り替わったことを感知する実装です。

ゲームやアプリケーションによってはフォーカスしているウィンドウが切り替わったり、コントローラの接続が切断されたりして操作に支障がでうる場合にポーズメニューを表示し、一時停止する機能がついています。

![B11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/f470dace-3050-1242-cef6-415c8d8ce4f5.png)


オーディオデバイスの切断により演出やゲーム要素の認識に問題が出る可能性がある場合、同じようにゲームをポーズしてユーザーの環境を待つと親切設計かもしれません。特にリズムゲームなどのサウンドに重点を置いたゲームではあると嬉しい機能でしょう。


# 前提

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


# 実装

## ポーズ用のUIを用意する

ポーズメニューとなる簡易なUIをUEのウィジェットとして準備します。

### Widget Blueprintの作成

コンテンツブラウザの適当な場所（UIフォルダなどがいいでしょう）で、右クリックして「Use Interface」→「Widget Blueprint」を選択します。
![A01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/89ce8669-8d9d-e7bf-2b9e-c7d03af11ff9.png)
親クラスは「User Widget」を選択。
![A02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/d3e4fa02-b4c3-a2f2-3d01-ab6a33442305.png)
新しくウィジェット用のアセットが作られるので、分かりやすい名前をつけます。
![A03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/0b4079ce-5321-8169-deeb-4eb4a2fa60e3.png)


### ウィジェットのデザイン

アセットをダブルクリックして開きます。
まずはUIの下地となる「Canvas Panel」をPaletteパネルからドラッグアンドドロップして配置します。
![A04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/1014021a-46fc-0485-dcc2-9771b975dd27.png)
UIの背景となる「Image」を配置し、画面全体を覆うよう引き伸ばします。
アンカー設定や色の設定などは画像のDetailsパネルを参考にしてください。
![A05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/9aade86b-bc5d-26c8-10e2-83e2b14d51d2.png)
テキストブロックを置き、オーディオデバイスの変更が原因でゲームがポーズされた旨のメッセージを表示します。
![A06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/3f752c47-328a-a97a-edab-e856e96a11c7.png)


### ウィジェットのアニメーション

ウィジェットが呼び出された際にアニメーションを再生し、ユーザーの注意が向くようにしてみましょう。
Animationsパネルの「+Animation」ボタンからアニメーションを新規に追加します。
![A07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/3fc38ff5-3168-6f9a-bdb6-fc3f9a2ba60d.png)
キーフレームを打ち、背景がフェードインしテキストが現れる、という流れがグラフィカルに分かるようにします。
![A08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/993b9e30-fada-df46-a1fc-666dd7296b87.png)
アニメーションを再生するためのブループリント処理を書きます。
右上のタブからイベントグラフに移動します。
![A09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/7e891066-d3f6-fe33-51df-28f01eecf749.png)
ウィジェットのアニメーションを再生するには、**Play Animation**ノードを使用します。
![A11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/13178410-db51-fa3a-94f8-c462391c2917.png)
アニメーション再生の対象となる**Get UI_Appear**（アニメーション名）ノードは、My Blueprintパネルから該当のアニメーションをドラッグアンドドロップし、「Get （アニメーション名）」を選択することで配置できます。
![A10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/b99561e0-ab8b-962f-2888-73db0975c94c.png)


## デバイスの変更を検知する

オーディオデバイスが変更されたことを検知して、ゲームをポーズしポーズメニューを表示します。

### Atom Device Watcherを追加する

まずはオーディオデバイスを監視するためのアクターを作ってしまいましょう。
コンテンツブラウザの適当な場所で右クリックし、「Blueprint Class」を選びます。
![B01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/bd836e60-aba1-2cf2-bb8c-02b943ab5db7.png)
親クラスは「Actor」です（画像ではLyraSampleのテンプレートを使用しているため、表示が少し変わっています）。
![B02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/a5461a31-f84f-9bb8-c288-415d18bd9ee6.png)
アクターが作成されます。分かりやすいよう名前をつけます。
![B03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/5cff609b-5d25-8c62-1fd1-beef805aeb8e.png)
アクターを開き、イベントグラフに移動します。
オーディオデバイスを監視するための「AtomDeviceWatcher」が必要ですので追加していきます。
**Construct Object from Class**ノードを配置します。これはクラスを指定し、対象のアクターなどに付加できるノードです。
![B04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/e4664667-9037-9f99-a019-350fe0b4d7f7.png)
「Class」には「Atom DeviceWatcher」を指定します。
青いインプットピン「Outer」には付加先のオブジェクトを指定しますが、このアクターにオーディオデバイスの監視機能を持たせたいため何も繋がず「Self」としておきます。
![B05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/f0f57a20-856e-89fa-7cb3-c2c5c3ce98d8.png)
青いアウトプットピンを右クリックし、「Promote to Variable」で変数として登録します。
これで他の場所からも手軽にアクセス可能になります。
![B06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/e0f9a556-8fe8-3253-6215-dce292d4d76a.png)
![B07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/160db2e7-9ab0-48bd-3ed7-d340aaadb0a6.png)


### デバイスの変更をイベントとして登録する

**Set Device Watcher**の青いアウトプットピンから線を伸ばし、「assign」と検索します。
候補に出てくる**Assign On Device Updated**ノードは「**オーディオデバイスの変更を検知した際に発火されるイベント**」を登録するはたらきがあります。
![B08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/0a3620a3-fa8d-12ec-e3ca-cc36b6de8fad.png)
自動的に新しいイベントが作られます。赤い線がつながっており、トリガーとなるノードが分かるようになっています。
![B09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/c1728769-c1e7-4799-55b2-9ae2096034d6.png)


### ゲームをポーズする

このイベントからゲームのポーズを行います。
**Set Game Paused**ノードでゲームの一時停止が可能です。「Paused」にチェックを入れれば一時停止、外せば再開となります。
また、同時に**Create Widget**でウィジェットを呼び出して、**Add to Viewport**ノードで画面にウィジェットを表示します。
![B10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/596ac21a-3595-187a-5edf-174ac561e7c3.png)

これでオーディオデバイスが変化した際にポーズメニューが表示されるはずです。
ゲームを再生して、PCの設定などでオーディオの出力デバイスを変えてみましょう。
![B11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/80658b34-4889-6ddc-d93a-9d4498e4ad6d.png)
画面が暗くなり、ゲームがポーズされました！



## ボタンを押してポーズメニューを閉じる

さて、このままではゲームが再開できないため処理を追加します。
デバイスを再設定後、ユーザーがボタンを押すことでポーズメニューを閉じ、ゲームを再開します。

### ポーズ解除ボタンを追加する

ウィジェットのデザイン画面に戻ります。
Paletteパネルから「Button」を追加します。
![C01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/e8f46afe-467c-9ab5-c901-b4bbc6e7cc12.png)
追加した「Button」の中に、さらにテキストブロックをドラッグアンドドロップして配置します。
![C02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/41d54a40-424a-851c-4402-dbea4b171b3e.png)

### ゲームの再開

**ボタンを選択して**、Detailsパネルの下部の「Events」より「On Released」の「+」ボタンを押します。
これによりボタンをクリックして離した際にイベントが発火します。
![C03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/cdd4d065-644a-688a-d176-5c0fadc81515.png)

自動的にイベントグラフに移動します。イベントが追加されています。
![C04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/f7eb2fc2-4662-27b1-ea39-3b51e8fb164f.png)
ボタンが押された際のイベントではポーズを解除したいので、**Set Game Paused**、**Remove from Parent**ノードを配置します。ポーズ解除なので**Set Game Paused**ノードの**チェックは外しておきます**。
![C05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/fe2cc977-365c-c192-c33d-8d55e5b707b4.png)
ゲーム中はマウスカーソルが非表示になっているので、このままではユーザーはボタンを押したくても押せません。
そこで、ウィジェット表示時に**Set Show Mouse Cursor**ノードを使ってマウスカーソルの表示設定を変えてあげる必要があります。
まずは**Get Player Controller**でプレイヤーコントローラを取得します。
![C06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/2929ad02-2496-d8af-6fa2-671c9debe1f7.png)
**Get Player Controller**から線を伸ばし、**Set Show Mouse Cursor**ノードを配置します。
マウスカーソルを表示させたいのでチェックは入れておきます。
![C07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/c518107c-1159-6cb9-d591-bd9f2a63c95f.png)
ポーズ解除側でも同じように**Get Player Controller**から線を伸ばし、**Set Show Mouse Cursor**ノードを配置します。
こちらのチェックは外します。
![C08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/9ec7b30e-caab-369c-9587-43eea9deb304.png)
ゲームをテストしてみましょう。
オーディオデバイスの変更とともにウィジェットとマウスカーソルが表示され、操作が有効になります。
![C09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/7ddf61e9-908d-cc16-ae29-c123312c1f4a.png)
「Resume」ボタンを押すことでウィジェットが非表示になり、ゲームが再開します。
![C10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154452/e780b66e-7991-5125-867a-e9a9be6f6cd8.png)

オーディオ機能に異常をきたしたままのゲームプレイはなかなかにユーザーの体験を削ぐものです。
デバイスのトラブルになるためゲーム側ではケアしきれない問題ですが、時にはこのようにゲームを停止させるなどの措置をしてあげるといいでしょう。