はじめに
ADXアンバサダーとして記事を書いておりますSigと申します。
この記事ではUE5で作るVRプロジェクトとADX for UEを併用し、「視線によるUI選択とそれに伴うサウンド」を実装します。
VRゲームではそのデバイスの性質上、視線による判定(トラッキング)も可能です。
視線を利用したインタラクションにサウンド効果をつけ、リッチなUI演出を作ってみましょう。
やること
- 3D空間上にボタンを作る
- 視線をボタンに合わせると決定音を再生する
- 視線がボタンに合っていないときはボタンからかすかなループサウンドが再生される
- ループサウンドは、視線がボタンに近づくとはっきりと聞こえるようになる
聴覚でボタンの位置をプレイヤーに伝え、視線が近づくにつれ音の存在感を増すことで聴覚から視線を誘導します。
また、記事最後に「blueprintue」を使用したBPグラフ共有を載せています。コピペするだけで大体の実装ができますので、よろしければご活用ください。
当記事ではUE5.6 及び 「ADX LE UE SDK(2.04.02)」を使用します。
また、基本的にブループリントのみでの実装を行います。
ADX for UEはインディー向けの「LE版」であれば、無料で使用できます。
https://game.criware.jp/products/adx-le/
前提
「ADX for UE LE」を使用します。導入や簡単な使い方は以下の記事にあります。
ADX for UEの導入で、一歩上のサウンド表現を(導入編)
ADX for UEの導入で、一歩上のサウンド表現を(実践編)
実装
AtomCraftでサウンドを用意する
まずは効果音を用意します。
ふたつのwavファイルをマテリアルツリーにインポートしました。
- 「SE_UI_Decide.wav」: 視線をボタンに合わせたときの決定音
- 「SE_UI_PreDecide.wav」: 視線がボタンに近づくにつれ大きくなる誘導音

誘導音はループさせたいので、選択してインスペクターでループタイプを「ループ」にしておきます。

キューシートを右クリックし、キューを作成します。
「新規オブジェクト」→「キュー『ポリフォニック』の作成」です。

ふたつのキューを作り、マテリアルツリーからドラッグ&ドロップしてトラック上に配置します。
ふたつのキューはインスペクターでパンタイプを「3Dポジショニング」にします。
空間上で距離減衰がかかるので、どこから聞こえてくるかでボタンの方向が分かりやすくなります。

誘導音のキューを選択します。
トラックリストの空欄で右クリックし、「新規オブジェクト」→「AISACの作成」を選択します。
Aisacコントロールを使い、視線とボタン方向の角度の差によってボリューム、フィルタを操作して聞こえ方を変化させていきます。

AISAC名、グラフタイプを設定します。

グラフを操作し、画像のような右肩上がりの形にします。

「ポイントリスト」に直接数値を入力することで値をしっかりとデザインすることができます。

タイムラインに戻り、もうひとつのAISACを作ります。今度のグラフタイプは「バンドパス - Cof低域」です。
この値が大きいほど広域がカットされる(低域だけが通される)ので、誘導音が聞こえにくくなります。

「Aisacコントロールの数値が高いほど視線が近い」という法則でロジックを作るので、右に行くほどボリュームは高く、バンドパスのフィルタは低くしていきます。

再生して、上部のスライダーを動かしながら聞こえ方を確かめましょう。
実際にはこの2つの数値は「視線方向」というひとつのパラメータで操作しますので、連動するイメージです。

ここまでできたらキューシートをビルドし、エクスポートします。


キューシートのインポート、テスト再生
ビルドしたacb, acfファイルをインポートします。
コンテンツドロワー(コンテンツブラウザ)にドラッグ&ドロップして配置します。
ダイアログでは「Yes」を選択します。

2つのアセットがあることを確認します。

キューが再生されるか確認してみましょう。ドラッグ&ドロップでレベル上に配置します。

Detailsパネルの「Control Modulations」でAisacコントロールの値のテストができます。
+ボタンを押して項目を追加します。

AisacControl_00とAisacControl_01の値を変更し、再生してみましょう。聞こえ方が変わっていれば成功です。

ボタン用アクターの作成
ボタンとなるアクターを作成します。
コンテンツドロワーの適当な場所で右クリックして、「Blueprint Class」を選択。

親クラスでは「Actor」を選択します。

名前をつけます。視線で選ぶメニューなので、今回は「BP_SightMenu」としました。

ダブルクリックして開、StaticMeshとTextRenderコンポーネントを追加します。
位置やサイズ、パラメータを調整し画像のようにボタンにします。
座標を管理しやすくするため、中心部が原点になるようにするとベターです。

