UE4+Oculus+oFで展示コンテンツをつくった

Unreal Engine 4 (UE4) Advent Calendar 2014 – Qiitaの15日目の記事です。

先日、パシフィコ横浜にてYokohama ホットロッドカスタムカーショーに、UE4+Oculusを使ったコンテンツを展示してきました。
初めてのUE4とOculusでコンテンツを作ったのでそのことについて書きます。
概要については、「横浜ホットロッドカスタムショーにGodSpeed VRを展示してきた」を。

今回使用したUEのバージョンはwindowsのUnreal Engine 4.5.1です。
UEを触り始めて間もないので、間違いなどあれば指摘ください。

■システム概要
スライド1

まずシステムの概要から、シーン描画はUE4を使用しOculusに表示しています。
センシング用のアプリケーションはoFでOpenCVを使い実装しています。
4つのカメラ入力がありそれぞれのセンシング位置についたマーカーの位置の変化に応じて、アクセル、フロント・リアブレーキ、クラッチ、ギア入力の値をoscでシーン描画PCに送信しています。
受け取った値を元に、描画PCではVR上のバイクの動作を変化させ、Oculusのヘッドトラッキング位置と合わせて、ライダーのボーンを実際のユーザーの動きに近くなるように制御しています。
バイクの速度変化に合わせて、工業用扇風機の風力を変化させるためにセンシングPC内のDoctorMXに対してOSCで風量を送信し、DoctorMX からDMXで調光器から工業用扇風機を制御しています。
バイクには振動装置を2台つけて、ローパスフィルタを通った低音で筐体となるバイクを振動させています。

UE4上でのバイク部分のBlueprintは下記のようなコンポーネントの構造となっています。

01_2

各種サウンドをそれぞれサウンドの発生位置に配置しています。
・InputOscActor はセンサーPCからのOSCを取得・処理するためのもの。
・IKTarget内にははライダー(riderIK2)をIKで動かすための目標が入っています。
・PoseableMeshBikeにバイクのSkeletalMeshを設定し、なかにmeter部分をChildActorとして入れています。
・VehiclePhysicMovementがバイクを動かすエンジン部分にあたりActorとなります。
・dmxSenderActorは扇風機に風量を送るためのものです。

actorをコンポーネントとして使用するためにChildActor Componentを使用しています。

01

 

■いくつかの課題

UE4を初めて触るために、今回のコンテンツの為にいくつか調べなければならない技術的な課題がありました。
・バイクの挙動(物理挙動について)
・センシングPCからの値の受取り
・ヘッドトラッキング位置によるVRライダーのボーン制御

 

バイクの挙動

当初は、VehicleTemplate があるから楽勝かと思っていました。
実際にドキュメントを読みながら試してみたのですが二輪のときに上手く動かず、VehicleWheelのみ使えないかと試しても思った通りに動かず、、(自分のやり方が悪いだけかもしれませんが、、、)また、ドキュメントでは、SkeletalMeshで乗り物を作成する必要などあり、この時には、バイクのボーン構造など決定していなかったので、VehicleTemplateを使わない方法で作成しました。
今回のコンテンツは、直線の最高速を体感するのみなので、曲がる必要ありません。
なので、オブジェクトを進行方向のみにロックし進行方向に対して力を与えることにします。

Unityではrigidbodyがあり、それに対して力を加えていくのですが、UEではそこのルールがわからない、、
いろいろ試してみたところ、基本的にはPhysics属性があるコンポーネント(Shape, SkeletalMesh, StaticMeshなど)には物理環境でどう動くか設定する項目がり、その子になる各コンポーネントはPhysicsの設定があるものは、「Auto Weld」で親に対して固定させることができるようです。

バイクを前方に動作させる方法として、「Thruster」をコンポーネントとして追加し、推進力を変化させる方法も検討しましたが、シンプルにAddForceノードで制御するようにしました。
(Thrusterは、常に指定の力を与えるロケットエンジンのようなもので、強さやrotationを設定することで推進力を変化させることができます。ベクターノズルのようなイメージですね。)

