de:code2019できいてAzureSpatialAnchorsは面白いなーとは思ったのですが、中身がどうなっているのか全く分からなかったので、 こちらのブログを参考にAPIを眺めてみました
Azure Spatial Anchorsとは?
クイック スタート - Azure Spatial Anchors を使用して Unity HoloLens アプリを作成する | Microsoft Docs
Spatial Anchorと呼ばれる空間の位置を覚えておく機能の永続化と共有がAzureSpatialAnchorsです。iOS、Android、HoloLensがそのサポート対象です。
Microsoft.Azure.SpatialAnchors
UWP用のSpatialAnchorを扱うネームスペースです。
Microsoft.Azure.SpatialAnchors Namespace | Microsoft Docs
見ての通り、C++なのでUnityから扱うには一手間いります。それがExampleのPluginに含まれる、AzureSpatialAnchorsBridgeです。
HoloLensであれば、UNITY_WSAで囲われた部分のNativeLibraryの部分がそうです。
自分でこれらをフル移植はするのはなかなか面倒なので、ここのPluginだけぶっこ抜くのがいいと思います。
C++ちょっとできる人はこちらを読むほうが早いかもしれません。
C++/WinRT で Azure Spatial Anchors を使用してアンカーを作成して配置する方法 | Microsoft Docs
CloundSpatialAnchor
SpatialAnchorsへ永続化されるSpatialAnchorです。
Identifierによって一意に識別が可能であり、生成時に取得できるIdentifierをDBやローカルに保持、またこの識別子を用いてSpatialAnchorを取得することができます。LocalAnchorはAnchorとしての実際のデータになります。
また、アンカーの寿命をExpirationで指定したり、PropertiesからAnchor自体に情報を付加することもできます。
// 生成したCubeにARAnchorを打つ GameObject.CreatePrimitive(PrimitiveType.Cube).AddARAnchor(); // CloudSpatialAnchorを作成する var cloudAnchor = new CloudSpatialAnchor(); cloudAnchor.LocalAnchor = cube.GetNativeAnchorPointer(); cloudAnchor.Expiration = DateTimeOffset.Now.AddDays(1);
CloudSpatialAnchorSession
SpatialAnchorの永続化や接続などはCloudSpatialAnchorSessionが担います。
ConfigurationでSpatialAnchorsのアカウントIDとアカウントKeyを設定し、各種デリゲートの登録(後述)を行います。
// Sessionを生成する var session = new CloudSpatialAnchorSession(); //アカウントを設定する session.Configuration.AccountId = "AccountID"; session.Configuration.AccountKey = "AccountKey"; //デリゲートを登録する //AnchorLocatedは、アンカーを見つかったこと、あるいは見つけられていないことを通知します //LocateAnchorsCompletedは、検索操作が完了したことを通知します session.AnchorLocated += SessionOnAnchorLocated; session.LocateAnchorsCompleted += SessionOnLocateAnchorsCompleted; //セッションを開始する session.Start();
CloudAnchorを生成する場合、アンカーを作成するのに環境データを十分に取得している必要があります。
// CloudAnchorが生成可能になる十分な状態まで待機する // RecommendedForCreateProgress: 推奨閾値の範囲で[0,1]を返す while (true) { var status = await session.GetSessionStatusAsync(); if (status.RecommendedForCreateProgress >= 1.0) { break; } else {Debug.Log($"Progress: {status.RecommendedForCreateProgress * 100f}%");} await Task.Delay(250); } // CreateForProgress: 操作に十分なレベルの範囲で[0,1]を返す if((await session.GetSessionStatusAsync()).ReadyForCreateProgress < 1) return;
AnchorをAzure Spatial Anchorsへ永続化し、Anchorの識別子も別途保存します。
Anchorの保存とAnchorの識別子の保存は別で考える必要があるのです(これがちょっとややこしい)
Redis GEO とか MySQL の GEO 管理と紐付けておいてもいいのかもしれない。GPS から近傍検索して ID を取得できれば Azure Spatial Anchors に問い合わせることが可能かも。#decode19 #cm03
— じんぐる (@xin9le) 2019年5月30日
チュートリアルではCosmosDBを使っていますが、SQLなりローカルにJSONで保存するなり何でもいいと思います。
// SpatialAnchorを永続化する await session.CreateAnchorAsync(cloudAnchor); //識別子をDBやローカルに永続化する(てきとーなメソッド) SaveAnchorIdentifier(cloudAnchor.Identifier);
続いて、Anchorが見つかった時の挙動です。
private void SessionOnAnchorLocated(object sender, AnchorLocatedEventArgs args) { UnityEngine.WSA.Application.InvokeOnAppThread(() => { if (args.Status == LocateAnchorStatus.Located) { var cube = CreateCube(); var anchor = cube.AddComponent<WorldAnchor>(); // Anchorからデータを取り出し、WorldAnchorへセットする anchor.SetNativeSpatialAnchorPtr(args.Anchor.LocalAnchor); } }, false); }
検出した後はWatcherを止めます。Watcherについては次で説明します。
private void SessionOnLocateAnchorsCompleted(object sender, LocateAnchorsCompletedEventArgs args) { args.Watcher.Stop(); }
AnchorWatcher&Criteria
先ほどは生成までのフローを行いましたが、今度は検出するところをやります。
Class AnchorLocateCriteria | Microsoft Docs Class CloudSpatialAnchorWatcher | Microsoft Docs
AnchorWatcherはAnchorCritiriaを使ってアンカーの検出を行います。Criteriaというのはふるいのことです。
Criteriaには検出対象のIdentifierを指定します。これは先ほどローカルやサーバへ保存したIdentifierを指定します。
// IDの一覧をとってくる(てきとーなメソッド) var identifies = GetIdentifies(); // 検出条件を生成する var creteria = new AnchorLocateCriteria() { Identifiers = identifies, BypassCache = true, RequestedCategories = AnchorDataCategory.Spatial, Strategy = LocateStrategy.AnyStrategy };
また、このCriteriaにはあるSpatialAnchor近傍のAnchorだけを探索するNearAnchorCriteriaも指定することもできます。
var nearCreteria = new NearAnchorCriteria(); //指定のSpatialAnchor付近のAnchorを探索する nearCreteria.SourceAnchor = targetAnchor; //範囲を指定する nearCreteria.DistanceInMeters = 100; //最大アンカー数 nearCreteria.MaxResultCount = 10;
あとはWatcherに渡すだけです。これで探索が始まり、見つかり次第先のLocatedイベントが呼ばれるようです。
session.CreateWatcher(creteria);
まとめ
Sessionを作って、Anchorを登録する。Watcherを作って探索を行い、Locatedから結果を受け取る。以上が基本的なSpatialAnchorを扱う上での基本要素になります。
次は何か作るところまでやりたい。