Unity でのローカル アンカー転送
Azure Spatial Anchors を使用できない状況では、ローカルアンカー転送により、1 つの HoloLens デバイスがアンカーをエクスポートして 2 番目の HoloLens デバイスによってインポートできるようになります。
Note
このアプローチによるアンカー呼び戻しは Azure Spatial Anchors ほど安定したものではなく、iOS と Android デバイスはサポートされません。
SpatialPerception 機能の設定
空間アンカーをアプリで転送するためには、SpatialPerception 機能を有効にする必要があります。
SpatialPerception 機能を有効にするには:
- Unity エディターの [Edit]\(編集\) > [Project Settings]\(プロジェクトの設定\) > [Player]\(プレーヤー\) で、[Player Settings]\(プレーヤーの設定\) ペインを開きます
- [Windows Store]\(Windows ストア\) タブをクリックします
- [公開設定] を展開し、[機能] 一覧で [SpatialPerception] 機能のチェック ボックスをオンにします。
Note
Visual Studio ソリューションに既に Unity プロジェクトをエクスポートしている場合は、新しいフォルダーにエクスポートするか、または手動で Visual Studio で AppxManifest にこの機能を設定する必要があります。
アンカー転送
Namespace: UnityEngine.XR.WSA.Sharing
型: WorldAnchorTransferBatch
WorldAnchor を転送するには、転送するアンカーを設定する必要があります。 一方の HoloLens のユーザーがその環境をスキャンし、共有するアンカーの空間内のポイントを手動またはプログラムを使用して選択します。 そのポイントを表すデータをシリアル化して、このエクスペリエンスにおける他のデバイスに転送して共有することができます。 その後それぞれのデバイスは、アンカー データを逆シリアル化して、その空間ポイントを特定しようと試みます。 アンカー転送が正しく機能するためには、アンカーによって表されたポイントを特定できる程度に環境が各デバイスによってスキャンされている必要があります。
セットアップ
このページのサンプル コードには、初期化する必要のあるフィールドがいくつかあります。
- GameObject rootGameObject は、WorldAnchor コンポーネントの追加先となる Unity の GameObject です。 共有エクスペリエンスに参加するいずれかのユーザーが、この GameObject を配置して、他のユーザー向けにそのデータをエクスポートします。
- WorldAnchor gameRootAnchor は、rootGameObject に追加される UnityEngine.XR.WSA.WorldAnchor です。
- byte[] importedData は、各クライアントがネットワーク経由で受信するシリアル化されたアンカーのバイト配列です。
public GameObject rootGameObject;
private UnityEngine.XR.WSA.WorldAnchor gameRootAnchor;
void Start ()
{
gameRootAnchor = rootGameObject.GetComponent<UnityEngine.XR.WSA.WorldAnchor>();
if (gameRootAnchor == null)
{
gameRootAnchor = rootGameObject.AddComponent<UnityEngine.XR.WSA.WorldAnchor>();
}
}
エクスポート
エクスポートするために必要なのは、WorldAnchor とその名称だけです。受信側のアプリにとって意味のある名称が必要になります。 共有アンカーをエクスポートするには、共有エクスペリエンスに参加するいずれかのクライアントが、次の手順を実行します。
- WorldAnchorTransferBatch を作成します
- 転送する WorldAnchors を追加します
- エクスポートを開始します
- データが利用可能になったら OnExportDataAvailable イベントを処理します
- OnExportComplete イベントを処理します
WorldAnchorTransferBatch を作成して転送内容をカプセル化し、それをバイトにエクスポートします。
private void ExportGameRootAnchor()
{
WorldAnchorTransferBatch transferBatch = new WorldAnchorTransferBatch();
transferBatch.AddWorldAnchor("gameRoot", this.gameRootAnchor);
WorldAnchorTransferBatch.ExportAsync(transferBatch, OnExportDataAvailable, OnExportComplete);
}
データが利用可能になったら、そのバイトをクライアントに送信します。または、そのバイトをバッファーに格納しておき、データのセグメントが利用可能になったときに任意の手段で送信します。
private void OnExportDataAvailable(byte[] data)
{
TransferDataToClient(data);
}
エクスポートの完了後、データを転送していてシリアル化に失敗した場合は、そのデータを破棄するようにクライアントに指示します。 シリアル化に成功した場合は、すべてのデータが転送済みであること、またインポートを開始できることをクライアントに伝えます。
private void OnExportComplete(SerializationCompletionReason completionReason)
{
if (completionReason != SerializationCompletionReason.Succeeded)
{
SendExportFailedToClient();
}
else
{
SendExportSucceededToClient();
}
}
インポート
送信側からすべてのバイトを受信したら、そのデータを再び WorldAnchorTransferBatch にインポートし、ルート ゲーム オブジェクトを同じ物理位置にロックすることができます。 注: インポートが一時的に失敗し、再試行が必要となる場合があります。
// This byte array should have been updated over the network from TransferDataToClient
private byte[] importedData;
private int retryCount = 3;
private void ImportRootGameObject()
{
WorldAnchorTransferBatch.ImportAsync(importedData, OnImportComplete);
}
private void OnImportComplete(SerializationCompletionReason completionReason, WorldAnchorTransferBatch deserializedTransferBatch)
{
if (completionReason != SerializationCompletionReason.Succeeded)
{
Debug.Log("Failed to import: " + completionReason.ToString());
if (retryCount > 0)
{
retryCount--;
WorldAnchorTransferBatch.ImportAsync(importedData, OnImportComplete);
}
return;
}
this.gameRootAnchor = deserializedTransferBatch.LockObject("gameRoot", this.rootGameObject);
}
LockObject の呼び出しによってロックされた GameObject には WorldAnchor が設定され、それによって GameObject はワールド内の同じ物理位置に維持されますが、Unity の座標空間における位置は他のユーザーとは異なる場合があります。