23_バイクの物理1

また、Physicsの項目として
・MassScale (変更することで「KG単位の質量」が変化。)
・LinearDamping (移動方向の抵抗)
・AngularDamping (回転方向の抵抗)

を調整します。
「KG単位の質量」はオブジェクトのサイズに合わせてデフォルト値が入っています。MassScaleを変更することで、そのオブジェクトの重さ(密度)を変化させることができます。
LinearDamping、AngularDampingはそれぞれ移動、回転方向の抵抗です。大きければ大きいほどオブジェクトは抵抗を受けて動作がとまりやすく(動きにくく)なります。

Phys Material Overrideはオブジェクトの物理マテリアルを上書きすることができます。
今回はバイクのタイヤの動きを再現したいので、抵抗を低めにしています。

バイクの動きは簡単な説明となりますが、GetVelocity.VectorLength / (wheelRadius*2.0*PI/60.0) でwheelRPMでタイヤのRPMがでるので、それとGearRatioとengineRPMを元に推進力を調整します。

またエンジンはActorとしてバイク本体とは別のVehiclePhysicMovementBPとして作成し、
ChildActor Componentとして、バイク本体に配置しています。
バイク本体から GetChildActor -> CastTo VehiclePhysicMovementBP
としてエンジンに対して各種インプット値をセットし動作させています。

 

センシングのPCからの値の受取り

センシングのPCは4台のカメラが移すマーカー位置から、アクセル等の値を計測しています。
センシングのPCから値を受け取る為に、UE4-OSC Pluginを使用しています。

OSCについてはこちらに詳しく記載がありますが、
UDPをベースとして、addressと値の型を指定してデータを送信することができます。

複数のアプリケーションを連動させたりするので通信を使うと便利で切り分けがしやすくなるので、UEでOSCが使えるというのはかなり可能性を感じます。
ある機能をC++のPluginとしてUEに機能を取り込まなくても、他のアプリケーションとOSCで通信することで、いろいろな拡張ができるかと思います。
今回のコンテンツではセンシング部分はopenCVなどの使いやすいoFで処理をおこない、その結果をUEで受信しています。

プラグインはUE4-OSCのREADMEにもある通り、プロジェクト直下に「Plugins」フォルダを作成してEditorを再起動して使います。
UEでプラグインを使うときにちょっとハマったのが、フォルダに入れたのはいいが起動時に「ProjectName could not be compiled. Try rebuilding from source manually.」とエラーがでてしまうということです。
Pluginを使用するためにはC++のプロジェクトを作成する必要があります。その為にVisualStudioがインストールされている必要があります。
UEでのC++のプロジェクトの作成と実行については@aizen76さんのこの記事が分かりやすくまとまっています。
一度、C++のプロジェクトを作成して実行すると、その後はプラグインを追加したときに自動でビルドしてくれるようになります。
※既に作成済みのプロジェクトに後からC++のプロジェクトを作成する方法がとして、やり方が正しいかわかりませんが「ファイル」 – 「プロジェクトにコードを追加…」でコードを追加することでC++のプロジェクトが作成されます。

UE4-OSCのレシーバーを使用するには、Osc Receiver Actorを継承する必要があります。
(※下図の右上の赤い部分が現在のBPの親クラス)
すでに作成済みのActorの親を変更する場合は、Blueprint Optionから変更できます。

センサー側からは、/yokohama/moto というアドレスでアクセル、フロントブレーキ、リアブレーキ、クラッチの値をfloatでギアがアップしたときに1 ダウンしたときに-1 それ以外は0をintで送信しています。

OSCのデータを取得したときにOn Osc Receivedが呼ばれます。
Addressには、送信側で指定したOSCのアドレス名が出力されますが、今回はアドレスは1つのみなので使用していません。
ここでは、各値を受け取ってeventを発生させています。
各値を取得するとイベントが呼ばれ、このイベントを親のBPでバインドしバイクとライダーを動作させています。

