Apple Vision Pro の WorldAnchor をUnityで使う

## はじめに
Apple Vision Proの [WorldAnchor](https://developer.apple.com/documentation/visionos/tracking-points-in-world-space) をUnityで利用する方法を検証しました。

### [World Anchor](https://developer.apple.com/documentation/arkit/worldanchor)とは
AR Kitで提供されている、現実空間の位置を記録し追従する機能です。

Vision Proを一回外し、アプリ終了、デバイス電源OFFしても、次にアプリを立ち上げた時に現実空間の同じ位置にコンテンツを再表示できます。

Unityでは、AR Foundationで提供されており、Vision Proでも使えます。

ただし、Vision Proはデバイス間でアンカーを共有する[ARWorldMap](https://developer.apple.com/documentation/arkit/arworldmap)には対応していません。


### 開発環境
* Unity 2022.3.27f1
* PolySpatial 1.3.1
* AR Foundation 5.1.5

### 公式サンプル
手っ取り早く試すには公式サンプルを触ってみましょう。
World Anchorを使ったサンプルはApple visionOS XR PluginのSampleにあります。
Built-in用 と URP用の２種類がありますので、プロジェクトに合った方をimportします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43734/3356d440-5212-b3c9-bfea-b23d212bed74.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43734/d0fea55b-7272-447f-1189-d8411ce8c8a8.png)

エアタップした場所にAnchorが配置されます。
Digital Crown長押しやデバイスを再起動してもアプリを起動すれば同じ位置にAnchorオブジェクトが配置されている事を確認出来ます。

UnityEditorでも一応動きますが、実機で試すのが確実です。
Vision OSシミュレーターでは動作しません。

また、サンプルはFully-immersive(VR)ですが、Immersive(Unbounded MR)でも動作します。
Share Space(Bounded)では動作しません。

### 解説

公式ドキュメント

https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@5.1/manual/features/anchors.html

AR Anchorを利用するには、以下のコンポーネントが必要になります
- [AR Anchor Manager](https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@5.1/manual/features/anchors.html)
- [AR Session]()

### Anchorを追加する
[ARAnchor](https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@5.1/api/UnityEngine.XR.ARFoundation.ARAnchor.html)コンポーネントをGameObjectに貼るだけです。

```csharp
gameObject.AddComponent<ARAnchor>();
```

非常に簡単です。
コンテンツは、このGameObjectの子にする事でアンカーに追従させられます。

### Anchorを削除する
ARAnchorコンポーネントを削除するだけです。GameObjectごと削除してもOKです。

### 複数のAnchorをIdで区別する
ARAnchorは複数配置出来ます。

AR Anchorにはユニークな[TrackableId](https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@5.1/api/UnityEngine.XR.ARFoundation.ARTrackable-2.html#UnityEngine_XR_ARFoundation_ARTrackable_2_trackableId)が割り当てられるので、配置したAR Anchor毎に異なるコンテンツを割り当てたい場合にはこのTrackableIdを使うと良いでしょう。

#### TrackableIdの取得
```csharp
TrackableId trackabeId = GameObject.GetComponent<ARAnchor>().trackabelId;
```

####  ARAnchorManagerからTrackabelIdでARAnchorを取得
```csharp
anchor = m_AnchorManager.GetAnchor(trackedId);
```

### ARAnchorを再起動後に取得する
このTrackableIdはアプリ再起動時に復元されるARAnchorにも引き継がれています。

復元されたARAnchorをTrackableId指定で取得したい場合にはStringとしてPlayerPrefsなどに保存しておくと良いでしょう。

```csharp
// AnchorのTrackableId保存
public void RecordAnchor(string key, ARAnchor anchor)
{
    PlayerPrefs.SetString($"ARAnchor_{key}", anchor.trackableId.ToString());
}
```
```csharp
// TrackableIdからのARAnchor取得
public async UniTask<ARAnchor> GetAnchor(string key)
{
    string trackedIdString = PlayerPrefs.GetString($"ARAnchor_{key}");
    
    TrackableId trackedId = new TrackableId(trackedIdString);

    ARAnchor anchor = manager.GetAnchor(trackedId);

    return anchor;
}
```
注意点として、ARAnchorの復元には時間がかかります。
アプリ起動直後に実行すると取得に失敗する場合があるので適切な待ち処理を実装してください。

## 全てのAnchorを破棄する

> アンカーは一般にリソースを大量に消費するオブジェクトであるため、控えめに使用する必要があります。([引用元](
https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@5.1/manual/features/anchors.html))


とある通り、使い過ぎに注意しましょう。
ARAnchorは配置すると残り続けるので、要らなくなったら消しましょう。

* 公式サンプルのClearWorldAnchorsから引用
```csharp
[SerializeField]
ARAnchorManager m_AnchorManager;

static readonly List<ARAnchor> k_AnchorsToDestroy = new();


public void ClearWorldAnchors()
{
    var anchorSubsystem = m_AnchorManager.subsystem;

    k_AnchorsToDestroy.Clear();
    foreach (var anchor in m_AnchorManager.trackables)
    {
        if (anchor == null)
            continue;

        k_AnchorsToDestroy.Add(anchor);
    }

    foreach (var anchor in k_AnchorsToDestroy)
    {
        Destroy(anchor.gameObject);
    }

    k_AnchorsToDestroy.Clear();
}
```

以上です。

## 参考

https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@5.1/manual/features/anchors.html


https://qiita.com/afjk/items/74c35b254f31a0e3646b


[Unity Discussions:World Anchors in MR](https://discussions.unity.com/t/world-anchors-in-mr/318422)