HoloLensの中でも特に「HoloLensらしい」機能の1つ、WorldAnchorを使ってみたいと思います。
WorldAnchorについては既にだいぶ前に先駆者の皆様がわかりやすいブログを書かれており、とても参考になるので、ご一読をお勧めします。
WorldAnchorは、オブジェクトを空間上のある点に固定する、という役割を持っています。とはいえ、HoloLensアプリでの座標系は、起動したときのカメラの位置が原点になるので、アプリ実行ごとに固定されるワールド座標の位置は変化します。
では、何に対して固定を行うかというと、HoloLensが持っている空間マッピングの情報(Spaces)に対して固定を行う、という高度なことを行っています。HoloLensが持っている高性能なDepth Censerがフル活用されているというわけですね。
HoloLensアプリを落としてもWorldAnchorの情報は残すことができますので、ある空間(部屋など)で一度WorldAnchorを使って位置を固定すれば、そのオブジェクトはずっとその場所に残ります。
WorldAnchorを直接オブジェクトに「AddComponent<WorldAnchor>()」することでもアンカーを設置できるのですが、MixedRealityToolkit-Unityには、WorldAnchorManagerという便利なスクリプトが入っていますので、こちらを経由して使うのが便利です。
WorldAnchorManagerを空のGameObjectなどにアタッチするだけで使用できるようになります。なお、アプリを終了してもアンカーを削除しないようにするには、インスペクタで「Persistent Anchors」にチェックを入れてください。
WorldAnchorを設置する時には、下記を使用します。
WorldAnchorManager.Instance.AttachAnchor(対象のGameObject,"一意のアンカー名");
ただし、既にアンカーが設定されているGameObjectに対してさらに設定しようとすると例外が発生しますので、先に削除を行ってください。
WorldAnchorManager.Instance.RemoveAnchor(対象のGameObject);
アプリ起動後に、既に保存されているアンカーを呼び出して復元することができます。ただし、これも前述の通り、アンカーは空間マッピング情報に基づいて計算されるので、呼び出すのに時間がかかります。なので、アンカーの準備完了を待ってから呼び出す必要があります。
方法としては、下記の方法でイベントを仕込んでおき、WorldAnchorStoreのインスタンスが返ってきたところで処理を行います。(一例です)
void Start() { WorldAnchorStore.GetAsync(AnchorStoreReady); } void AnchorStoreReady(WorldAnchorStore store) { foreach(string id in store.GetAllIds()) { if(id == "一意のアンカー名") { WorldAnchorManager.Instance.AttachAnchor(対象のGameObject, "一意のアンカー名"); } } }
WorldAnchorの特性と相性がいいのはVuforiaではないでしょうか。例えば家具配置シミュレーターを作成して家具の位置を指定する時に、最初はマーカーで家具を設置し、その状態で家具にWorldAnchorを設置すれば、それ以降はマーカーなしでもずっとその場所に固定することができます。
これも一例ですが、Vuforiaのマーカー発見イベントを使って、5秒間マーカーを見ているとWorldAnchorが設置されるようにしてみました。VuforiaのDefaultTrackableEventHandler.csを改造します。
public GameObject objToPlace; bool Placing = false; int timeToPlace = 5; (中略)もともとのVuforiaのコード protected virtual void OnTrackingFound() { (中略)もともとのVuforiaのコード if(!Placing) { Placing = true; objToPlace.transform.position = gameObject.transform.position + オブジェクトの高さ分のオフセット; objToPlace.transform.rotation = gameObject.transform.rotation; StartCoroutine(placeCountDown()); } } IEnumerator placeCountDown() { while (timeToPlace >= 0) { yield return new WaitForSeconds(1); timeToPlace--; } WorldAnchorManager.Instance.AttachAnchor(objToPlace,anchorName); } protected virtual void OnTrackingLost() { (中略)もともとのVuforiaのコード if (Placing) { Placing = false; StopCoroutine(placeCountDown()); timeToPlace = 5; } }
ざっくりしたコードですので、オブジェクト位置の調整や既存アンカー(AnchorStore経由)のチェックなど、幾つか追加処理が必要になるかと思います。