14_OSCインプット

15_OSCバインド

親BPでのバインド。
親BP内のコンポーネントでChildActor Componentを追加し、OSCを受信しているInputOSCを設定します。
親BPではInputOsc ActorからGetChildActorでChildActor Componentを取得し、InputOSCへとキャストします。

26_dmxSender

送信側はもっとシンプルで、
SendOscノードにAddressとDataを入れるだけになります。
ここでは、スピードに応じた風量を0〜1のfloatとしてdoctorMXに対しておくっています。
DMXのチャンネル1を変更するので、アドレス: /dmx/1 引数をつけて送ります。

参考:DoctorMX
http://kuwatec.co.jp/doctormx/
http://kuwatec.co.jp/doctormx/……s/osc.html

ReceiverとSenderのそれぞれの受信ポートと送信先IP/portは、「プロジェクト設定」の「プラグイン」の中の「OSC」の中で設定します。
Receiverはここで設定したポートにくるメッセージを受信します。
Senderはここで指定したIP/portに対してメッセージを送るようになります。

設定を変更したときに、上部の「デフォルトとして設定」を押してください。
デフォルトとして設定しないでも、エディタ上では反映されるのですが、ビルド時には設定が反映されません。
設定することで、ビルドしたときにもこの設定内容が反映されるようになります。

16_プラグイン設定

※UE4-OSCとoFでのLAN環境通信での注意点
UE4-OSCのレシーバーとoFのセンダーで通信するときに、local環境内だと正常に動作するのですが、LAN環境だとUE4がクラッシュします。Unityで作成したセンダーではクラッシュしないので、何かしらUE4-OSCまたはoFのoscに問題があるのかと思います。 今回のコンテンツではセンシングPCと描画用PCの通信ができたのが開催日前日だったため、[ センシングPC / oF Sender] > [ 描画用PC / Unity Receiver – Sender > UE4 Receiver] とトリッキーなことをして回避しました。

 

ヘッドトラッキング位置によるVRライダーのボーン制御

ヘッドトラッキングの位置や回転を取得するには GetOrientation and Positionで取得できます。
この取得した位置に頭のIKターゲット位置を合わせます。
参考:【UE4+OculusRift】HMDカメラの座標を取得する方法 (@pafuhana1213さん)

カメラの位置に合わせて、VRライダーの体を制御するために
UEでのSkeletalMesh(ボーンの入っているメッシュ)のアニメーションの制御する為にAnimationBlueprintを作成します。
AnimationBlueprintはSkeletalMeshの設定のAnimationで
Animation Mode: Use Animation Blueprint
Animation Blueprint Generated Class: AnimationBP
で指定します。

AnimationBlueprintはほかのBlueprintと違い、EventGraphの他にAnimGraphというものがあります。
AnimGraphでは、専用のノードを使用しSkeletalMeshのアニメーション/ボーンの制御を行うことができます。
AnimGraphのノードだけでもかなりの量があり、いろいろなことが出来そうです。

Animation BlueprintからBlueprintを参照するには Try Get Pawn Owner を使用します。

スライド2

↑BlueprintとAnimation Blueprintの関係。

13_ライダーAnimGraph

↑ライダーを制御するためのAnimation BlueprintのAnimGraph

IKによるボーン制御ですが、
AnimGraphには、TwoBoneIKとFABRIKがあります。
TwoBoneIKはその名前のとおり、腕や足など2つのボーンからなるボーンの制御にむいています。
FABRIKは多間接などのときの使用がよいでしょう。

腕と足は、基本のアニメーションをベースに、TwoBoneIKを使用して頭位置に応じてボーンを制御しています。

20_TwoBoneIK

