はじめに
今回は Meta Quest の v71 から追加された グループベースの共有空間アンカー についての記事です。
Unity での実装方法として Meta XR SDK と AR Foundation + OpenXR: Meta の2つの選択肢がありますが、今回の記事では Meta XR SDK を使用します。
開発環境
- Unity 6000.1.8f1
- OpenXR Plugin 1.14.3
- Meta XR SDK 77.0.0
- Core
- Interaction
- Platform
概念

- ユーザーは各々が作成した空間アンカーをMetaのクラウド経由で他ユーザーに共有することができます。
- 共有の際にGUIDを指定し、そのGUIDを知っているユーザーのみが共有された空間アンカーにアクセスできます。
- 共有用のGUIDは何らかの方法で他ユーザーに共有する必要があります。
空間アンカーを作成する
1. 空間アンカーを有効化する
OVR ManagerのQuest FeaturesのAnchor SupportをEnabledにします。

2. GameObject を空間アンカーにする
GameObjectにOVRSpatialAnchorコンポーネントを追加することで空間アンカーにすることができます。OVRSpatialAnchorコンポーネントを追加した直後はまだアンカーの作成が完了しておらず、OVRSpatialAnchor.CreatedがTrueになるまで待機する必要があります。
public async UniTask<OVRSpatialAnchor> CreateAnchorAsync(GameObject anchorObject)
{
if (!anchorObject.TryGetComponent(out OVRSpatialAnchor anchor)) anchor = anchorObject.AddComponent<OVRSpatialAnchor>();
await UniTask.WaitUntil(() => anchor.Created);
return anchor;
}
3. 空間アンカーを永続化する
- 作成した空間アンカーを Meta Quest のローカルに保存します。
- 読み込みや削除の際に
OVRSpatialAnchor.Uuidが必要になるのでPlayerPrefsなどで別途保存しておく必要があります。
public async UniTask SaveAnchorAsync(OVRSpatialAnchor anchor)
{
var result = await anchor.SaveAnchorAsync();
if (!result.Success) throw new Exception($"Save failed with error {result.Status}.");
// anchor.Uuid を PlayerPrefs などに保存する
}
4. 永続化した空間アンカーで位置合わせをする
PlayerPrefsなどに保存してあるOVRSpatialAnchor.Uuidで永続化した空間アンカーを読み込み位置合わせをします。
public async UniTask LocalizeAnchorsAsync(IEnumerable<Guid> uuids, Action<bool, UnboundAnchor> onLocalized)
{
// PlayerPrefs などから読み込んだUUIDで空間アンカーを読み込む
var unboundAnchors = new List<UnboundAnchor>();
var result = await OVRSpatialAnchor.LoadUnboundAnchorsAsync(uuids, unboundAnchors);
if (!result.Success) throw new Exception($"Load failed with error {result.Status}.");
// 読み込んだ空間アンカーで位置合わせを行う
foreach (var unboundAnchor in result.Value)
{
unboundAnchor.LocalizeAsync(LOCALIZE_TIMEOUT_SECONDS)
.ContinueWith(onLocalized, unboundAnchor);
}
}
アンカー共有用GUIDを共有する
- ホストとなるユーザーが GUID を作成し、他ユーザーに共有します。
- 共有する方法に縛りはなく
Photonなどの外部ライブラリでの共有も可能です。 - 本記事では
Meta XR SDKに含まれるColocation Discovery機能を使用します。
1. Colocation Discovery を有効化
OVR ManagerのQuest FeaturesのColocation Session SupportをRequiredにします。

2. 【ホスト】コロケーションの宣伝を開始する
- ホストがバイナリに変換した GUID をコロケーションで宣伝します。
private async UniTask<bool> startAdvertiseAsync(ColocationSessionData data)
{
var json = JsonUtility.ToJson(data);
var bytes = Encoding.UTF8.GetBytes(json);
var result = await OVRColocationSession.StartAdvertisementAsync(bytes);
return result.Success;
}
3. 【クライアント】コロケーションの発見を開始する
- クライアントはコロケーションの発見を開始し、ホストから宣伝されている GUID を受け取ります。
public async UniTask StartDiscover(Action<OVRColocationSession.Data> onDiscovered)
{
OVRColocationSession.ColocationSessionDiscovered += onDiscovered;
var result = await OVRColocationSession.StartDiscoveryAsync();
if(!result.Success) throw new Exception($"Failed to start discovery: {result.Status}");
}
空間アンカーを共有する
1. 空間アンカーを有効化する
OVR ManagerのQuest FeaturesのShared Spatial Anchor SupportをRequiredにします。- (補足)
Shared Spatial Anchor SupportをRequiredにするとAnchor Supportも強制的にEnabledになります。
- (補足)

2. 【ホスト】空間アンカーを共有する
- 空間アンカーを GUID を指定して共有します。
public async UniTask ShareAnchorsAsync(IEnumerable<OVRSpatialAnchor> anchors, Guid guid)
{
var result = await OVRSpatialAnchor.ShareAsync(anchors, guid);
if(!result.Success) throw new Exception($"Failed to share anchors: {result.Status}");
}
3. 【クライアント】共有された空間アンカーで位置合わせをする
- GUID に紐付いて共有された空間アンカーをクラウドから取得し、位置合わせを行います。
- 位置合わせが完了すると、空間アンカーの位置や向きなどの情報を持った
UnboundAnchor構造体が取得できます。
public async UniTask LocalizeSharedAnchors(Guid uuid, Action<bool, UnboundAnchor> onLocalized)
{
var unboundAnchors = new List<UnboundAnchor>();
var result = await OVRSpatialAnchor.LoadUnboundSharedAnchorsAsync(uuid, unboundAnchors);
if(!result.Success) throw new Exception($"Load failed with error {result.Status}.");
foreach (var unboundAnchor in result.Value)
{
unboundAnchor.LocalizeAsync(LOCALIZE_TIMEOUT_SECONDS).ContinueWith(onLocalized, unboundAnchor);
}
}
4. 【クライアント】位置合わせ後のデータを元に空間アンカーを作成する
UnboundAnchor構造体のままではアンカーとして使用できないので、アンカーとするGameObjectに位置と向きを適用して紐付けます。
private void onLocalizedAnchor(bool isSuccess, UnboundAnchor unboundAnchor)
{
if (!isSuccess) return;
if (!unboundAnchor.TryGetPose(out var pose)) return;
var anchorObject = Instantiate(_anchorPrefab);
anchorObject.name = $"Anchor_{unboundAnchor.Uuid}";
anchorObject.transform.SetPositionAndRotation(pose.position, pose.rotation);
var anchor = anchorObject.AddComponent<OVRSpatialAnchor>();
unboundAnchor.BindTo(anchor);
}
おわりに
GUID の共有だけで簡単に空間アンカーの共有が実装できました。
空間アンカーの共有ができれば、その配下のオブジェクトの Transform も一致させることができます。
スペースの作成なども不要なので、見るだけのシンプルなコンテンツであれば、手軽に体験できると思います。
ただ、機能が不足している点も目についたので、次回は AR Foundation + OpenXR: Meta についてと、不足している機能について書いていこうと思います。
ぜひ次回もご覧ください。
