床が落ちるHoloLensアプリ「FloorFalling MR」作成メモ

シェアする

  • このエントリーをはてなブックマークに追加

先日、「FloorFalling MR」というアプリをWindowsストアに申請しました。複合現実を使って「自分の部屋の床が落ちる」という体験をするものです。(9/19現在、MSの審査は通ったのですが掲載ページが404になる状態です)
公開されました!

HoloLens 向けの Microsoft ストアからこのアプリをダウンロードします。スクリーンショットを確認し、最新のカスタマー レビューを読んで、FloorFalling MR の評価を比較してください。
※HoloLensからは検索で「floorfa・・」くらいまで入力すると出てきます。

初めは単に床が落ちるだけだったのですが、それだけだとエンタメ性が足りない、という社内のゲームプランナーの意見でいくつか要素を追加しました。結果、「部屋のどこかにいるユニティちゃんにたどり着けばゴールで、その途中の道だけは床が落ちない」という技術者(わたくし)泣かせの仕様になりました。

ということで、「FloorFalling MR」の作成に使用したコンポーネントや使用方法などをメモ的に記載しておきたいと思います。

まずは、UnityでのHoloLensアプリにほぼ必須のMixedRealityToolkit-Unity(旧 HoloToolkit-Unity)をインポートします。その中の下記のプレファブを使用します。

・HoloLensCamera
・InputManager
・BasicCursor

また、部屋の形を認識することが重要なので、

・SpatialMapping
・SpatialUnderstanding

も使用します。
SpatialMappingは空間の形状をメッシュにするもので、SpatialUnderstandingはそのメッシュの情報を使ってさらに高度なことをすることが可能です。

まずは「床」を認識する部分なのですが、SpatialMappingと、Toolkitに同梱されている「SurfaceMeshesToPlanes.cs」を使用します。この辺りは以前、下記の記事を書きましたのでご参照ください。

目指せ脱HoloLens初心者!その2(SpatialProcessing)
の続きです。 さて、次は別のサンプルを使ってSpatialMappingを更に調べてみます。 HoloToolkit-U...

この方法を使うと、床をCubeとして取得することができます。ただ、認識の誤差によって、床となるCubeが複数枚帰ってきたりするので、その中から最大サイズのものだけを採用する作りにしました。

この床に、タイルとなるCubeを敷き詰めていきます。タイルの大きさと床の大きさがわかれば割り算で個数がわかるので、縦横のループでタイルをInstantiateするようにしました。そしてタイルの親オブジェクトのPosition、Rotationを床Cubeに合わせれば、床部分がタイルになります。

次は、ゴールとなるUnityちゃんを配置する方法です。HoloLensでスキャンした空間内にオブジェクトを配置するには、 SpatialMappingに加えて、SpatialUnderstandingを使用します。こちらも下記記事で解説しています。

SpatialUnderstandingを使ってみる(その1)
前のエントリでSpatialMappingからPlaneを生成するのを行いました。 今回は、より空間マッピングを使いこなすために、Sp...
SpatialUnderstandingを使ってみる(その2)
前回、サンプルコードから、SpatialUnderstandingの配置条件・ルール・制約を使ってオブジェクトを配置する機能を見てみ...

この方法でゴールとなるUnityちゃんの位置を決めて、そこにUnityちゃんをInstantiateします。また、Unityちゃんの視線がこちらを向くようにカメラの位置に回転します。このとき、単純にカメラの位置を向かせると、Unityちゃんが上や下を向いて斜めになってしまい困りました。これは下記のようにY軸成分を0にすると改善できます。

   // カメラを向いてもらう
  var dif = Camera.main.transform.position - unityChan.transform.position;
  var rot = Quaternion.LookRotation(new Vector3(dif.x, 0, dif.z));
  unityChan.transform.rotation = rot;

そして、HoloLens装着者からUnityちゃんまでの道を作り、そこのブロックを落ちないようにする方法です。
ルートの計算にはNavMeshを使用しますが、今回はBakeできないので、動的NavMeshを使用します。

