UE4でOculusTouchを簡単に使えるテンプレートのメモ
今回はOculusTouchをUE4で使うお話です。
UE4には最初からモーションコントローラ用のプレイヤーポーンが存在しますが、それの掴む部分の中身の解釈をメモ書きとして残しておきます。間違ってたら修正ください。
ブログ執筆時に使用しているプロジェクトの実行環境はUE4.14.2です。まぁ別口で4.14.1で正月に試していたのでそっちでも動きます。
プロジェクトはVRTemplateで作ります。手のメッシュと入力イベントが最初から実装されてるので。
まず、Pawnを親クラスとしてブループリントクラスを作成します。次に、Actorを親クラスとしたブループリントクラスを作成します。こちらは、実際の手として扱うアクターになります。
手の表示
手の表示部分です。この部分だけを実装するだけでも(メッシュを設定していれば)手がでて、Touchの動きに手が追従するようになります。
プレイヤーポーンです。コンポーネントは参考にしたMotionControllerPawnと同じで、DefaultSceneRootにSceneComponentを子に、そして更にその子にCameraComponentをくっつけます。
イベントグラフはこれです。これもだいたい同じですが、OculusTouchにしか対応する気がない感じの実装です。
重要というか今回の目的である手の生成はこれです。先程作った、手を扱うためのActorを生成してます。後述で説明しますが、右手か左手か判定用の列挙体変数をスポーンに公開して、生成時に設定できるようにしています。あと、何が重なってても生成は絶対なので、Always Spawn, Ignore Collisionsに設定して、適当な位置に生成します。直後にこちらのSceneComponentにアタッチするので問題ないです。
生成した手のアクターはいろんなとこで使うので変数化して保持しています。
手のアクターはこんなかんじです。参考元ではテレポートように色々ありましたが、そこは省略するので、必要なコンポーネントは、RootにSceneComponent、その子にMotionControllerComponent、更にその子にSkeltalMeshComponent、そして最後の子にSphereCollisionConmpornentです。名前はそれぞれ参考元のを流用しました。
コンストラクションスクリプトでは、EControllerHand列挙体の変数HandをMotionControllerComponentのHandに設定しています。アクターが持っている変数のほうは、スポーンに公開しているため、生成時に設定されています。
BeginPlayでは手のスケルタルメッシュがLeftの場合に限り、左手の向きになるよう設定しています。もともとのスケルタルメッシュが右手で作られているのを使っているために必要な処理です。もし、両手別々のスケルタルメッシュの素材を使う際には、手の向きによってスケルタルメッシュにここで動的に設定するようにすればいいともいます。僕は持ってないので試せないです。
ここまで作ったらコンパイルして、レベル上に作ったプレイヤーポーンを配置してVRプレビューで見ると、手がTouchと同じように動くと思います。
なんかを掴む
しかし、このままでは、何を押しても反応しないし手は握ったアニメーションをしません。ので、その辺も実装してしまいましょう。
サンプルのほうの実装の簡単な説明をすると、
①手のアクターのSphereCollisionに重なってるアクターのリストを取得
②そのアクターがPickupActorInterfaceを持っているかを調べる
③重なってるアクターでPickupActorIntarfaceを持っているアクターが複数ある場合、一番距離が近いやつを掴む対象とする
こんな感じです。PickupActorInterfaceは掴んだときと離したときのイベントメッセージを送るために実装されています。
処理の流れがわかったところで実装に移ります。
入力を受け取るプレイヤーポーン側はほとんど実装するものがありません。入力を受け取って、左右それぞれのつかみに対応したボタン入力を受け取って入力されたときと離したときのイベントを呼ぶのみです。
掴む入力があったときに呼び出す関数GrabActorの中身です。
WantsToGripというBool変数は握ってることを管理するフラグです。次のGetActorNearHandはGrabShereに重なってるアクターを取得する関数です。あとで中身は載せます。
その後、条件を満たした重なってるアクターが存在していたらActor型変数のAttachActorに設定します。その後、MotionControllerをアタッチする際の親として参照を渡して、先程取得したAttachActorに拾ったときのメッセージを送ってます。
最後のRumbleControllerイベントはよくわかんないです。PlayDynamicForceFeedbackの説明読んでもいまいちよくわかんなかったです。関数名とかからしてコントローラに力を反映するみたいな意味っぽいですけどよくわかんないです。誰か教えてください。
ちなみになくてもつかめます。
掴む際に対象を取得するGetActorNearHandです。
まず、SphereCollisionのGrabSphereに重なっているアクターのリストを取得して、それをForEachLoopで回します。
リストの要素がPickupActorInterfaceを持っていて、NearestOverlapの値よりも、このアクターとPickupActorInterfaceを持っているアクターの距離がより近い場合、戻り値用のローカル変数であるNearestOverlappingActorと比較用のNearestOverlapにそのアクターと距離を設定し、次の要素へ進みます。
リストの要素すべてを確認したらReturnNodeへNearestOverlappingActorをわたし、処理を終えます。
そして、離したときの処理です。WantstoGripをfalseにし、AttachedActorが存在していた場合、MotionControllerComponentを親としてアタッチしていたら、離す処理のメッセージをAttachedActorに送り、AttachedActorの中身を空にします。
最後にEventGraphのEventTickです。WantstoGripをみて手のアニメーションやその他更新を行っています。順番に見ていきます。
まず、WantstoGripがtrueかAttachedActorがNULLでなければ握っている状態なのでGripEnum列挙体変数GripStateをGrabに設定します。
もしどちらでもない場合、近くに拾えるアクターがあるかどうかを見て判定を行います。
存在していれば握っている状態なので、GripStateにCanGripを、存在していないけどもWantstoGripがtrueならGrabを、そうでないならOpenを設定します。
次の処理ですが、もともと用意されているアニメーションインスタンスに先程設定した握ってるかの状態をそのままわたします。これでアニメーションが遷移するようになりました。
最後に、握っていたらコリジョンを有効に、握っていなかったら無効にします。これにより、OculusTouchのFirstContactのようにグーならものを殴れるようになります。
あとは、掴むものを用意します。初めから用意されているものでも全然構いませんが、どうせなのでこっちも作ります。
今回はこちらのScifi Melee Weapon Packを使います。理由は特にありません。強いて言えばかっこいいからです。有料アセットですので、無料でやるならば適当なスタティックメッシュをジオメトリで作ったり、InfinityBlade:Weaponsを使うといいと思います。
こんな感じのブループリントです。もともと、剣のスタティックメッシュにコリジョンが設定されています。その辺気をつけてください。
SceneConponentをRootに配置して、その下にStaticMeshComponentを子に置いています。取っ手の部分がRootの位置に重なるようにします。銃とかでもその辺気をつけてください。
InterfaceにPickupActorInterfaceを設定します。これにより、PickupとDropのイベントを実装することが出来るようになります。
実装ですがこんな感じ。もともとサンプルのほうではコリジョン設定を物理にしているため、物理を無効化する処理が入っていますが、この剣にはつけてません。宙に浮いていたほうがつかみやすいので。
AttachToComponentとDetachFromActorの赤枠で囲んでいる部分は、初期設定がKeepRelativeになっているため、なんか変な挙動をします。
掴む際には位置は手の位置と同期させるためSnapToTarget、回転とスケーリングはそのまま使ってくれたほうがいいのでKeepWorldです。
離す際にはそのままワールドの位置に残ってもらいたいのでKeepWorldです。
以上ですべて完了です。うまく言ってるとこんな感じに作った剣を掴んだり、やってはいませんが、目の前に用意されている青い箱を掴んだりできます。
剣を掴む pic.twitter.com/tTvjwC8upp
— 故・白鳥隆士 (@shiratori1221) 2017年1月8日