您现在访问的是微软AZURE全球版技术文档网站,若需要访问由世纪互联运营的MICROSOFT AZURE中国区技术文档网站,请访问 https://docs.azure.cn.

如何借助 Unity 来使用 Azure 空间定位点创建和查找定位点How to create and locate anchors using Azure Spatial Anchors in Unity

借助 Azure 空间定位点,可以在不同设备之间共享全球的定位点。Azure Spatial Anchors allow you to share anchors in the world between different devices. 它支持多个不同的开发环境。It supports several different development environments. 本文将深入介绍如何借助 Unity 来使用 Azure 空间定位点 SDK 执行以下操作:In this article, we'll dive into how to use the Azure Spatial Anchors SDK, in Unity, to:

  • 正确设置和管理 Azure 空间定位点会话。Correctly set up and manage an Azure Spatial Anchors session.
  • 创建和设置本地定位点的属性。Create and set properties on local anchors.
  • 将其上传到云。Upload them to the cloud.
  • 查找和删除云空间定位点。Locate and delete cloud spatial anchors.

必备条件Prerequisites

若要完成本指南,请确保做好以下准备:To complete this guide, make sure you have:

跨平台

初始化会话Initialize the session

此 SDK 的主入口点是表示会话的类。The main entry point for the SDK is the class representing your session. 通常,你会在此类中声明一个用于管理视图和本机 AR 会话的字段。Typically you'll declare a field in the class that manages your view and native AR session.

详细了解 CloudSpatialAnchorSession 类。Learn more about the CloudSpatialAnchorSession class.

    CloudSpatialAnchorSession cloudSession;
    // In your view handler
    this.cloudSession = new CloudSpatialAnchorSession();

设置身份验证Set up authentication

若要访问服务,需提供帐户密钥、访问令牌或 Azure Active Directory 身份验证令牌。To access the service, you need to provide an account key, access token, or Azure Active Directory auth token. 还可在“身份验证概念”页中了解此方面的详细信息。You can also read more about this in the Authentication concept page.

帐户密钥Account Keys

帐户密钥是一种允许应用程序使用 Azure 空间定位点服务进行身份验证的凭据。Account Keys are a credential that allows your application to authenticate with the Azure Spatial Anchors service. 帐户密钥的预期用途是帮助你快速入门,The intended purpose of Account Keys is to help you get started quickly. 尤其是在应用程序与 Azure 空间定位点集成的开发阶段。Especially during the development phase of your application's integration with Azure Spatial Anchors. 因此,可以这样使用帐户密钥:在开发过程中将它们嵌入客户端应用程序。As such, you can use Account Keys by embedding them in your client applications during development. 完成开发阶段以后,强烈建议迁移到一种属于生产级别、受访问令牌支持或者是 Azure Active Directory 用户身份验证的身份验证机制。As you progress beyond development, it's highly recommended to move to an authentication mechanism that is production-level, supported by Access Tokens, or Azure Active Directory user authentication. 若要获取用于开发的帐户密钥,请访问 Azure 空间定位点帐户并导航到“密钥”选项卡。To get an Account Key for development, visit your Azure Spatial Anchors account, and navigate to the "Keys" tab.

详细了解 SessionConfiguration 类。Learn more about the SessionConfiguration class.

    this.cloudSession.Configuration.AccountKey = @"MyAccountKey";

访问令牌Access Tokens

访问令牌是一种更可靠的方法,用于使用 Azure 空间定位点进行身份验证。Access Tokens are a more robust method to authenticate with Azure Spatial Anchors. 为生产部署准备应用程序时,它尤其有用。Especially as you prepare your application for a production deployment. 概括而言,此方法设置客户端应用程序可以用来安全地进行身份验证的后端服务。The summary of this approach is to set up a back-end service that your client application can securely authenticate with. 后端服务在运行时与 AAD 进行交互并与 Azure 空间定位点安全令牌服务进行交互来请求访问令牌。Your back-end service interfaces with AAD at runtime and with the Azure Spatial Anchors Secure Token Service to request an Access Token. 然后,该令牌将被传递给客户端应用程序,并在 SDK 中用来通过 Azure 空间定位点进行身份验证。This token is then delivered to the client application and used in the SDK to authenticate with Azure Spatial Anchors.

    this.cloudSession.Configuration.AccessToken = @"MyAccessToken";