視線による当たり判定はTrace系のノードを使用します。
専用の当たり判定チャンネルを作りたいので、Project Settingsに移動します。

「Collision」タブに移動します。

「New Trace Channel」を押してトレースチャンネルを追加します。

名前をつけ、Default Responseは「Inogre」(無視)とします。
基本的にオブジェクトはプレイヤーの視線に反応せず、特定のものだけに適用したいためです。

トレースチャンネル「SightMenu」が追加されました。

ブループリントアクターのメッシュのCollsiion Presetsを「Csutom」にします。

「SightMenu」の反応を「Block」にします。これにより、このメッシュがトレースチャンネル「SightMenu」に反応するようになります。

レベル上の適当な場所にアクターを配置します。

視線合致の判定
プレイヤーがボタンを見たときに効果音が再生されるロジックを組んでいきます。
ここからの処理はプレイヤー側のBPで行うと取り回しがいいですが、実装サンプルとして簡易にするためこの記事ではボタン側のBPで行います。
プレイヤーのBPを取得し、キャストします。

アウトプットピンを右クリックして「Promote to Variable」で変数化します。

Tickイベントにて、Line Trace By Channelで当たり判定を飛ばします。

プレイヤー座標からプレイヤーの見ている方向に判定を飛ばします。
ノード構成はこのようになります。

入力ピンの色(型)が合わない場合は、右クリックしてピンのタイプを変更できます。

Trace Channelを「Sight Menu」にします。
また、「Ignore Self」のチェックを外します。そのままでは当たり判定がこのアクター自身を無視してしまうためです。

トレースが反応したかどうかを判定します。

変数を新しく作ります。型はBoolです。これは視線によるインタラクトが完了しているかどうかを記憶するためのものです。

「bInteracted」がFalseの場合のみ処理を行います。「bInteracted」をTrueにします。

Play Sound at Locationノードで効果音のキューを再生します。
必ず「Atom」カテゴリにあるものを使用します。同名のもう片方のノードはUEデフォルトのものなので、Atomキューは再生できません。

再生するキューを選びます。

視線がメッシュにヒットすると、一度だけ決定音が流れます。

Line Trace By ChannelノードのDraw Debug Typeを「For One Frame」にするとデバッグ用の視線描画が行えます。
ただしラインはまっすぐ飛んでいるため、VRの一人称では視界の真ん中に点が表示されるだけです。
エディタ表示で横から見てみると分かりやすいと思います。

ファジーな視線判定
「視線の近さ」を判定し、それが近いほど音を変化させていくアプローチを行います。
Tickイベント内で処理を行いますが、ノードを整理するためSequenceノードを置きます。

視線の角度判定の流れはこのようになります。
- 視線ベクトルを取得する(現在のベクトル)
- プレイヤー座標からボタンがある方向へのベクトルを取得する(目標ベクトル)
- ふたつのベクトルをDot Productノードで比較する
- ベクトルが一致しているほど、AisacControlの値を高くしてサウンドを変化させる
ふたつのベクトルはこのようなノードで取得できます。

Dot Productノードを使います。これは2つのノードを比較し、同じ方向であれば1.0に近く、反対方向であれば-1.0に近い値を返すノードです。

試しにPrint Stringノードを使い、Dot Productの結果を出力してプレビューしてみましょう。
毎フレーム出力するので、Durationは「0.0」にしておきます。

画面左上に数値が表示されます。
視線がボタン方向に一致していると1.0に近く……

別の方向では低い数値になります。

反対方向では-1.0に近い数値になります。

この数値を利用してAisacに渡していきましょう。
誘導音を再生するAtomコンポーネントを追加します。

キューを指定します。

Dot Productは「-1.0~1.0」の数値を返しますので、その結果をMap Range ClampedノードでAisacControlの数値である「0.0~1.0」に変換します。
この数値をSet Aisac Control Valueノードに渡します。
ボリュームとフィルタの2種類のAisacControlがあるので両方とも使用します。

決定音の再生時に、誘導音を停止します。

これで視線によるインタラクションと、音による誘導が実装できました。
「視線がどの程度一致しているか」というギミックはUIだけでなく、様々なVRゲームのシチュエーションに流用できるものです。ぜひ使用して、VR空間における頭や視線の動きをデザインしてみてください。
今回のグラフ
「blueprintue」にて、今回のサンプルとなるグラフを公開しています。
そのままコピペすればノードを流用できますので、ぜひご利用ください。
https://blueprintue.com/blueprint/5xpa_036/