MRが楽しい

MRやVRについて学習したことを書き残す

Meta QuestのPersist Content Across Sessionsのドキュメントを読む その2

本日は Meta Quest の技術調査枠です。
Meta QuestのPersist Content Across Sessionsのドキュメントページが日本語訳されていなかったので参照ページを翻訳しつつ読みました。

前回記事

以下の前回記事の続きです。
bluebirdofoz.hatenablog.com

OVRSpatialAnchorコンポーネント

OVRSpatialAnchor コンポーネントは空間アンカーのライフサイクルをカプセル化したものです。
(作成、破棄、ストレージへの保存、消去など)

Unityライフサイクル 効果
Start() 新しいアンカーを作成します(既存のアンカーから割り当てた場合を除く)
Update() GameObject の transform を空間アンカーのポーズから設定します
Destroy() 空間アンカーを消去します

空間アンカーは Unity では System.Guid で表される一意の識別子(UUID)によって識別されます。
UUID はアプリケーションやセッションのライフタイムに関係なく保持されます。

空間アンカーの作成

Meta Quest のランタイムで新しい空間アンカーを作成するには任意の GameObject に OVRSpatialAnchor コンポーネントを追加します。

gameObject.AddComponent<OVRSpatialAnchor>();

OVRSpatialAnchor コンポーネントインスタンス化の次のフレームで現在の transform に新しいアンカーを生成します。
空間アンカーの基礎となるランタイムは非同期なので UUID はすぐに有効にはなりません。

Meta Quest ランタイムで空間アンカーを正常に作成すると OVRSpatialAnchor コンポーネントは自動的に transform を更新します。
空間アンカーは時間の経過とともに微妙にずれることがあるため、この更新が必要になります。

空間アンカーの保存

OVRSpatialAnchor の Save メソッドを使用すると空間アンカーを永続化することができます。

public void Save(SaveOptions saveOptions, Action<OVRSpatialAnchor, bool> onComplete = null)

saveOptions パラメータは空間アンカーをローカルストレージとクラウドストレージのどちらに保存するかを決定します。
他のユーザーとアンカーを共有するにはまずクラウドストレージに保存する必要があります。

保存操作は非同期です。保存操作が完了したときに通知するデリゲートをオプションで指定することができます。
デリゲートは以下の2つの引数を取ります。
・OVRSpatialAnchor: 保存する空間アンカー
・bool: 操作が成功したか

GetComponent<OVRSpatialAnchor>().Save((anchor, success) =>
{
    ShowSaveIcon = success;
});

空間アンカーの読み込み

空間アンカーの読み込みには幾つかの段階があります。

1.未割り当ての空間アンカーの読み込み

割り当てされていない空間アンカーのコレクションをロードします。
未割り当ての空間アンカーとは以前にストレージに保存したが、まだ OVRSpatialAnchor コンポーネントに割り当てていない空間アンカーのことです。
読み込み操作は非同期で動作し、完了するまでに複数のフレームがかかることがあります。

public static bool LoadUnboundAnchors(LoadOptions options, Action<UnboundAnchor[]> onComplete)

LoadOptions は読み込みする UUID の明示的なリストを必要とします。
この操作は非同期なので、結果を処理するために onComplete コールバックを利用する必要があります。

空間アンカーをローカライズする

空間アンカーをローカライズすると、システムは現実世界における空間アンカーのポーズを決定します。
空間アンカーに関連するコンテンツがある場合、関連するコンテンツをインスタンス化する前に空間アンカーをローカライズしておく必要があります。
この操作がすぐに必要でない場合は、この手順を省略することができます。

public void Localize(Action<UnboundAnchor, bool> onComplete = null, double timeout = 0)

この段階も非同期です。
空間アンカーがユーザーの背後にあることなどが原因で、空間アンカーが認識できない箇所にあったり、空間のマッピングが不十分だったりすると、本処理が失敗することがあります。
その場合は空間アンカーを再びローカライズさせるか、ユーザが周囲を見回すように誘導することも考えられます。
空間アンカーによってはすでにローカライズされている場合があります。この場合は、UnboundAnchor の Localized プロパティを使用して確認できます。

public bool Localized { get; }

各空間アンカーを OVRSpatialAnchor に割り当てる

空間アンカーは OVRSpatialAnchor コンポーネントに割り当てて、そのライフサイクルを管理し、他の機能(保存、消去、破壊など)にアクセスできるようにします。
OVRSpatialAnchor コンポーネントインスタンス化して後、すぐに未割り当ての空間アンカーを割り当てる必要があります。

void OnLocalized(OVRSpatialAnchor.UnboundAnchor unboundAnchor, bool success)
{
    if (!success) return;

    var pose = unboundAnchor.Pose;
    var spatialAnchor = Instantiate(_anchorPrefab, pose.position, pose.rotation);
    unboundAnchor.BindTo(spatialAnchor);
}

メモ:既存の空間アンカーにバインドされていない OVRSpatialAnchor は最初のフレームの後に新しい空間アンカーを作成します。

空間アンカーを消去する

ストレージから空間アンカーを消去します。消去操作は非同期です。
消去操作が完了したときに通知するデリゲートをオプションで指定することができます。
デリゲートは以下の2つの引数を取ります。
・OVRSpatialAnchor: 消去する空間アンカー
・bool: 操作が成功したか

GetComponent<OVRSpatialAnchor>().Erase((anchor, success) =>
{
    ShowSaveIcon = !success;
});

空間アンカーを破棄する

OVRSpatialAnchor コンポーネントを破棄すると Meta Quest ランタイム内の空間アンカーも破棄されます。
アンカーの破棄は現在のランタイムインスタンスのみを破棄することに注意してください。ストレージにあるアンカーには影響しません。