如果未设置访问令牌,则必须处理 TokenRequired 事件,或在委托协议上实现 tokenRequired 方法。If an access token isn't set, you must handle the TokenRequired event, or implement the tokenRequired method on the delegate protocol.

可以通过设置事件参数上的属性来同步处理事件。You can handle the event synchronously by setting the property on the event arguments.

详细了解 TokenRequiredDelegate 委托。Learn more about the TokenRequiredDelegate delegate.

    this.cloudSession.TokenRequired += (object sender, TokenRequiredEventArgs args) =>
    {
        args.AccessToken = @"MyAccessToken";
    };

如果需要在处理程序中执行异步工作,则可以通过请求 deferral 对象然后完成该对象来延迟设置令牌,如以下示例所示。If you need to execute asynchronous work in your handler, you can defer setting the token by requesting a deferral object and then completing it, as in the following example.

    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 Active Directory Authentication

Azure 空间定位点还允许应用程序通过用户 Azure AD (Active Directory) 令牌进行身份验证。Azure Spatial Anchors also allows applications to authenticate with user Azure AD (Active Directory) tokens. 例如,你可以使用 Azure AD 令牌来与 Azure 空间定位点进行集成。For example, you can use Azure AD tokens to integrate with Azure Spatial Anchors. 如果企业在 Azure AD 中维护用户,则你可以在 Azure 空间定位点 SDK 中提供用户 Azure AD 令牌。If an Enterprise maintains users in Azure AD, you can supply a user Azure AD token in the Azure Spatial Anchors SDK. 这样,对于属于同一 Azure AD 租户的帐户,你可以直接通过 Azure 空间定位点服务进行身份验证。Doing so allows you to authenticate directly to the Azure Spatial Anchors service for an account that's part of the same Azure AD tenant.

    this.cloudSession.Configuration.AuthenticationToken = @"MyAuthenticationToken";

与访问令牌一样,如果未设置 Azure AD 令牌,则必须处理 TokenRequired 事件,或在委托协议上实现 tokenRequired 方法。Like with access tokens, if an Azure AD token isn't set, you must handle the TokenRequired event, or implement the tokenRequired method on the delegate protocol.

可以通过设置事件参数上的属性来同步处理事件。You can handle the event synchronously by setting the property on the event arguments.

    this.cloudSession.TokenRequired += (object sender, TokenRequiredEventArgs args) =>
    {
        args.AuthenticationToken = @"MyAuthenticationToken";
    };

如果需要在处理程序中执行异步工作,则可以通过请求 deferral 对象然后完成该对象来延迟设置令牌,如以下示例所示。If you need to execute asynchronous work in your handler, you can defer setting the token by requesting a deferral object and then completing it, as in the following example.

    this.cloudSession.TokenRequired += async (object sender, TokenRequiredEventArgs args) =>
    {
        var deferral = args.GetDeferral();
        string myToken = await MyGetTokenAsync();
        if (myToken != null) args.AuthenticationToken = myToken;
        deferral.Complete();
    };

设置会话Set up the session

调用 Start() 使会话能够处理环境数据。Invoke Start() to enable your session to process environment data.

若要处理由会话引发的事件,请附加事件处理程序。To handle events raised by your session, attach an event handler.

详细了解 Start 方法。Learn more about the Start method.

#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();

为会话提供帧Provide frames to the session

空间定位点会话的工作方式是映射用户周围的空间。The spatial anchor session works by mapping the space around the user. 这样做有助于确定定位点的位置。Doing so helps to determine where anchors are located. 移动平台(iOS 和 Android)需要对相机馈送进行本机调用才能从平台的 AR 库中获取帧。Mobile platforms (iOS & Android) require a native call to the camera feed to obtain frames from your platform's AR library. 相比之下,HoloLens 一直在扫描环境,因此不需要像在移动平台上那样进行特定的调用。In contrast, HoloLens is constantly scanning the environment, so there's no need for a specific call like on mobile platforms.

