HoloLensアプリ「AirNotes」ストア掲載&技術メモ

Microsoft主催のHoloLensアプリケーション開発コンテスト用に作成した「AirNotes」がMicrosoft Storeで公開されました。

AirNotesは、簡単に言えば実世界に付箋を貼るアプリケーションです。
HoloLensで付箋を作るというアイデアは、既にてんちょーさんが作られた、HoloStickyというアプリで実現されています。日本語対応の音声認識や、ペリッとはがれるUIなど、非常に完成度が高いです。


付箋のアイデアをお借りして、今回はコンテストの「ビジネス部門」に応募しようと考え、法人向けのビジネスモデルも含めて考えてみました。
コンセプトとしては「場所に紐づいたマニュアル」とし、ユースケースとしては、例えば小売店などの施設で、新人研修のためのコンテンツ(テキスト・画像・動画など)を必要な「場所」に配置し、それを順番に閲覧する機能があれば、HoloLensがインストラクター代わりになる、というものです。
アプリの動作画面はこちらです


実装できたのは本当に基本的な機能だけで、ひとまずコンテストには間に合ったものの、実用的なアプリにするには下記の機能などが足りないと考えています。
・Web管理画面を使ったコンテンツ作成機能
・利用者からのフィードバックを受ける機能
・シェアリングによる複数人での使用
・日本語の音声認識(外部APIの使用料金の算段も含めて)
これらは今後実装していきたいと思いますが、備忘録としてひとまず現在の形での技術メモを残しておきたいと思います。
今回も皆様のブログや調査結果に大変お世話になりました。

WorldAnchorの設置と削除

作成したノート(付箋)は、WorldAnchorによって空間マッピングに固定されます
WorldAnchorの使い方は、以前このブログでも取り上げましたが、HoloToolkitに入っている「TapToPlace.cs」がわかりやすいです。
TapToPlace自体が、アタッチされたオブジェクトを空間に固定する、という付箋にぴったりの機能があるので、AirNotesでもかなり流用させてもらっています。
生成したノートにWorldAnchorをつけるのは単純で、下記の構文となります。

WorldAnchorManager.Instance.AttachAnchor(gameObject);

ところが、今回はノートの削除機能があります。この時に、例えば下記のようにするとExceptionで落ちます。

WorldAnchorManager.Instance.RemoveAnchor(gameObject);
Destroy(gameObject);

WorldAnchorManagerは、操作対象をキューに入れてから順次操作を行います。
そのため、Destoryしてしまうと操作対象が見つからずに例外となってしまうためです。
回避策として、下記のようにしました。

if(WorldAnchorManager.Instance.AnchorStore != null)
{
      WorldAnchorManager.Instance.AnchorStore.Delete(gameObject);
}
Destroy(gameObject);

WorldAnchorManagerのAnchorStoreから直接削除する方法になります。

WorldAnchorの復元

AirNotesでは、アプリを再起動したときにノートを復元するため、起動時にWorldAnchorの情報からノートを生成しています。

void AnchorStoreReady(WorldAnchorStore store)
{
    //アンカーの復元
    string[] ids = store.GetAllIds();
    foreach (string objName in ids)
    {
        //アンカーの分Instantiateする
        var markerObj = Instantiate(MarkerPrefab);
        //WorldAnchorをLoadする
        markerObj.GetComponent<myTapToPlace>().IsBeingPlaced = true;
        store.Load(objName, markerObj); //Attachしてはいけない
        markerObj.GetComponent<myTapToPlace>().IsBeingPlaced = false;
    }
}

WorldAnchorがロードされた時にAnchorStoreReadyが呼ばれるように定義し、その中でWorldAnchorStoreの分だけループしつつノートをInstantiateして、WorldAnchorStoreのLoadメソッドでWorldAnchorを設置しています。
このあたりの挙動に関しては@dy_karousさんの記事がとても詳しいです。
http://blog.d-yama7.com/archives/549

AirTapの扱い

AirNotesでは、空間マッピングをタップすると新しいノートが生成される、という仕様にしました。これ自体は直感的でいいと思ったのですが、別の困った問題が出ました。
どこでもタップできるように、

InputManager.Instance.AddGlobalListener(gameObject);

を入れたのですが、そうすると、UIボタンのタップ時などにも反応してしまい、意図しない動作をしてしまいました。
回避方法は幾つかあると思うのですが、時間もなかったため、「モード」で場合分けをしてAirTapを処理するようにしました。

public void OnInputClicked(InputClickedEventData eventData)
{
    switch (behaveMode)
    {
        case EnumBehaveMode.CreateNote:
            //SpatialMapping以外のものにRaycastしている場合は終了
            if (IsExceptSpatialMappingRaycast(CameraCache.Main.transform.position,CameraCache.Main.transform.forward)) return;
            //SpatialMappingに当たっている位置を検出
            Vector3 placementPosition = GetPlacementPosition(CameraCache.Main.transform.position, CameraCache.Main.transform.forward, DefaultGazeDistance);
            //ノート生成
            var MarkerID = "N" + DateTime.Now.ToString("yyyyMMddHHmmssfff");
            var markerObj = Instantiate(MarkerPrefab, placementPosition, Quaternion.identity);
            markerObj.name = MarkerID;
            markerObj.GetComponent<myTapToPlace>().IsBeingPlaced = true;
            markerObj.GetComponent<noteBehavior>().SetNoteColor(color: normalColor);
            noteList.Add(markerObj);
            ...

こういったUIの操作が透過してオブジェクトの操作になったりする現象はよくあるので、きちんと対応方法をまとめておきたいと思います。

PhotoCapture

写真を撮ってファイルに保存しています。ほぼ公式チュートリアルと同じやり方です。

Learn how to capture a photo to a file or to a Texture2D, how to capture a photo and interact with the raw bytes, and how to capture a video.
ホロモンさんによる解説もとてもわかりやすいです。
http://bluebirdofoz.hatenablog.com/entry/2018/03/07/093557

音声認識

HoloToolkitに用意されているDictationManagerを使うと、英語の音声認識を利用できます。
基本的な仕組みは@decchiさんの「Holoの嫁と会話するためのステップバイステップ」がとてもわかりやすいです。

不思議なメガネをかけるとそこにはバーチャルな嫁※1が。そんなマンガやアニメのようなこともHoloLensによって現実に近づいてきました。この記事ではバーチャルな嫁と会話するためのステップを説明します。※1 一般社会におけるエージェントや秘書と読み替えてください…
今回は、AirTapで音声認識をストップしたりと少し機能を追加したのですが、その際には下記のコードが参考になりました。
https://codeholo.com/2018/03/17/dictationexamplehololens/

ストア申請

プログラムではなくMicrosoft Store申請の話なのですが、CameraのCapabilityを使用しているのに、プライバシーポリシーへのリンクが存在しない、ということでリジェクトされました。
CapabilityはInternet AccessやSpatial Perception、Micなどいろいろありますが、カメラは例外的に厳しいポリシーが要求されるようです。
急いでWordPressの固定ページを用意して、「撮影した写真はローカルに保存され、外部に送信することはありません」という内容を簡単に英語で書いて再申請し、無事通りました。

スポンサーリンク

シェアする

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

フォローする

スポンサーリンク