HoloLensでNavMeshといえば @tarukosuさん、@morio36さん、ということで、 下記の記事をご覧ください。

Unity 5.6 でナビゲーション機能が強化され、動的に NavMesh を構築できるようになりました。 NavMesh はキャラクターなどが移動できる面を表しており、NavMesh を設定したうえで NavMeshAgent を使えばルート探索や移動を行わせることができます。 動的に NavMesh を構築できるよ...
Unity5.6でNavMeshBuilderが強化され、動的にナビゲーションメッシュを生成し、エージェントを柔軟に適切なルートで移動させることが可能となりました。 HoloLensは環境認識に優れたMRデバイスであり、この新機能と相...

今回の場合は、SpatialMappingの結果のメッシュと、床に敷き詰めたブロックの両方の情報でルートを作成したいので、どちらにもNavMeshSourceTagをアタッチしました。また、実際に移動するエージェントはMeshRendererをdisableにして見えないようにして、ルートの結果だけを受け取るようにしました。結果はCornerのPositionの配列として扱うことができるので、Physics.SphereCastを用いて、ルート上のブロックを特定し、落下しない、というプロパティを与えました。

では、「落下する」という部分についてです。何かを落下させるにはRigidbodyをアタッチすればいいのですが、このままだとSpatialMappingの結果のメッシュが残っているので、それにぶつかって落下してくれません。メッシュ全体を消すことも可能なのですが、そうすると、壁などの障害物の情報もなくなってしまいます。また、あたり判定だけを消すこともできますが、そうすると床の下にある航空写真が見えなくなってしまいます。

なので、床部分だけを削除するのですが、これはToolkitに入っている「RemoveSurfaceVerticles.cs」で実現可能です。
メソッド RemoveSurfaceVerticesWithinBounds(IEnumerable boundingObjects) の引数として、検出された床のPlaneを指定すれば、床のメッシュのみ削除ができます。ただし、それだけだと床下部分のメッシュが微妙に残るので、public BoundsExpansionに値を入れるのですが、そうすると床上部分のメッシュまで消えたりするので、微調整のためにoffsetというプロパティを追加して、床ギリギリから床下のメッシュを削除するようにしました。

この「床ギリギリ」などのように、このアプリでは床のY座標を正確に取得することが重要となります。これも、SpataialUnderstandingでオブジェクトを配置するときと同じように、UnderstandingDLLを使用して取得できます。

float getFloorY()
{
    // Alignment information
    SpatialUnderstandingDll.Imports.QueryPlayspaceAlignment(SpatialUnderstanding.Instance.UnderstandingDLL.GetStaticPlayspaceAlignmentPtr());
    SpatialUnderstandingDll.Imports.PlayspaceAlignment alignment = SpatialUnderstanding.Instance.UnderstandingDLL.GetStaticPlayspaceAlignment();

    return alignment.FloorYValue;
}

ちなみに、下記のような「床に穴をあける」タイプですと、凹みさんのシェーダーが大活躍します。

はじめに 前回のブログで少しだけ穴あき表現について触れました。 tips.hecomi.com 本エントリでは動的に穴を開ける方法について解説したいと思います。 デモ 環境 Windows 10 Unity 5.5.1f1 ダウンロード github.com おさらい ドキュメント:Case study - Looki...

また、Unityちゃんが「こっちだよー」と話すのですが、空間上でどこにいるか分かりやすくするため、SpatialSoundを利用しています。いわゆる3Dオーディオで、どこでUnityちゃんが話しているか格段にわかりやすくなりました。

あとはUnityちゃんのアニメだったり、ゴール判定などはUnityではお馴染みの方法を使っています。

というわけで、皆様のブログや調査結果を使ってなんとか実現することができました。感謝!

スポンサーリンク

シェアする

  • このエントリーをはてなブックマークに追加

フォローする

App Store