详细了解 ProcessFrame 方法。Learn more about the ProcessFrame method.

#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

向用户提供反馈Provide feedback to the user

可以编写代码来处理会话的已更新事件。You can write code to handle the session updated event. 每次会话改进对你周围环境的理解时,都会触发此事件。This event fires every time the session improves its understanding of your surroundings. 这样做使你可以:Doing so, allows you to:

  • 使用 UserFeedback 类在设备移动时向用户提供反馈,而会话则会更新其环境理解。Use the UserFeedback class to provide feedback to the user as the device moves and the session updates its environment understanding. 为此,请按以下步骤操作:To do this,
  • 确定在何时有足够的已跟踪空间数据来创建空间定位点。Determine at what point there's enough tracked spatial data to create spatial anchors. 可以通过 ReadyForCreateProgressRecommendedForCreateProgress 来确定这一点。You determine this with either ReadyForCreateProgress or RecommendedForCreateProgress. 一旦 ReadyForCreateProgress 超过 1,我们就会有足够的数据来保存云空间定位点,不过我们建议你等到 RecommendedForCreateProgress 大于 1 才这样做。Once ReadyForCreateProgress is above 1, we have enough data to save a cloud spatial anchor, though we recommend you wait until RecommendedForCreateProgress is above 1 to do so.

详细了解 SessionUpdatedDelegate 委托。Learn more about the SessionUpdatedDelegate delegate.

    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.#%}";
    };

创建云空间定位点Create a cloud spatial anchor

若要创建云空间定位点,请先在平台的 AR 系统中创建一个定位点,然后创建一个云对应项。To create a cloud spatial anchor, you first create an anchor in your platform's AR system, and then create a cloud counterpart. 我们使用 CreateAnchorAsync() 方法。You use the CreateAnchorAsync() method.

详细了解 CloudSpatialAnchor 类。Learn more about the CloudSpatialAnchor class.

    // 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) { cloudNativeAnchor.NativeToCloud(); }  
    CloudSpatialAnchor cloudAnchor = cloudNativeAnchor.CloudAnchor;
    await this.cloudSession.CreateAnchorAsync(cloudAnchor);
    this.feedback = $"Created a cloud anchor with ID={cloudAnchor.Identifier}");

如前所述,在尝试创建新的云空间定位点之前,需要捕获足够的环境数据。As described earlier, you need sufficient environment data captured before trying to create a new cloud spatial anchor. 这意味着 ReadyForCreateProgress 必须大于 1,不过我们建议你等到 RecommendedForCreateProgress 大于 1 才这样做。That means ReadyForCreateProgress has to be above 1, though we recommend you wait until RecommendedForCreateProgress is above 1 to do so.

详细了解 GetSessionStatusAsync 方法。Learn more about the GetSessionStatusAsync method.

    SessionStatus value = await this.cloudSession.GetSessionStatusAsync();
    if (value.RecommendedForCreateProgress < 1.0f) return;
    // Issue the creation request ...

设置属性Set properties

可以选择在保存云空间定位点时添加一些属性。You may choose to add some properties when saving your cloud spatial anchors. 例如,要保存的对象的类型,或基本属性(例如是否应允许它进行交互)。Like the type of object being saved, or basic properties like whether it should be enabled for interaction. 在发现后,这样做可能会很有用:可以立即呈现用户的对象,例如包含空白内容的图片框。Doing so can be useful upon discovery: you can immediately render the object for the user, for example a picture frame with blank content. 然后,后台的另一下载会获取其他状态详细信息,例如,要在框架中显示的图片。Then, a different download in the background gets additional state details, for example, the picture to display in the frame.

