今回は、VR中にドアを手動で開け閉めする方法をご紹介します。
基本的には、UE4のVirturalRealityTemplateを使用して、すでに用意されているBPを利用していきます。
エンジンのバージョンは、4.17で作成していきます。
(1)プロジェクトの開始
まずはVirturalRealityTemplateを選択して、プロジェクトを開始しましょう。

次にマップを選択しますが、こちらも既に用意されているMotionControllerMapを使用していきます。

また、基本的にはVirturalRealityTemplateに用意されている
MotionControllerPwanとBP_MotionControllerを利用し、少し変更を加えていく形で進めていきますので、
Viveのコントローラー等の動作処理は、ほとんど作る必要はありません。

(2)必要なBPや設定の追加
今回ドア手動で開閉するにあたって、下記を準備していきます。
・DoorHandleとHandコリジョンプリセット
・IF_DoorOpen
・BP_Door
では順番に作成していきましょう。
(3)コリジョンプリセット
ここではオブジェクトチャンネルとコリジョンプリセットを追加していくので、
エディターのProjectSettingsを開き、Collisionを選択しましょう。

1.オブジェクトチャンネル
上記画面のObjectChannelsの項目からNewObjectChannelを選択し、
DooHandleとHandというオブジェクトチャンネルを新たに作成します。

2.コリジョンプリセット
Presetの項目からNewProfileを選択し、新たにDoorHandleとHandというコリジョンプリセットを作成します。
・DoorHandle

HandのみOverlapに設定し、他は全てIgnoreとします。
・Hand

HandleのみOverlapに設定し、他は全てIgnoreとします。
これで、コリジョンプリセットの準備は終了です。
(4)IF_DoorOpen
ブループリントインターフェースから、IF_DoorOpenを作成します。

作成したブループリントを開き、関数を追加していきます。
今回は、OpenとCloseの2つの関数を作成します。

・Open

Open関数には、Inputsに変数を1つ追加し、型にはBP_MotionControllerを設定します。
・Close

Close関数には、何も設定する必要はありません。
(5)BP_Door
アクタークラスを基底にした、BP_Doorを作成します。

1.ドア用スタティックメッシュコンポーネント
BP_Doorを開いたら、ドアフレームと、ドアプレート用のスタティックメッシュコンポーネントを作成します。

ドアのメッシュは今回はStarterContentsに入っている、
SM_DoorFrameとSM_Doorを使用しています。
・SM_DoorFrame

・SM_Door

もちろん、ご自分で作成したメッシュを使用してもかまいません。
2.ボックスコリジョン
ドアハンドル位置を取得するための、ボックスコリジョンを追加します。
こちらのボックスは、ドアプレートに追従して動いて欲しいので、
先程のDoorPlateへ、アタッチします。

そして、Box_Handleのコリジョンプリセットを、DoorHandleに設定します。

4.変数
BP_Doorで必要な変数は下記との4つとなります。
・HandleVector・・・ハンドルの位置を保存します。型はVectorとなります。
・Angle_Min・・・ドアの開閉角度最小値をセットします。型はFloatとなります。
・Angle_Max・・・ドアの開閉角度最大値をセットします。型はFloatとなります。
・BP_MotionController・・・ドアハンドルに触れたコントローラーを保存します。型はBP_MotionControllerとなります。

5. IF_DoorOpenを追加
クラスセッティングスを選択し、Interfacesの項目へ、
(4)で作成したIF_DoorOpenを追加します。

これでIF_DoorOpenを使用できるようになりました。
6.MakeAngleFrom2Vectors関数
内容としては、握られた時点でのドアハンドルの位置と、
そこから動いた手の位置の2つのベクターの角度を取得します。

2つのベクターの角度取得の仕方は、下記サイトを参考にさせていただきました。
・KIT数学ナビゲーション
http://w3e.kanazawa-it.ac.jp/math/category/vector/henkan-tex.cgi?target=/math/category/vector/naiseki-wo-fukumu-kihonsiki.html
中身の説明は、下記画像のようになります。

7.RotateDoorByHand関数
内容としては、5で作成した関数を利用して、ドアプレートを回転させるものとなります。

少し横に長くなってしまったので、分割して説明をしていきます。

まずは、5で作成したMakeAngleFrom2Vectors関数へ、
・ドア軸の位置
・ドアを掴み始めた位置
・手の位置
をセットします。
そして取得した角度の値で、ドアを回転します。

その時に、手がドアの回転する側へある時のみ回転するように、
ブランチで分岐させています。
8.イベントグラフ
最後にこれまで作成した変数、関数を用いて、イベントグラフに、ノードを配置していきます。
イベントは、下記の3つから成っています。
・Event Tick
・Event Open
・Event Close
Event Tickでは、Gateノードをはさんで、RotateDoorByHand関数へつなげます。

Event Openでは、ドアハンドルを掴んだ時のドアハンドルの位置を保存します。
その時に、Z値をだけをドア軸のZ値に入れ替えます。
そして、Event TickからでているGateノードを開きます。

Event Closeで、Gateノードを閉じます。

これで、BP_Doorの完成です。
(6)BP_MotionController
VirturalRealityTemplateに既に入っている、BP_MotionControllerを
少しだけ変更していきます。
1.GrabSphereのコリジョンプリセット
GrabSphereを選択し、コリジョンプリセットをHandに設定します。

2.GetActorNearHand関数の変更
BP_MotionControllerに既にある、GetActorNearHand関数を、ドア開閉用に少し変更します。

・GetOverlappingActorsノードのクラフフィルターをBP_Doorへ変更し、
ドアとのオーバーラップ情報を取得します。

・DoesImplementInterfaceノードのインターフェースをIF_DoorOpenに変更します。
これで、手とドアハンドルとの重なり情報を取得できるようになります。
3.GrabDoor関数
GrabDoor関数を作成し、InputsへBoolean型の変数を追加し、
オンとオフを切り替えられるようにします。

・オン
掴んだハンドルアクターを取得し、IF_DoorOpenのOpenイベントへ通知します。

・オフ
IF_DoorOpnのCloseイベントへ通知します。

これで、手がドアハンドルを掴んだことを、BP_Doorへ通知することができるようになります。
(7)MotionControllerPawn
(6)と同様に、VirturalRealityTemplateへ既に入っている、MotionControllerPawnを
少しだけ変更していきます。
1. IF_DoorOpenを追加
クラスセッティングスを選択し、Interfacesの項目へ、
(4)で作成したIF_DoorOpenを追加します。

2. GrabDoor関数のセット
MotionControllerのイベントグラフ内にある、
InputAction GrabLeftとInputAction GrabRightの処理を、
(6)で作成したGrabDoor関数へ切り替えます。


これで、ドアハンドル位置で、コントローラーのトリガーを引くと、
ドアを開け閉めできるようになりました!

機会がありましたら、今度はスライドドアの開閉を、
ご紹介できればと思います。