如何在 Unity 中使用 Azure Spatial Anchors 建立和尋找錨點
Azure Spatial Anchors 可讓您在世界各地的不同裝置間共用錨點。 其支援數種不同的開發環境。 在本文中,我們將深入探討如何在 Unity 中使用 Azure Spatial Anchors SDK,以:
- 正確設定和管理 Azure Spatial Anchors 工作階段。
- 在本機錨點建立及設定屬性。
- 將屬性上傳至雲端。
- 找出並刪除雲端空間錨點。
必要條件
若要完成本指南,請確定您:
- 已完整閱讀 Azure Spatial Anchors 概觀。
- 已完成其中一個 5 分鐘快速入門。
- 已基本了解 C# 和 Unity。
- 已基本了解 ARCore (如果您想要使用 Android) 或 ARKit (如果您想要使用 iOS)。
初始化工作階段
SDK 的主要進入點是代表您工作階段的類別。 通常您會在此類別中宣告一個欄位,用以管理您的檢視和原生 AR 工作階段。
深入了解 CloudSpatialAnchorSession 類別。
CloudSpatialAnchorSession cloudSession;
// In your view handler
this.cloudSession = new CloudSpatialAnchorSession();
設定驗證
若要存取服務,您必須提供帳戶金鑰、存取權杖或 Azure Active Directory 驗證權杖。 您也可以在驗證概念頁面中深入了解相關資訊。
帳戶金鑰
帳戶金鑰是一種認證,可讓您的應用程式向 Azure Spatial Anchors 服務進行驗證。 帳戶金鑰的用途是協助您快速開始使用。 在應用程式與 Azure Spatial Anchors 整合的開發階段,其效用尤其顯著。 因此,您可以在開發期間,將帳戶金鑰內嵌在用戶端應用程式中。 在完成開發階段時,強烈建議您改用屬於生產層級、由存取權杖支援的驗證機制,或是 Azure Active Directory 使用者驗證。 若要取得用於開發的帳戶金鑰,請造訪您的 Azure Spatial Anchors 帳戶,並瀏覽至 [金鑰] 索引標籤。
深入了解 SessionConfiguration 類別。
this.cloudSession.Configuration.AccountKey = @"MyAccountKey";
存取權杖
向 Azure Spatial Anchors 進行驗證時,使用存取權杖是更健全的方法。 特別是當您準備應用程式以進行生產環境部署時。 此方法的運作方式大致上是設定可供您的用戶端應用程式用以安全驗證的後端服務。 您的後端服務在執行階段會與 AAD 互動,並且透過 Azure Spatial Anchors 安全權杖服務來要求存取權杖。 然後,此權杖會傳遞至用戶端應用程式,並且在 SDK 中用來向 Azure Spatial Anchors 進行驗證。
this.cloudSession.Configuration.AccessToken = @"MyAccessToken";
若未設定存取權杖,您就必須處理 TokenRequired
事件,或是對委派通訊協定實作 tokenRequired
方法。
您可以在事件引數上設定屬性,以同步處理事件。
深入了解 TokenRequiredDelegate 委派。
this.cloudSession.TokenRequired += (object sender, TokenRequiredEventArgs args) =>
{
args.AccessToken = @"MyAccessToken";
};
如果您需要在處理常式中執行非同步工作,可以藉由要求 deferral
物件並加以完成來延遲設定權杖,如下列範例所示。
this.cloudSession.TokenRequired += async (object sender, TokenRequiredEventArgs args) =>
{
var deferral = args.GetDeferral();
string myToken = await MyGetTokenAsync();
if (myToken != null) args.AccessToken = myToken;
deferral.Complete();
};
Azure Active Directory 驗證
Azure Spatial Anchors 也可讓應用程式使用使用者 Azure AD (Active Directory) 權杖進行驗證。 例如,您可以使用 Azure AD 權杖與 Azure Spatial Anchors 整合。 如果企業在 Azure AD 中維護使用者,您可以在 Azure Spatial Anchors SDK 中提供使用者 Azure AD 權杖。 這麼做可讓您直接向 Azure Spatial Anchors 服務驗證屬於相同 Azure AD 租用戶的帳戶。
this.cloudSession.Configuration.AuthenticationToken = @"MyAuthenticationToken";
和存取權杖一樣,若未設定 Azure AD 權杖,您就必須處理 TokenRequired 事件,或是對委派通訊協定實作 tokenRequired 方法。
您可以在事件引數上設定屬性,以同步處理事件。
this.cloudSession.TokenRequired += (object sender, TokenRequiredEventArgs args) =>
{
args.AuthenticationToken = @"MyAuthenticationToken";
};
如果您需要在處理常式中執行非同步工作,可以藉由要求 deferral
物件並加以完成來延遲設定權杖,如下列範例所示。
this.cloudSession.TokenRequired += async (object sender, TokenRequiredEventArgs args) =>
{
var deferral = args.GetDeferral();
string myToken = await MyGetTokenAsync();
if (myToken != null) args.AuthenticationToken = myToken;
deferral.Complete();
};
設定工作階段
叫用 Start()
,讓您的工作階段能夠處理環境資料。
若要處理您的工作階段所引發的事件,請連結事件處理常式。
深入了解 Start 方法。
#if UNITY_ANDROID || UNITY_IOS
this.cloudSession.Session = aRSession.subsystem.nativePtr.GetPlatformPointer();
#elif UNITY_WSA || WINDOWS_UWP
// No need to set a native session pointer for HoloLens.
#else
throw new NotSupportedException("The platform is not supported.");
#endif
this.cloudSession.Start();
提供工作階段的畫面
空間錨點工作階段的運作方式為對應使用者周圍的空間。 這麼做有助於判斷錨點的所在位置。 行動平臺 (iOS & Android) 需要原生呼叫相機摘要,才能從您平臺的 AR 程式庫取得畫面。 相反地,HoloLens 會持續掃描環境,因此無須像在行動裝置上那樣進行特定呼叫。
深入瞭解 ProcessFrame 方法。
#if UNITY_ANDROID || UNITY_IOS
XRCameraFrame xRCameraFrame;
if (aRCameraManager.subsystem.TryGetLatestFrame(cameraParams, out xRCameraFrame))
{
long latestFrameTimeStamp = xRCameraFrame.timestampNs;
bool newFrameToProcess = latestFrameTimeStamp > lastFrameProcessedTimeStamp;
if (newFrameToProcess)
{
session.ProcessFrame(xRCameraFrame.nativePtr.GetPlatformPointer());
lastFrameProcessedTimeStamp = latestFrameTimeStamp;
}
}
#endif
將意見反應提供給使用者
您可以撰寫程式碼以處理工作階段更新的事件。 每當工作階段增進其對環境的了解時,就會引發此事件。 這麼做可讓您:
- 在裝置移動時,使用
UserFeedback
類別將意見反應提供給使用者,且工作階段會更新其對環境的了解。 若要這樣做: - 判斷何時有足夠的追蹤空間資料可建立空間錨點。 您可以使用
ReadyForCreateProgress
或RecommendedForCreateProgress
進行此判斷。 一旦ReadyForCreateProgress
高於1,即表示有足夠的資料可儲存雲端空間錨點,但建議您等到RecommendedForCreateProgress
高於 1 時再執行此作業。
深入了解 SessionUpdatedDelegate 委派。
this.cloudSession.SessionUpdated += (object sender, SessionUpdatedEventArgs args) =>
{
var status = args.Status;
if (status.UserFeedback == SessionUserFeedback.None) return;
this.feedback = $"Feedback: {Enum.GetName(typeof(SessionUserFeedback), status.UserFeedback)} -" +
$" Recommend Create={status.RecommendedForCreateProgress: 0.#%}";
};
建立雲端空間錨點
若要建立雲端空間錨點,請先在您平台的 AR 系統中建立錨點,然後再建立雲端對應項目。 您可以使用 CreateAnchorAsync()
方法。
深入了解 CloudSpatialAnchor 類別。
// Create a local anchor, perhaps by hit-testing and spawning an object within the scene
Vector3 hitPosition = new Vector3();
#if UNITY_ANDROID || UNITY_IOS
Vector2 screenCenter = new Vector2(0.5f, 0.5f);
List<ARRaycastHit> aRRaycastHits = new List<ARRaycastHit>();
if(arRaycastManager.Raycast(screenCenter, aRRaycastHits) && aRRaycastHits.Count > 0)
{
ARRaycastHit hit = aRRaycastHits[0];
hitPosition = hit.pose.position;
}
#elif WINDOWS_UWP || UNITY_WSA
RaycastHit hit;
if (this.TryGazeHitTest(out hit))
{
hitPosition = hit.point;
}
#endif
Quaternion rotation = Quaternion.AngleAxis(0, Vector3.up);
this.localAnchor = GameObject.Instantiate(/* some prefab */, hitPosition, rotation);
this.localAnchor.AddComponent<CloudNativeAnchor>();
// If the user is placing some application content in their environment,
// you might show content at this anchor for a while, then save when
// the user confirms placement.
CloudNativeAnchor cloudNativeAnchor = this.localAnchor.GetComponent<CloudNativeAnchor>();
if (cloudNativeAnchor.CloudAnchor == null) { await cloudNativeAnchor.NativeToCloud(); }
CloudSpatialAnchor cloudAnchor = cloudNativeAnchor.CloudAnchor;
await this.cloudSession.CreateAnchorAsync(cloudAnchor);
this.feedback = $"Created a cloud anchor with ID={cloudAnchor.Identifier}");
如前所述,在嘗試建立新的雲端空間錨點之前,您必須先擷取足夠的環境資料。 這表示 ReadyForCreateProgress
必須高於 1,但建議您等到 RecommendedForCreateProgress
高於 1 時再執行此作業。
深入瞭解 GetSessionStatusAsync 方法。
SessionStatus value = await this.cloudSession.GetSessionStatusAsync();
if (value.RecommendedForCreateProgress < 1.0f) return;
// Issue the creation request ...
設定屬性
在儲存雲端空間錨點時,您可以選擇新增一些屬性。 例如要儲存的物件類型,或是決定是否要啟用其互動功能的基本屬性。 這樣做在探索時可能有其效用:您可以立即為使用者轉譯物件,例如具有空白內容的圖片框架。 然後,背景中的不同下載會取得其他狀態詳細資料,例如要在框架中顯示的圖片。
深入瞭解 AppProperties 屬性。
CloudSpatialAnchor cloudAnchor = new CloudSpatialAnchor() { LocalAnchor = localAnchor };
cloudAnchor.AppProperties[@"model-type"] = @"frame";
cloudAnchor.AppProperties[@"label"] = @"my latest picture";
await this.cloudSession.CreateAnchorAsync(cloudAnchor);
更新屬性
若要更新錨點的屬性,您可以使用 UpdateAnchorProperties()
方法。 如果有兩個或更多裝置同時嘗試更新相同錨點的屬性,我們會使用開放式並行模型。 這表示會以最先寫入者為準。 其他所有的寫入都會收到「並行」錯誤:必須在重新整理屬性後再試一次。
深入了解 UpdateAnchorPropertiesAsync 方法。
CloudSpatialAnchor anchor = /* locate your anchor */;
anchor.AppProperties[@"last-user-access"] = @"just now";
await this.cloudSession.UpdateAnchorPropertiesAsync(anchor);
錨點在服務上建立後,您就無法更新其位置 - 您必須建立新的錨點並刪除舊的錨定,以追蹤新的位置。
如果您不需要尋找錨點以更新其屬性,您可以使用 GetAnchorPropertiesAsync()
方法,以傳回具有屬性的 CloudSpatialAnchor
物件。
深入了解 GetAnchorPropertiesAsync 方法。
var anchor = await cloudSession.GetAnchorPropertiesAsync(@"anchorId");
if (anchor != null)
{
anchor.AppProperties[@"last-user-access"] = @"just now";
await this.cloudSession.UpdateAnchorPropertiesAsync(anchor);
}
設定到期日
您也可以將錨點設定成在未來指定的日期自動到期。 錨點到期後,您就無法再找到該錨點或加以更新。 只有在建立錨點之後,才能將到期時間儲存至雲端。 其後將無法更新到期日。 如果在錨點建立期間未設定到期,錨點只會 在手動刪除時到期。
深入瞭解 Expiration 屬性。
cloudAnchor.Expiration = DateTimeOffset.Now.AddDays(7);
尋找雲端空間錨點
使用 Azure Spatial Anchors 的主要原因之一,是為了要找出先前儲存的雲端空間錨點。 為此,我們會使用「監看員」。 您一次只能使用一個監看員;不支援多個監看員。 (也稱為 錨點尋找策略) 監看員找到雲端空間錨點的方式有幾種不同。 您一次只能對監看員使用一種策略。
注意
每當您找到錨點時,Azure Spatial Anchors 就會嘗試使用收集到的環境資料來增加錨點的視覺效果資訊。 如果您找不到錨點,建議您在建立錨點後,以不同的角度和光線條件尋找數次。
如果您要依識別碼尋找雲端空間錨點,您可以將雲端空間錨點識別碼儲存在應用程式的後端服務中,並讓所有可正確向它進行驗證的裝置存取。 如需這類範例,請參閱教學課程:跨裝置共用空間錨點。
具現化 AnchorLocateCriteria
物件、設定您要尋找的識別碼,並提供您的 AnchorLocateCriteria
藉以在工作階段上叫用 CreateWatcher
方法。
深入瞭解 CreateWatcher 方法。
AnchorLocateCriteria criteria = new AnchorLocateCriteria();
criteria.Identifiers = new string[] { @"id1", @"id2", @"id3" };
this.cloudSession.CreateWatcher(criteria);
監看員建立後,系統會針對每個要求的錨點引發 AnchorLocated
事件。 在找到錨點時,或找不到錨點時,都會引發此事件。 如果發生這種情況,將會在狀態中指出原因。 在處理完監看員的所有錨點後 (包括找到和找不到的),就會引發 LocateAnchorsCompleted
事件。 每個監看員有 35 個識別碼的限制。
深入了解 AnchorLocated 委派。
this.cloudSession.AnchorLocated += (object sender, AnchorLocatedEventArgs args) =>
{
switch (args.Status)
{
case LocateAnchorStatus.Located:
CloudSpatialAnchor foundAnchor = args.Anchor;
// Go add your anchor to the scene...
break;
case LocateAnchorStatus.AlreadyTracked:
// This anchor has already been reported and is being tracked
break;
case LocateAnchorStatus.NotLocatedAnchorDoesNotExist:
// The anchor was deleted or never existed in the first place
// Drop it, or show UI to ask user to anchor the content anew
break;
case LocateAnchorStatus.NotLocated:
// The anchor hasn't been found given the location data
// The user might in the wrong location, or maybe more data will help
// Show UI to tell user to keep looking around
break;
}
}
刪除錨點
在您的開發程序和實務中,最好盡早設定將不再使用的錨點刪除,以讓您的 Azure 資源保持乾淨。
深入了解 DeleteAnchorAsync 方法。
尋找建議的 () 之後刪除錨點
await this.cloudSession.DeleteAnchorAsync(cloudAnchor);
// Perform any processing you may want when delete finishes
刪除錨點而不尋找
如果您找不到錨點,但仍想要刪除它,您可以使用 GetAnchorPropertiesAsync
採用 anchorId 做為輸入的 API 來取得 CloudSpatialAnchor
物件。 然後,您可以將此物件傳遞至 DeleteAnchorAsync
以將其刪除。
var anchor = await cloudSession.GetAnchorPropertiesAsync(@"anchorId");
await this.cloudSession.DeleteAnchorAsync(anchor);
暫停、重設或停止工作階段
若要暫時停止工作階段,您可以叫用 Stop()
。 如此,即使您叫用 ProcessFrame()
,也會停止任何監看員和環境處理。 接著,您可以叫用 Start()
以繼續處理。 繼續處理時,會保有工作階段中已擷取的環境資料。
深入瞭解 Stop 方法。
this.cloudSession.Stop();
若要重設您工作階段中已擷取的環境資料,您可以叫用 Reset()
。
深入了解 Reset 方法。
this.cloudSession.Reset();
若要在工作階段結束後適當進行清除,請叫用 Dispose()
。
深入了解 Dispose 方法。
this.cloudSession.Dispose();
後續步驟
在本指南中,您已了解如何使用 Azure Spatial Anchors SDK 來建立和尋找錨點。 若要深入了解錨點關聯性,請繼續進行下一個指南。