详细了解 AppProperties 属性。Learn more about the AppProperties property.

    CloudSpatialAnchor cloudAnchor = new CloudSpatialAnchor() { LocalAnchor = localAnchor };
    cloudAnchor.AppProperties[@"model-type"] = @"frame";
    cloudAnchor.AppProperties[@"label"] = @"my latest picture";
    await this.cloudSession.CreateAnchorAsync(cloudAnchor);

更新属性Update properties

若要更新定位点上的属性,请使用 UpdateAnchorProperties() 方法。To update the properties on an anchor, you use the UpdateAnchorProperties() method. 如果两个或更多设备同时尝试更新同一定位点的属性,我们将使用乐观并发模型。If two or more devices try to update properties for the same anchor at the same time, we use an optimistic concurrency model. 这意味着第一个写入将会胜出。Which means that the first write will win. 所有其他写入将收到“并发”错误:在重试之前需要刷新属性。All other writes will get a "Concurrency" error: a refresh of the properties would be needed before trying again.

详细了解 UpdateAnchorPropertiesAsync 方法。Learn more about the UpdateAnchorPropertiesAsync method.

    CloudSpatialAnchor anchor = /* locate your anchor */;
    anchor.AppProperties[@"last-user-access"] = @"just now";
    await this.cloudSession.UpdateAnchorPropertiesAsync(anchor);

在服务上创建定位点后,无法更新定位点的位置 - 你必须创建一个新的定位点并删除旧的,才能跟踪新位置。You can't update the location of an anchor once it has been created on the service - you must create a new anchor and delete the old one to track a new position.

如果不需要定位某个定位点来更新其属性,则可以使用 GetAnchorPropertiesAsync() 方法,该方法将返回具有属性的 CloudSpatialAnchor 对象。If you don't need to locate an anchor to update its properties, you can use the GetAnchorPropertiesAsync() method, which returns a CloudSpatialAnchor object with properties.

详细了解 GetAnchorPropertiesAsync 方法。Learn more about the GetAnchorPropertiesAsync method.

    var anchor = await cloudSession.GetAnchorPropertiesAsync(@"anchorId");
    if (anchor != null)
    {
        anchor.AppProperties[@"last-user-access"] = @"just now";
        await this.cloudSession.UpdateAnchorPropertiesAsync(anchor);
    }

设置到期时间Set expiration

还可以将定位点配置为在将来的给定日期自动过期。It's also possible to configure your anchor to expire automatically at a given date in the future. 当定位点过期后,将不再对其进行定位或更新。When an anchor expires, it will no longer be located or updated. 只能在创建定位点时设置过期时间。Expiration can only be set when the anchor is created. 之后无法更新过期时间。Updating expiration afterwards isn't possible. 因此,你可以在将其保存到云之前设置其过期时间。So, you can set its expiration before saving it to the cloud.

详细了解 Expiration 属性。Learn more about the Expiration property.

    cloudAnchor.Expiration = DateTimeOffset.Now.AddDays(7);

查找云空间定位点Locate a cloud spatial anchor

查找以前保存的云空间定位点是使用 Azure 空间定位点的主要原因之一。Being able to locate a previously saved cloud spatial anchor is one of the main reasons for using Azure Spatial Anchors. 可通过多种不同的方式来查找云空间定位点。There are several different ways you can locate a cloud spatial anchor. 每次可以使用一种策略来观察效果。You can use one strategy on a watcher at a time.

  • 按标识符查找定位点。Locate anchors by identifier.
  • 查找已连接到以前所找到的定位点的定位点。Locate anchors connected to a previously located anchor. 可在此处了解定位点关系。You can learn about anchor relationships here.
  • 使用粗略重新定位查找定位点。Locate anchor using Coarse relocalization.

备注

每次当你查找定位点时,Azure 空间定位点都会尝试使用收集的环境数据来扩充定位点上的视觉对象信息。Each time you locate an anchor, Azure Spatial Anchors will attempt to use the environment data collected to augment the visual information on the anchor. 如果在查找定位点时遇到问题,则可创建一个定位点,然后在不同角度和光照条件下多次定位,这样做可能有用。If you are having trouble locating an anchor, it can be useful to create an anchor, then locate it several times from different angles and lighting conditions.