手の場合を例にとって説明すると、
TwoBoneIKでは、
・IKの先端となる (EndEffectorを目指す)「IKBone」
・IKの目標とする位置「EndEffector」
・間接の目標とする位置「JointTarget」
を指定します。
StretchLimitsはどういう動作になるのかイマイチわかりませんでした、、、
それぞれの座標系をどのように扱うかをEffectorLocationSpace, JointTargetLocationSpaceで設定できるのですが、ここではWorldSpaceを指定します。
AlphaがIKの影響度となります。0だと影響なし(元のアニメーションのまま)、1だとIKの影響度が最大(EndEffectorの方向にIKBoneが向く)となります。

両手両足のEndEffectorをそれぞれハンドルとステップ辺りに設定し、
頭のEndEffectorをOculusのヘッドトラッキング位置に設定します。

JointTargetは肘や膝の向く方向になるので適当に調整、頭のJointTargetは背中後方に設定します。
JointTargetの位置をヘッドトラッキング位置に合わせてへ変化させることでもっとライダーの動きが豊かになると思います。

これで、ヘッドトラッキング に合わせてライダーが動作するようになります。

アクセル開度にあわせた、右手首の動きは右手のEndEffector位置を開度に合わせて移動させ、それにあわせて右手首のボーンを回転させることで実現しています。

※今回のバージョンでは胴体もTwoBoneIKを使用したのですが、FABRIKの方が良かったかもしれません。
FABRIKを使用し後日調整したバージョン
後日、再調整したバージョンについて書ければと思います。LeapとOculusをIKで組み合わせるのも面白そうですね。

 

■その他

タイムラインによる値の変更

今回、バイクの走行音、走行による振動、スピードによる風量などを変化させるために、パラメーターとしてタイムラインを使用しています。
タイムラインはアニメーションを再生する他に、「SetNewTime」と「New Time」インプットを使用することで、タイムライン内で指定したグラフの値を取得することができます。
下の例では、バイクのスピードをインプットとして使用して、Updateでグラフ内の値「speed」を取得して、サウンドのPitchやVolumeを変化させています。
グラフ内は、横軸がスピード(km/h)、縦軸が0〜1の出力の変化となります。
0km/hからゆっくりと値が上昇し、320km/hであたりで出力の最大値となります。

タイムラインを使用することで、見た目にも分かりやすくノードもすっきりと調整もしやすく実装することができます。

18_サウンド

17_サウンドカーブ

 

Rerouteがいろいろと使いやすい

本来の使いかたと違うかもしれませんが、今回「Reroute」が役に立ちました。
BPが複雑になってくると、どこがどの処理の流れなのか?値なのか?分からなくなってきます。要所要所でRerouteをつかいコメントを入れてあげることで、ある程度処理の把握がしやすくなります。
また、いくつかの動きを比較したい場合などRerouteから処理のながれを作成し、元の流れに繋ぎ変えることで処理の変更もやりやすくなります。もちろん、もっと規模がおおきくなると関数などでまとめておくのがよいのでしょうが。
 
Oculus、UEでの最適化

OculusでのUEの最適化については、@junyashさんのOculus向けのコンテンツをUE4で制作するためのノウハウ共有♪(Oculus向けの最適化とか品質向上とか) を参考にさせていただきました。「hmd sp 100」が抜群に効果的でした。
記事にはUE4の最適化方法についてもいろいろと書かれているので、Oculus以外でも参考になりそうです。

UEを初めてつかってみて、すべてがBPのようにノードベースで形成されていて驚きでした。
Shaderもサウンドもなにもかも!
Shaderなどはあまり触れなかったので、もっと質感をあげればとおもいます。
あと、バイクの動きももっと調整できればと。
もっと使い方を把握できると素早くいろいろとつくれそうです。

今回、初めてUE4でコンテンツをつくるにあたり、web上のさまざまな情報に助けられました。こんなにすごいゲームエンジンがたくさんの人が使える環境、時代であるということに興奮しています!

明日はnasubi_sさんの「地味に超便利プロパティマトリックスあたりについて書きます。」です。
プロパティマトリックス便利そうなので楽しみです。

There are no comments.

Please Leave a Reply

TrackBack URL :

pagetop