如果按标识符查找云空间定位点,需要将云空间定位点标识符存储在应用程序的后端服务中,并使可供能够对其进行适当身份验证的所有设备访问。If you are locating cloud spatial anchors by identifier, you will want to store the cloud spatial anchor identifier in your application's back-end service, and make it accessible to all devices that can properly authenticate to it. 有关示例,请参阅教程:跨设备共享空间定位点For an example of this, see Tutorial: Share Spatial Anchors across devices.

实例化 AnchorLocateCriteria 对象,设置要查找的标识符,并提供 AnchorLocateCriteria 在会话中调用 CreateWatcher 方法。Instantiate an AnchorLocateCriteria object, set the identifiers you're looking for, and invoke the CreateWatcher method on the session by providing your AnchorLocateCriteria.

详细了解 CreateWatcher 方法。Learn more about the CreateWatcher method.

    AnchorLocateCriteria criteria = new AnchorLocateCriteria();
    criteria.Identifiers = new string[] { @"id1", @"id2", @"id3" };
    this.cloudSession.CreateWatcher(criteria);

创建观察程序后,将为所请求的每个定位点触发 AnchorLocated 事件。After your watcher is created, the AnchorLocated event will fire for every anchor requested. 找到定位点或无法找到定位点时都会触发此事件。This event fires when an anchor is located, or if the anchor can't be located. 如果发生这种情况,将在状态中说明原因。If this situation happens, the reason will be stated in the status. 在处理完观察程序的所有定位点(找到或未找到)后,将触发 LocateAnchorsCompleted 事件。After all anchors for a watcher are processed, found or not found, then the LocateAnchorsCompleted event will fire. 每个观察程序有 35 个标识符的限制。There is a limit of 35 identifiers per watcher.

详细了解 AnchorLocatedDelegate 委托。Learn more about the AnchorLocatedDelegate delegate.

    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;
        }
    }

删除定位点Delete anchors

若要删除云空间定位点,请使用 DeleteAnchor() 方法。To delete a cloud spatial anchor, you use the DeleteAnchor() method. 删除不再使用的定位点是要尽早包括在开发流程和做法中的不错做法,可使 Azure 资源保持清洁状态。Deleting anchors when no longer used is a good practice to include early on in your development process and practices, to keep your Azure resources cleaned up.

详细了解 DeleteAnchorAsync 方法。Learn more about the DeleteAnchorAsync method.

    await this.cloudSession.DeleteAnchorAsync(cloudAnchor);
    // Perform any processing you may want when delete finishes

暂停、重置或停止会话Pause, reset, or stop the session

若要暂时停止会话,可以调用 Stop()To stop the session temporarily, you can invoke Stop(). 这样做会停止所有观察程序和环境处理,即使调用 ProcessFrame() 也是如此。Doing so will stop any watchers and environment processing, even if you invoke ProcessFrame(). 然后,可以调用 Start() 以恢复处理。You can then invoke Start() to resume processing. 恢复时,将保留已在会话中捕获的环境数据。When resuming, environment data already captured in the session is maintained.

详细了解 Stop 方法。Learn more about the Stop method.

    this.cloudSession.Stop();

若要重置已在会话中捕获的环境数据,可以调用 Reset()To reset the environment data that has been captured in your session, you can invoke Reset().

详细了解 Reset 方法。Learn more about the Reset method.

    this.cloudSession.Reset();

若要在会话后进行适当清理,请调用 Dispose()To clean up properly after a session, invoke Dispose().

详细了解 Dispose 方法。Learn more about the Dispose method.

    this.cloudSession.Dispose();

后续步骤Next steps

本指南介绍了如何使用 Azure 空间定位点 SDK 创建和定位定位点。In this guide, you learned about how to create and locate anchors using the Azure Spatial Anchors SDK. 若要详细了解定位点关系,请继续阅读下一指南。To learn more about anchor relationships, continue to the next guide.