チュートリアル:Azure Spatial Anchors を使用して新しい HoloLens Unity アプリを作成する詳細な手順Tutorial: Step-by-step instructions to create a new HoloLens Unity app using Azure Spatial Anchors

このチュートリアルでは、Azure Spatial Anchors を使用して新しい HoloLens Unity アプリを作成する方法について説明します。This tutorial will show you how to create a new HoloLens Unity app with Azure Spatial Anchors.

前提条件Prerequisites

このチュートリアルを完了するには、以下のものが必要です。To complete this tutorial, make sure you have:

  1. Visual Studio 2017 以降ユニバーサル Windows プラットフォーム開発ワークロード、Windows 10 SDK (10.0.18362.0 以降) コンポーネント、および Git for Windows がインストールされた Windows マシン。A Windows machine with Visual Studio 2017+ installed with the Universal Windows Platform development workload and the Windows 10 SDK (10.0.18362.0 or newer) component, and Git for Windows.
  2. Visual Studio Marketplace から Visual Studio 用の C++/WinRT Visual Studio 拡張機能 (VSIX) をインストールする必要があります。The C++/WinRT Visual Studio Extension (VSIX) for Visual Studio should be installed from the Visual Studio Marketplace.
  3. 開発者モードが有効になっている HoloLens デバイス。A HoloLens device with developer mode enabled. この記事では、Windows 10 October 2018 Update (別名 RS5) が適用された HoloLens デバイスが必要です。This article requires a HoloLens device with the Windows 10 October 2018 Update (also known as RS5). HoloLens を最新のリリースに更新するには、 [設定] アプリを開き、 [更新とセキュリティ] を選択し、 [更新プログラムの確認] ボタンをクリックします。To update to the latest release on HoloLens, open the Settings app, go to Update & Security, then select the Check for updates button.

使用の開始Getting started

最初に、プロジェクトと Unity シーンを設定します。We'll first set up our project and Unity scene:

  1. Unity を起動します。Start Unity.
  2. [新規] を選択します。Select New.
  3. [3D] が選択されていることを確認します。Ensure 3D is selected.
  4. プロジェクトに名前を付け、保存場所を入力します。Name your project and enter a save Location.
  5. [プロジェクトの作成] をクリックします。Click Create project.
  6. 空の既定のシーンを新しいファイルに保存します (使用するのは [File](ファイル) > [Save As](名前を付けて保存) )。Save the empty default scene to a new file using: File > Save As.
  7. 新しいシーンに「Main」という名前を付け、 [Save](保存) をクリックします。Name the new scene Main and press the Save button.

プロジェクト設定をセットアップするSet up the project settings

次に、開発で Windows Holographic SDK をターゲットにするのに役立つ、Unity プロジェクトの設定をいくつか行います。We'll now set some Unity project settings that help us target the Windows Holographic SDK for development.

まず、アプリケーションの品質設定を行いましょう。First, lets set quality settings for our application.

  1. [Edit](編集) > [Project Settings](プロジェクト設定) > [Quality](品質) の順に選択します。Select Edit > Project Settings > Quality
  2. Windows ストア ロゴの下の列で、 [Default](既定値) 行の矢印をクリックし、 [Very Low](非常に低い) を選択します。In the column under the Windows Store logo, click on the arrow at the Default row and select Very Low. [Windows Store](Windows ストア) 列のボックスと [Very Low](非常に低い) 行が緑色の場合、設定が適切に適用されていることがわかります。You'll know the setting is applied correctly when the box in the Windows Store column and Very Low row is green.

エクスポートしようとしているアプリでは 2D ビューではなく没入型ビューを作成することを Unity に伝える必要があります。We need to let Unity know that the app we are trying to export should create an immersive view instead of a 2D view. Windows 10 SDK に対して Unity でバーチャル リアリティのサポートを有効にし、没入型ビューを作成します。We create an immersive view by enabling Virtual Reality support on Unity targeting the Windows 10 SDK.

  1. [Edit](編集) > [Project Settings](プロジェクト設定) > [Player](プレーヤー) の順に移動します。Go to Edit > Project Settings > Player.
  2. [Player Settings](プレーヤー設定)[Inspector](インスペクター) パネルで、Windows ストア アイコンを選択します。In the Inspector Panel for Player Settings, select the Windows Store icon.
  3. [XR Settings](XR 設定) グループを展開します。Expand the XR Settings group.
  4. [Rendering](レンダリング) セクションで、 [Virtual Reality Supported](バーチャル リアリティ サポート) チェック ボックスをオンにして、新しいバーチャル リアリティ SDK の一覧を追加します。In the Rendering section, check the Virtual Reality Supported checkbox to add a new Virtual Reality SDK's list.
  5. [Windows Mixed Reality] が一覧に表示されていることを確認します。Verify that Windows Mixed Reality appears in the list. されていない場合は、一覧の下部にある [+] ボタンを選択し、 [Windows Mixed Reality] を選択します。If not, select the + button at the bottom of the list and choose Windows Mixed Reality.

注意

Windows ストア アイコンが表示されない場合は、インストールの前に Windows ストア .NET スクリプト バックエンドを選択したことをもう一度確認します。If you do not see the Windows Store icon, double check to make sure you selected the Windows Store .NET Scripting Backend prior to installation. していない場合は、適切な Windows インストールを使用して、Unity を再インストールする必要があります。If not, you may need to reinstall Unity with the correct Windows installation.

スクリプト バックエンド構成を検証するVerify Scripting Backend configuration

  1. [Edit](編集) > [Project Settings](プロジェクト設定) > [Player](プレーヤー) の順に移動します ( [Player](プレーヤー) は、前の手順から開いたままになっている場合があります)。Go to Edit > Project Settings > Player (you may still have Player open from the previous step).
  2. [Player Settings](プレーヤー設定)[Inspector](インスペクター) パネルで、Windows ストア アイコンを選択します。In the Inspector Panel for Player Settings, select the Windows Store icon.
  3. [Other Settings](その他の設定) 構成セクションで、 [Scripting Backend](スクリプト バックエンド)[IL2CPP] に設定されていることを確認します。In the Other Settings Configuration section, make sure that Scripting Backend is set to IL2CPP.

機能を設定するSet capabilities

  1. [Edit](編集) > [Project Settings](プロジェクト設定) > [Player](プレーヤー) の順に移動します ( [Player](プレーヤー) は、前の手順から開いたままになっている場合があります)。Go to Edit > Project Settings > Player (you may still have Player open from the previous step).
  2. [Player Settings](プレーヤー設定)[Inspector](インスペクター) パネルで、Windows ストア アイコンを選択します。In the Inspector Panel for Player Settings, select the Windows Store icon.
  3. [Publishing Settings](公開の設定) 構成セクションで、 [InternetClientServer][SpatialPerception] をオンにします。In the Publishing Settings Configuration section, check InternetClientServer and SpatialPerception.

メインの仮想カメラを設定するSet up the main virtual camera

  1. [Hierarchy](階層) パネルで、 [Main Camera](メイン カメラ) を選択します。In the Hierarchy Panel, select Main Camera.
  2. [Inspector](インスペクター) で、変換座標を 0、0、0 に設定します。In the Inspector, set its transform position to 0,0,0.
  3. [Clear Flags](フラグをクリア) プロパティを探して、ドロップダウンを [Skybox](スカイボックス) から [Solid Color](ソリッド カラー) に変更します。Find the Clear Flags property, and change the dropdown from Skybox to Solid Color.
  4. [Background](背景) フィールドをクリックして、カラー ピッカーを開きます。Click on the Background field to open a color picker.
  5. R、G、B、A0 に設定します。Set R, G, B, and A to 0.
  6. [コンポーネントの追加] を選択し、Spatial Mapping Collider を追加します。Select Add Component and search for and add the Spatial Mapping Collider.

スクリプトを作成するCreate our script

  1. [Project](プロジェクト) ウィンドウで、Assets フォルダーの下に新しいフォルダー (Scripts) を作成します。In the Project pane, create a new folder, Scripts, under the Assets folder.
  2. そのフォルダーを右クリックして、 [Create](作成) > [C# Script](C# スクリプト) の順に選択します。Right click on the folder, then select Create >, C# Script. タイトルは「AzureSpatialAnchorsScript」にします。Title it AzureSpatialAnchorsScript.
  3. [GameObject](ゲームオブジェクト) -> [Create Empty](空のオブジェクトの作成) の順に移動します。Go to GameObject -> Create Empty.
  4. それを選択し、 [Inspector](インスペクター) でその名前を GameObject から MixedRealityCloud に変更します。Select it, and in the Inspector rename it from GameObject to MixedRealityCloud. [Add Component](コンポーネントの追加) を選択し、AzureSpatialAnchorsScript を検索して追加します。Select Add Component and search for and add the AzureSpatialAnchorsScript.

球のプレハブを作成するCreate the sphere prefab

  1. [GameObject] -> [3D オブジェクト] -> [Sphere](球) に移動します。Go to GameObject -> 3D Object -> Sphere.
  2. [インスペクター] で、そのスケールを 0.25、0.25、0.25 に設定します。In the Inspector, set its scale to 0.25, 0.25, 0.25.
  3. [階層] ウィンドウでオブジェクトを見つけます。Find the Sphere object in the Hierarchy pane. それをクリックして、 [プロジェクト] ウィンドウの [資産] フォルダーにドラッグします。Click on it and drag it into the Assets folder in the Project pane.
  4. 右クリックして、 [階層] ウィンドウで作成した元の球を削除します。Right click and Delete the original sphere you created in the Hierarchy pane.

[プロジェクト] ウィンドウに球のプレハブが作成されているのがわかります。You should now have a sphere prefab in your Project pane.

試してみるTrying it out

問題ないことをテストするために、Unity でアプリをビルドし、Visual Studio でそれをデプロイします。To test out that everything is working, build your app in Unity and deploy it from Visual Studio. そのためには、「MR Basics 100: Getting started with Unity (MR の基本 100: Unity の概要)」 コースの 6 章に従ってください。Follow Chapter 6 from the MR Basics 100: Getting started with Unity course to do so. Unity のスタート画面、そしてクリアな表示が示されます。You should see the Unity start screen, and then a clear display.

現実世界でのオブジェクトの配置Place an object in the real world

アプリを使用してオブジェクトを作成し、配置しましょう。Let's create & place an object using your app. アプリのデプロイ時に作成した Visual Studio ソリューションを開きます。Open the Visual Studio solution that we created when we deployed our app.

まず、以下の import を Assembly-CSharp (Universal Windows)\Scripts\AzureSpatialAnchorsScript.cs に追加します。First, add the following imports into your Assembly-CSharp (Universal Windows)\Scripts\AzureSpatialAnchorsScript.cs:


```csharp
using Microsoft.Azure.SpatialAnchors;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

それから、次のメンバー変数を AzureSpatialAnchorsScript クラスに追加します。Then, add the following members variables into your AzureSpatialAnchorsScript class:

using UnityEngine.XR.WSA;
using UnityEngine.XR.WSA.Input;

public class AzureSpatialAnchorsScript : MonoBehaviour
{   
    /// <summary>
    /// The sphere prefab.
    /// </summary>
    public GameObject spherePrefab;

    /// <summary>
    /// Set this string to the Spatial Anchors account id provided in the Spatial Anchors resource.
    /// </summary>
    protected string SpatialAnchorsAccountId = "Set me";

    /// <summary>
    /// Set this string to the Spatial Anchors account key provided in the Spatial Anchors resource.
    /// </summary>
    private readonly Queue<Action> dispatchQueue = new Queue<Action>();

    /// <summary>
    /// Use the recognizer to detect air taps.
    /// </summary>
    protected CloudSpatialAnchor currentCloudAnchor;

    /// <summary>
    /// True if we are 1) creating + saving an anchor or 2) looking for an anchor.
    /// </summary>
    protected bool tapExecuted = false;

    /// <summary>
    /// The ID of the CloudSpatialAnchor that was saved. Use it to find the CloudSpatialAnchor
    /// </summary>
    protected string cloudSpatialAnchorId = "";

    /// <summary>
    /// The sphere rendered to show the position of the CloudSpatialAnchor.
    /// </summary>
    protected GameObject sphere;
    protected Material sphereMaterial;

    /// <summary>

続行する前に、spherePrefab メンバー変数で作成した球のプレハブを設定する必要があります。Before we continue, we need to set the sphere prefab we created on our spherePrefab member variable. [Unity] に戻ります。Go back to Unity.

  1. [Unity][階層] ウィンドウで [MixedRealityCloud] オブジェクトを選択します。In Unity, select the MixedRealityCloud object in the Hierarchy pane.
  2. [プロジェクト] ウィンドウに保存したのプレハブをクリックします。Click on the Sphere prefab that you saved in the Project pane. [インスペクター] ウィンドウで、 [Azure Spatial Anchors (スクリプト)] の下の Sphere Prefab 領域にクリックした Sphere をドラッグします。Drag the Sphere you clicked on into the Sphere Prefab area under Azure Spatial Anchors Script (Script) in the Inspector pane.

これでがスクリプト上のプレハブとして設定されます。You should now have the Sphere set as the prefab on your script. Unity からビルドし、結果の Visual Studio ソリューションを試してみるで行ったのと同じように再度開きます。Build from Unity and then open the resulting Visual Studio solution again, like you just did in Trying it out.

Visual Studio で、AzureSpatialAnchorsScript.cs を再度開きます。In Visual Studio, open up AzureSpatialAnchorsScript.cs again. 次のコードを Start() メソッドに追加します。Add the following code into your Start() method. このコードでは GestureRecognizer をフックします。これは、エア タップが行われたことを検出して HandleTap を呼び出します。This code will hook up GestureRecognizer, which will detect when there is an air tap and call HandleTap.

/// </summary>
protected float recommendedForCreate = 0;

// Start is called before the first frame update
void Start()
{
    recognizer = new GestureRecognizer();

    recognizer.StartCapturingGestures();

    recognizer.Tapped += HandleTap;

ここで、次の HandleTap() メソッドを Update() の下に追加する必要があります。We now have to add the following HandleTap() method below Update(). これにより、レイ キャストを実行し、球体を配置するヒット ポイントを取得します。It will do a ray cast and get a hit point at which to place a sphere.

    Debug.Log("ASA Info: Locate anchors completed. Watcher identifier: " + args.Watcher.Identifier);
}

/// <summary>
/// Called by GestureRecognizer when a tap is detected.
/// </summary>
/// <param name="tapEvent">The tap.</param>    
public void HandleTap(TappedEventArgs tapEvent)
{
    if (tapExecuted)
    {
        return;
    }
    // Clean up any anchors that have been placed.
    CleanupObjects();

    // Construct a Ray using forward direction of the HoloLens.
    Ray GazeRay = new Ray(tapEvent.headPose.position, tapEvent.headPose.forward);

    // Raycast to get the hit point in the real world.
    RaycastHit hitInfo;
    Physics.Raycast(GazeRay, out hitInfo, float.MaxValue);

ここで、球体を作成する必要があります。We now need to create the sphere. 球体は最初白になります。しかし、この値は後で調整します。The sphere will initially be white, but this value will be adjusted later on. 次の CreateAndSaveSphere() メソッドを追加します。Add the following CreateAndSaveSphere() method:

    this.CreateAndSaveSphere(hitInfo.point);
}

/// <summary>
/// Creates a sphere at the hit point, and then saves a CloudSpatialAnchor there.
/// </summary>
/// <param name="hitPoint">The hit point.</param>
protected virtual void CreateAndSaveSphere(Vector3 hitPoint)
{
    // Create a white sphere.
    sphere = GameObject.Instantiate(spherePrefab, hitPoint, Quaternion.identity) as GameObject;
    sphere.AddComponent<WorldAnchor>();
            Debug.LogError("ASA Error: " + ex.Message);

Visual Studio で自分のアプリを実行して、もう一度検証します。Run your app from Visual Studio to validate it once more. 今回は、画面をタップして白い球体を作成し、任意のサーフェス上に配置します。This time, tap the screen to create & place your white sphere over the surface of your choice.

ディスパッチャー パターンの設定Set up the dispatcher pattern

Unity を使用する際には、Unity API (UI を更新するために使用する API など) はすべて、メイン スレッドで実行される必要があります。When working with Unity, all Unity APIs, for example APIs you use to do UI updates, need to happen on the main thread. しかし、これから記述するコードでは、他のスレッドでコールバックを取得します。In the code we'll write however, we get callbacks on other threads. これらのコールバックで UI を更新したいため、サイド スレッドからメイン スレッドに移動する手段が必要です。We want to update UI in these callbacks, so we need a way to go from a side thread onto the main thread. サイド スレッドからメイン スレッド上でコードを実行するには、ディスパッチャー パターンを使用します。To execute code on the main thread from a side thread, we'll use the dispatcher pattern.

メンバー変数 dispatchQueue を追加してみましょう。これはアクションのキューです。Let's add a member variable, dispatchQueue, which is a Queue of Actions. アクションをキューにプッシュしてからデキューし、そのアクションをメイン スレッドで実行します。We will push Actions onto the queue, and then dequeue and run the Actions on the main thread.

/// </summary>
protected string SpatialAnchorsAccountId = "Set me";

/// <summary>
/// Set this string to the Spatial Anchors account key provided in the Spatial Anchors resource.
/// </summary>
protected string SpatialAnchorsAccountKey = "Set me";

/// <summary>
/// Our queue of actions that will be executed on the main thread.
/// </summary>
private readonly Queue<Action> dispatchQueue = new Queue<Action>();

/// <summary>

次に、アクションをキューに追加する方法を追加しましょう。Next, let's add a way to add an Action to the Queue. QueueOnUpdate()Update() の直後に追加します。Add QueueOnUpdate() right after Update() :

    }
}

/// <summary>
/// Queues the specified <see cref="Action"/> on update.
/// </summary>
/// <param name="updateAction">The update action.</param>
protected void QueueOnUpdate(Action updateAction)
{
    lock (dispatchQueue)
    {

次は Update() ループを使用して、キューに入れられたアクションがあるかどうかを確認しましょう。Let's now use the Update() loop to check if there is an Action queued. ある場合は、そのアクションをデキューして実行します。If so, we will dequeue the action and run it.

    InitializeSession();
}

// Update is called once per frame
void Update()
{
    lock (dispatchQueue)
    {
        if (dispatchQueue.Count > 0)
        {
            dispatchQueue.Dequeue()();

Azure Spatial Anchors SDK の取得Get the Azure Spatial Anchors SDK

次に Azure Spatial Anchors SDK をダウンロードします。We'll now download the Azure Spatial Anchors SDK. GitHub の Azure Spatial Anchors リリース ページに移動します。Go to Azure Spatial Anchors GitHub releases page. [Assets](アセット) で、AzureSpatialAnchors.unitypackage ファイルをダウンロードします。Under Assets, download the AzureSpatialAnchors.unitypackage file.

Unity で [Assets](アセット) に移動し、 [Import Package](パッケージのインポート) > [Custom Package...](カスタム パッケージ) の順にクリックします。パッケージに移動して、 [Open](開く) を選択します。In Unity, go to Assets, click Import Package > Custom Package.... Navigate to the package and select Open.

表示される新しい [Import Unity Package](Unity パッケージのインポート) ウィンドウで、左下にある [None](なし) を選択します。In the new Import Unity Package window that pops up, select None at the bottom left. 次に、 [AzureSpatialAnchorsPlugin] > [Plugins](プラグイン) で、 [Common](共通)[Editor](エディター)[HoloLens] を選択します。Then under AzureSpatialAnchorsPlugin > Plugins, select Common, Editor, and HoloLens. 右下隅にある [Import](インポート) をクリックします。Click Import in the bottom-right corner.

Azure Spatial Anchors SDK を取得するために、ここで Nuget パッケージを復元する必要があります。We now need to restore Nuget packages in order to get Azure Spatial Anchors SDK. Unity でビルドし、その結果生成された Visual Studio ソリューションを開いてもう一度ビルドします (詳細は「試してみる」を参照)。Build from Unity and then open and build the resulting Visual Studio solution again, as detailed in Trying it out.

自分の Visual Studio ソリューションで、次のインポートを <ProjectName>\Assets\Scripts\AzureSpatialAnchorsScript.cs に追加します。In your Visual Studio solution, add the following import into your <ProjectName>\Assets\Scripts\AzureSpatialAnchorsScript.cs:

using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.XR.WSA;

続いて、以下のメンバー変数を AzureSpatialAnchorsScript クラスに追加します。Then, add the following member variables into your AzureSpatialAnchorsScript class:

/// </summary>
private readonly Queue<Action> dispatchQueue = new Queue<Action>();

/// <summary>
/// Use the recognizer to detect air taps.
/// </summary>
private GestureRecognizer recognizer;

protected CloudSpatialAnchorSession cloudSpatialAnchorSession;

/// <summary>
/// The CloudSpatialAnchor that we either 1) placed and are saving or 2) just located.
/// </summary>
protected CloudSpatialAnchor currentCloudAnchor;

/// <summary>

ローカル アンカーへのローカル Azure 空間アンカーのアタッチAttach a local Azure Spatial Anchor to the local anchor

Azure Spatial Anchor の CloudSpatialAnchorSession を設定しましょう。Let's set up Azure Spatial Anchor's CloudSpatialAnchorSession. まず、次の InitializeSession() メソッドを AzureSpatialAnchorsScript クラス内に追加します。We'll start by adding the following InitializeSession() method inside your AzureSpatialAnchorsScript class. これが呼び出されると、Azure Spatial Anchors セッションが作成され、アプリの起動時に適切に初期化されます。Once called, it will ensure an Azure Spatial Anchors session is created and properly initialized during the startup of your app.

    }
}

/// <summary>
/// Initializes a new CloudSpatialAnchorSession.
/// </summary>
void InitializeSession()
{
    Debug.Log("ASA Info: Initializing a CloudSpatialAnchorSession.");

    if (string.IsNullOrEmpty(SpatialAnchorsAccountId))
    {
        Debug.LogError("No account id set.");
        return;
    }

    if (string.IsNullOrEmpty(SpatialAnchorsAccountKey))
    {
        Debug.LogError("No account key set.");
        return;
    }

    cloudSpatialAnchorSession = new CloudSpatialAnchorSession();

    cloudSpatialAnchorSession.Configuration.AccountId = SpatialAnchorsAccountId.Trim();
    cloudSpatialAnchorSession.Configuration.AccountKey = SpatialAnchorsAccountKey.Trim();

    cloudSpatialAnchorSession.LogLevel = SessionLogLevel.All;

    cloudSpatialAnchorSession.SessionUpdated += CloudSpatialAnchorSession_SessionUpdated;
    cloudSpatialAnchorSession.AnchorLocated += CloudSpatialAnchorSession_AnchorLocated;
    cloudSpatialAnchorSession.LocateAnchorsCompleted += CloudSpatialAnchorSession_LocateAnchorsCompleted;

    cloudSpatialAnchorSession.Start();

ここで、デリゲート呼び出しを処理するコードを記述する必要があります。We now need to write code to handle delegate calls. 引き続き、これらにさらに追加していきます。We'll add more to them as we continue.

    Debug.Log("ASA Info: Session was initialized.");
}

private void CloudSpatialAnchorSession_Error(object sender, SessionErrorEventArgs args)
{
    Debug.LogError("ASA Error: " + args.ErrorMessage );
}

private void CloudSpatialAnchorSession_OnLogDebug(object sender, OnLogDebugEventArgs args)
{
    Debug.Log("ASA Log: " + args.Message);
    System.Diagnostics.Debug.WriteLine("ASA Log: " + args.Message);
}

private void CloudSpatialAnchorSession_SessionUpdated(object sender, SessionUpdatedEventArgs args)
{

次に、initializeSession() メソッドを Start() メソッドにフックしましょう。Now, let's hook your initializeSession() method into your Start() method.

/// </summary>
protected float recommendedForCreate = 0;

// Start is called before the first frame update
void Start()
{
    recognizer = new GestureRecognizer();

    recognizer.StartCapturingGestures();

    recognizer.SetRecognizableGestures(GestureSettings.Tap);

    recognizer.Tapped += HandleTap;

最後に、以下のコードを CreateAndSaveSphere() メソッドに追加します。Finally, add the following code into your CreateAndSaveSphere() method. これにより、現実世界に配置している球体に、ローカル Azure 空間アンカーがアタッチされます。It will attach a local Azure Spatial Anchor to the sphere that we're placing in the real world.

    this.CreateAndSaveSphere(hitInfo.point);
}

/// <summary>
/// Creates a sphere at the hit point, and then saves a CloudSpatialAnchor there.
/// </summary>
/// <param name="hitPoint">The hit point.</param>
protected virtual void CreateAndSaveSphere(Vector3 hitPoint)
{
    // Create a white sphere.
    sphere = GameObject.Instantiate(spherePrefab, hitPoint, Quaternion.identity) as GameObject;
    sphere.AddComponent<WorldAnchor>();
    sphereMaterial = sphere.GetComponent<MeshRenderer>().material;
    sphereMaterial.color = Color.white;
    Debug.Log("ASA Info: Created a local anchor.");

    // Create the CloudSpatialAnchor.
    currentCloudAnchor = new CloudSpatialAnchor();

    // Set the LocalAnchor property of the CloudSpatialAnchor to the WorldAnchor component of our white sphere.
    WorldAnchor worldAnchor = sphere.GetComponent<WorldAnchor>();
    if (worldAnchor == null)
    {
        throw new Exception("ASA Error: Couldn't get the local anchor pointer.");
    }
            Debug.LogError("ASA Error: " + ex.Message);

先に進む前に、Azure Spatial Anchors アカウント識別子とキーがまだない場合はこれらを作成します。Before proceeding any further, you'll need to create an Azure Spatial Anchors account Identifier and Key, if you don't already have them. 次のセクションに従ってこれらを取得します。Follow the following section to obtain them.

Spatial Anchors リソースを作成するCreate a Spatial Anchors resource

Azure ポータルにアクセスします。Go to the Azure portal.

Azure portal の左側のナビゲーション ウィンドウで、 [リソースの作成] を選択します。In the left navigation pane in the Azure portal, select Create a resource.

検索ボックスを使用して、「Spatial Anchors」を検索します。Use the search box to search for Spatial Anchors.

Spatial Anchors の検索

[Spatial Anchors] を選択します。Select Spatial Anchors. ダイアログ ボックスで [作成] を選択します。In the dialog box, select Create.

[Spatial Anchors アカウント] ダイアログ ボックスで以下を行います。In the Spatial Anchors Account dialog box:

  • 通常の英数字を使用して、一意のリソース名を入力します。Enter a unique resource name, using regular alphanumeric characters.

  • リソースをアタッチするサブスクリプションを選択します。Select the subscription that you want to attach the resource to.

  • [新規作成] を選択して、リソース グループを作成します。Create a resource group by selecting Create new. myResourceGroup」と名前を付け、 [OK] を選択します。Name it myResourceGroup and select OK. リソース グループとは、Web アプリ、データベース、ストレージ アカウントなどの Azure リソースのデプロイと管理に使用する論理コンテナーです。A resource group is a logical container into which Azure resources like web apps, databases, and storage accounts are deployed and managed. たとえば、後から簡単な手順で一度にリソース グループ全体を削除することもできます。For example, you can choose to delete the entire resource group in one simple step later.

  • リソースを配置する場所 (リージョン) を選択します。Select a location (region) in which to place the resource.

  • [新規] を選択して、リソースの作成を開始します。Select New to begin creating the resource.

    リソースの作成

リソースが作成されると、Azure portal に、デプロイが完了したことが表示されます。After the resource is created, Azure Portal will show that your deployment is complete. [リソースに移動] をクリックします。Click Go to resource.

デプロイ完了

これで、リソースのプロパティを確認できます。Then, you can view the resource properties. リソースの [アカウント ID] 値は、後で必要になるため、テキスト エディターにコピーしておきます。Copy the resource's Account ID value into a text editor because you'll need it later.

リソースのプロパティ

[設定][キー] を選択します。Under Settings, select Key. [主キー] の値をテキスト エディターにコピーします。Copy the Primary key value into a text editor. この値は Account Key ですThis value is the Account Key. この情報は後で必要になります。You'll need it later.

アカウント キー

クラウドへのローカル アンカーのアップロードUpload your local anchor into the cloud

Azure Spatial Anchors アカウント識別子とキーを作成したら、先に進んで Account IdSpatialAnchorsAccountIdAccount KeySpatialAnchorsAccountKey に貼り付けます。Once you have your Azure Spatial Anchors account Identifier and Key, go and paste the Account Id into SpatialAnchorsAccountId and the Account Key into SpatialAnchorsAccountKey.

最後に、すべてをつなげましょう。Finally, let's hook everything together. SpawnNewAnchoredObject() メソッドに次のコードを追加します。In your SpawnNewAnchoredObject() method, add the following code. これにより、球体が作成されるとすぐに CreateAnchorAsync() メソッドが呼び出されます。It will invoke the CreateAnchorAsync() method as soon as your sphere is created. メソッドから戻ると、次のコードによって球体への最終的な更新が実行され、色が青色に変わります。Once the method returns, the code below will perform one final update to your sphere, changing its color to blue.

    this.CreateAndSaveSphere(hitInfo.point);
}

/// <summary>
/// Creates a sphere at the hit point, and then saves a CloudSpatialAnchor there.
/// </summary>
/// <param name="hitPoint">The hit point.</param>
protected virtual void CreateAndSaveSphere(Vector3 hitPoint)
{
    // Create a white sphere.
    sphere = GameObject.Instantiate(spherePrefab, hitPoint, Quaternion.identity) as GameObject;
    sphere.AddComponent<WorldAnchor>();
    sphereMaterial = sphere.GetComponent<MeshRenderer>().material;
    sphereMaterial.color = Color.white;
    Debug.Log("ASA Info: Created a local anchor.");

    // Create the CloudSpatialAnchor.
    currentCloudAnchor = new CloudSpatialAnchor();

    // Set the LocalAnchor property of the CloudSpatialAnchor to the WorldAnchor component of our white sphere.
    WorldAnchor worldAnchor = sphere.GetComponent<WorldAnchor>();
    if (worldAnchor == null)
    {
        throw new Exception("ASA Error: Couldn't get the local anchor pointer.");
    }

    // Save the CloudSpatialAnchor to the cloud.
    currentCloudAnchor.LocalAnchor = worldAnchor.GetNativeSpatialAnchorPtr();
    Task.Run(async () =>
    {
        // Wait for enough data about the environment.
        while (recommendedForCreate < 1.0F)
        {
            await Task.Delay(330);
        }

        bool success = false;
        try
        {
            QueueOnUpdate(() =>
            {
                // We are about to save the CloudSpatialAnchor to the Azure Spatial Anchors, turn it yellow.
                sphereMaterial.color = Color.yellow;
            });

            await cloudSpatialAnchorSession.CreateAnchorAsync(currentCloudAnchor);
            success = currentCloudAnchor != null;

            if (success)
            {
                // Allow the user to tap again to clear state and look for the anchor.
                tapExecuted = false;

                // Record the identifier to locate.
                cloudSpatialAnchorId = currentCloudAnchor.Identifier;

                QueueOnUpdate(() =>
                {
                    // Turn the sphere blue.
                    sphereMaterial.color = Color.blue;
                });

                Debug.Log("ASA Info: Saved anchor to Azure Spatial Anchors! Identifier: " + cloudSpatialAnchorId);
            }
            else
            {
                sphereMaterial.color = Color.red;
                Debug.LogError("ASA Error: Failed to save, but no exception was thrown.");
            }
        }
        catch (Exception ex)
        {
            QueueOnUpdate(() =>
            {
                sphereMaterial.color = Color.red;
            });
            Debug.LogError("ASA Error: " + ex.Message);
        }

Visual Studio で自分のアプリをもう一度実行します。Run your app from Visual Studio once more. 頭を動かしてみてから、エア タップして球体を配置します。Move around your head and then air tap to place your sphere. 十分なフレームを取得すると、球体が黄色に変わり、クラウドのアップロードが開始されます。Once we have enough frames, the sphere will turn into yellow, and the cloud upload will start. アップロードの完了後に、球体は青色になります。Once the upload finishes, your sphere will turn blue. 必要に応じて、Visual Studio 内の出力ウィンドウを使用し、アプリによって送信されているログ メッセージを監視することもできます。Optionally, you could also use the Output window inside Visual Studio to monitor the log messages your app is sending. 作成の進行状況に関する推奨事項のほか、アップロード完了時にクラウドから返されるアンカー識別子を確認できるようになります。You'll be able to watch the recommended for create progress, as well as the anchor identifier that the cloud returns once the upload is completed.

注意

"DllNotFoundException: Unable to load DLL 'AzureSpatialAnchors': The specified module could not be found. (DllNotFoundException: DLL 'AzureSpatialAnchors' を読み込めません: 指定されたモジュールが見つかりません)" が表示される場合、ソリューションを削除してもう一度ビルドする必要があります。If you get "DllNotFoundException: Unable to load DLL 'AzureSpatialAnchors': The specified module could not be found.", you should Clean and Build your solution again.

クラウド空間アンカーの配置Locate your cloud spatial anchor

アンカーがクラウドにアップロードされると、それを配置する準備ができています。One your anchor is uploaded to the cloud, we're ready to attempt locating it again. 次のコードを HandleTap() メソッドに追加しましょう。Let's add the following code into your HandleTap() method. このコードでは、次のことが行われます。This code will:

  • ResetSession() を呼び出します。これは CloudSpatialAnchorSession を停止して、既存の青い球体を画面から削除します。Call ResetSession(), which will stop the CloudSpatialAnchorSession and remove our existing blue sphere from the screen.
  • CloudSpatialAnchorSession をもう一度初期化します。Initialize CloudSpatialAnchorSession again. これにより、配置しようとしているアンカーは、作成したローカル アンカーではなくクラウドから確実に取得されます。We do this so we're sure the anchor we're going to locate comes from the cloud instead of being the local anchor we created.
  • Azure Spatial Anchors にアップロードしたアンカーを検索するウォッチャーを作成します。Create a Watcher that will look for the anchor we uploaded to Azure Spatial Anchors.
    Debug.Log("ASA Info: Locate anchors completed. Watcher identifier: " + args.Watcher.Identifier);
}

/// <summary>
/// Called by GestureRecognizer when a tap is detected.
/// </summary>
/// <param name="tapEvent">The tap.</param>    
public void HandleTap(TappedEventArgs tapEvent)
{
    if (tapExecuted)
    {
        return;
    }
    tapExecuted = true;

    // We have saved an anchor, so we will now look for it.
    if (!String.IsNullOrEmpty(cloudSpatialAnchorId))
    {
        Debug.Log("ASA Info: We will look for a placed anchor.");
        tapExecuted = true;

        ResetSession(() =>
        {
            InitializeSession();

            // Create a Watcher to look for the anchor we created.
            AnchorLocateCriteria criteria = new AnchorLocateCriteria();
            criteria.Identifiers = new string[] { cloudSpatialAnchorId };
            cloudSpatialAnchorSession.CreateWatcher(criteria);

            Debug.Log("ASA Info: Watcher created. Number of active watchers: " + cloudSpatialAnchorSession.GetActiveWatchers().Count);
        });
        return;
    }

    Debug.Log("ASA Info: We will create a new anchor.");

    // Clean up any anchors that have been placed.
    CleanupObjects();

次に ResetSession() および CleanupObjects() メソッドを追加しましょう。Let's now add our ResetSession() and CleanupObjects() methods. これらは QueueOnUpdate() の下に配置できます。You can put them below QueueOnUpdate()

    }
}

/// <summary>
/// Cleans up objects.
/// </summary>
public void CleanupObjects()
{
    if (sphere != null)
    {
        Destroy(sphere);
        sphere = null;
    }

    if (sphereMaterial != null)
    {
        Destroy(sphereMaterial);
        sphereMaterial = null;
    }

    currentCloudAnchor = null;
}

/// <summary>
/// Cleans up objects and stops the CloudSpatialAnchorSessions.
/// </summary>
public void ResetSession(Action completionRoutine = null)
{
    Debug.Log("ASA Info: Resetting the session.");

    if (cloudSpatialAnchorSession.GetActiveWatchers().Count > 0)
    {
        Debug.LogError("ASA Error: We are resetting the session with active watchers, which is unexpected.");
    }

    CleanupObjects();

    this.cloudSpatialAnchorSession.Reset();

    lock (this.dispatchQueue)
    {
        this.dispatchQueue.Enqueue(() =>
        {
            if (cloudSpatialAnchorSession != null)
            {
                cloudSpatialAnchorSession.Stop();
                cloudSpatialAnchorSession.Dispose();
                Debug.Log("ASA Info: Session was reset.");
                completionRoutine?.Invoke();
            }
            else
            {
                Debug.LogError("ASA Error: cloudSpatialAnchorSession was null, which is unexpected.");
            }

ここで、クエリの実行対象であるアンカーが配置されたときに呼び出されるコードをフックする必要があります。We now need to hook up the code that will be invoked when the anchor we're querying for is located. InitializeSession() 内に、次のコールバックを追加します。Inside of InitializeSession(), add the following callbacks:


cloudSpatialAnchorSession.LogLevel = SessionLogLevel.All;

cloudSpatialAnchorSession.Error += CloudSpatialAnchorSession_Error;
cloudSpatialAnchorSession.OnLogDebug += CloudSpatialAnchorSession_OnLogDebug;
cloudSpatialAnchorSession.SessionUpdated += CloudSpatialAnchorSession_SessionUpdated;
cloudSpatialAnchorSession.AnchorLocated += CloudSpatialAnchorSession_AnchorLocated;

次に、CloudSpatialAnchor が配置されると緑色の球体を作成して配置するコードを追加しましょう。Now lets add code that will create & place a green sphere once the CloudSpatialAnchor is located. これにより、画面をもう一度タップできるようになるので、シナリオ全体をもう一度繰り返すことができます。別のローカル アンカーを作成し、アップロードして、もう一度配置します。It will also enable screen tapping again, so you can repeat the whole scenario once more: create another local anchor, upload it, and locate it again.

    recommendedForCreate = args.Status.RecommendedForCreateProgress;
}

private void CloudSpatialAnchorSession_AnchorLocated(object sender, AnchorLocatedEventArgs args)
{
    switch (args.Status)
    {
        case LocateAnchorStatus.Located:
            Debug.Log("ASA Info: Anchor located! Identifier: " + args.Identifier);
            QueueOnUpdate(() =>
            {
                // Create a green sphere.
                sphere = GameObject.Instantiate(spherePrefab, Vector3.zero, Quaternion.identity) as GameObject;
                sphere.AddComponent<WorldAnchor>();
                sphereMaterial = sphere.GetComponent<MeshRenderer>().material;
                sphereMaterial.color = Color.green;

                // Get the WorldAnchor from the CloudSpatialAnchor and use it to position the sphere.
                sphere.GetComponent<UnityEngine.XR.WSA.WorldAnchor>().SetNativeSpatialAnchorPtr(args.Anchor.LocalAnchor);

                // Clean up state so that we can start over and create a new anchor.
                cloudSpatialAnchorId = "";
                tapExecuted = false;
            });
            break;
        case LocateAnchorStatus.AlreadyTracked:
            Debug.Log("ASA Info: Anchor already tracked. Identifier: " + args.Identifier);
            break;
        case LocateAnchorStatus.NotLocated:
            Debug.Log("ASA Info: Anchor not located. Identifier: " + args.Identifier);
            break;
        case LocateAnchorStatus.NotLocatedAnchorDoesNotExist:
            Debug.LogError("ASA Error: Anchor not located does not exist. Identifier: " + args.Identifier);
            break;
    }
}

private void CloudSpatialAnchorSession_LocateAnchorsCompleted(object sender, LocateAnchorsCompletedEventArgs args)

これで完了です。That's it! 最後にもう一度 Visual Studio でアプリを実行し、シナリオ全体をエンドツーエンドで試します。Run your app from Visual Studio one last time to try out the whole scenario end to end. お使いのデバイスを動かしてみて、白い球体を配置します。Move around your device, and place your white sphere. 次に、球体が黄色に変わるまで頭を動かし続けて環境データをキャプチャします。Then, keep moving your head to capture environment data until the sphere turns yellow. ローカル アンカーがアップロードされ、球体が青色に変わります。Your local anchor will be uploaded, and your sphere will turn blue. 最後に、ローカル アンカーが削除されるように画面をもう一度タップしてから、対応するクラウド アンカーに対してクエリを実行します。Finally, tap your screen once more, so that your local anchor is removed, and then we'll query for its cloud counterpart. クラウド空間アンカーが配置されるまで、デバイスの移動を続けます。Continue moving your device around until your cloud spatial anchor is located. 正しい場所に緑色の球体が表示され、シナリオ全体をもう一度繰り返すことができます。A green sphere should appear in the correct location, and you can rinse & repeat the whole scenario again.

すべてをまとめるPutting everything together

ここでは、さまざまな要素がすべてまとめられた後に完全な AzureSpatialAnchorsScript クラス ファイルがどのようになるかを示します。Here is how the complete AzureSpatialAnchorsScript class file should look like, after all the different elements have been put together. これを参照用に使用してご自身のファイルと比較し、違いが残っているかどうかを特定できます。You can use it as a reference to compare against your own file, and spot if you may have any differences left.

using Microsoft.Azure.SpatialAnchors;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.XR.WSA;
using UnityEngine.XR.WSA.Input;

public class AzureSpatialAnchorsScript : MonoBehaviour
{   
    /// <summary>
    /// The sphere prefab.
    /// </summary>
    public GameObject spherePrefab;

    /// <summary>
    /// Set this string to the Spatial Anchors account id provided in the Spatial Anchors resource.
    /// </summary>
    protected string SpatialAnchorsAccountId = "Set me";

    /// <summary>
    /// Set this string to the Spatial Anchors account key provided in the Spatial Anchors resource.
    /// </summary>
    protected string SpatialAnchorsAccountKey = "Set me";

    /// <summary>
    /// Our queue of actions that will be executed on the main thread.
    /// </summary>
    private readonly Queue<Action> dispatchQueue = new Queue<Action>();

    /// <summary>
    /// Use the recognizer to detect air taps.
    /// </summary>
    private GestureRecognizer recognizer;

    protected CloudSpatialAnchorSession cloudSpatialAnchorSession;

    /// <summary>
    /// The CloudSpatialAnchor that we either 1) placed and are saving or 2) just located.
    /// </summary>
    protected CloudSpatialAnchor currentCloudAnchor;

    /// <summary>
    /// True if we are 1) creating + saving an anchor or 2) looking for an anchor.
    /// </summary>
    protected bool tapExecuted = false;

    /// <summary>
    /// The ID of the CloudSpatialAnchor that was saved. Use it to find the CloudSpatialAnchor
    /// </summary>
    protected string cloudSpatialAnchorId = "";

    /// <summary>
    /// The sphere rendered to show the position of the CloudSpatialAnchor.
    /// </summary>
    protected GameObject sphere;
    protected Material sphereMaterial;

    /// <summary>
    /// Indicate if we are ready to save an anchor. We can save an anchor when value is greater than 1.
    /// </summary>
    protected float recommendedForCreate = 0;

    // Start is called before the first frame update
    void Start()
    {
        recognizer = new GestureRecognizer();

        recognizer.StartCapturingGestures();

        recognizer.SetRecognizableGestures(GestureSettings.Tap);

        recognizer.Tapped += HandleTap;

        InitializeSession();
    }

    // Update is called once per frame
    void Update()
    {
        lock (dispatchQueue)
        {
            if (dispatchQueue.Count > 0)
            {
                dispatchQueue.Dequeue()();
            }
        }
    }

    /// <summary>
    /// Queues the specified <see cref="Action"/> on update.
    /// </summary>
    /// <param name="updateAction">The update action.</param>
    protected void QueueOnUpdate(Action updateAction)
    {
        lock (dispatchQueue)
        {
            dispatchQueue.Enqueue(updateAction);
        }
    }

    /// <summary>
    /// Cleans up objects.
    /// </summary>
    public void CleanupObjects()
    {
        if (sphere != null)
        {
            Destroy(sphere);
            sphere = null;
        }

        if (sphereMaterial != null)
        {
            Destroy(sphereMaterial);
            sphereMaterial = null;
        }

        currentCloudAnchor = null;
    }

    /// <summary>
    /// Cleans up objects and stops the CloudSpatialAnchorSessions.
    /// </summary>
    public void ResetSession(Action completionRoutine = null)
    {
        Debug.Log("ASA Info: Resetting the session.");

        if (cloudSpatialAnchorSession.GetActiveWatchers().Count > 0)
        {
            Debug.LogError("ASA Error: We are resetting the session with active watchers, which is unexpected.");
        }

        CleanupObjects();

        this.cloudSpatialAnchorSession.Reset();

        lock (this.dispatchQueue)
        {
            this.dispatchQueue.Enqueue(() =>
            {
                if (cloudSpatialAnchorSession != null)
                {
                    cloudSpatialAnchorSession.Stop();
                    cloudSpatialAnchorSession.Dispose();
                    Debug.Log("ASA Info: Session was reset.");
                    completionRoutine?.Invoke();
                }
                else
                {
                    Debug.LogError("ASA Error: cloudSpatialAnchorSession was null, which is unexpected.");
                }
            });
        }
    }

    /// <summary>
    /// Initializes a new CloudSpatialAnchorSession.
    /// </summary>
    void InitializeSession()
    {
        Debug.Log("ASA Info: Initializing a CloudSpatialAnchorSession.");

        if (string.IsNullOrEmpty(SpatialAnchorsAccountId))
        {
            Debug.LogError("No account id set.");
            return;
        }

        if (string.IsNullOrEmpty(SpatialAnchorsAccountKey))
        {
            Debug.LogError("No account key set.");
            return;
        }

        cloudSpatialAnchorSession = new CloudSpatialAnchorSession();

        cloudSpatialAnchorSession.Configuration.AccountId = SpatialAnchorsAccountId.Trim();
        cloudSpatialAnchorSession.Configuration.AccountKey = SpatialAnchorsAccountKey.Trim();

        cloudSpatialAnchorSession.LogLevel = SessionLogLevel.All;

        cloudSpatialAnchorSession.Error += CloudSpatialAnchorSession_Error;
        cloudSpatialAnchorSession.OnLogDebug += CloudSpatialAnchorSession_OnLogDebug;
        cloudSpatialAnchorSession.SessionUpdated += CloudSpatialAnchorSession_SessionUpdated;
        cloudSpatialAnchorSession.AnchorLocated += CloudSpatialAnchorSession_AnchorLocated;
        cloudSpatialAnchorSession.LocateAnchorsCompleted += CloudSpatialAnchorSession_LocateAnchorsCompleted;

        cloudSpatialAnchorSession.Start();

        Debug.Log("ASA Info: Session was initialized.");
    }

    private void CloudSpatialAnchorSession_Error(object sender, SessionErrorEventArgs args)
    {
        Debug.LogError("ASA Error: " + args.ErrorMessage );
    }

    private void CloudSpatialAnchorSession_OnLogDebug(object sender, OnLogDebugEventArgs args)
    {
        Debug.Log("ASA Log: " + args.Message);
        System.Diagnostics.Debug.WriteLine("ASA Log: " + args.Message);
    }

    private void CloudSpatialAnchorSession_SessionUpdated(object sender, SessionUpdatedEventArgs args)
    {
        Debug.Log("ASA Log: recommendedForCreate: " + args.Status.RecommendedForCreateProgress);
        recommendedForCreate = args.Status.RecommendedForCreateProgress;
    }

    private void CloudSpatialAnchorSession_AnchorLocated(object sender, AnchorLocatedEventArgs args)
    {
        switch (args.Status)
        {
            case LocateAnchorStatus.Located:
                Debug.Log("ASA Info: Anchor located! Identifier: " + args.Identifier);
                QueueOnUpdate(() =>
                {
                    // Create a green sphere.
                    sphere = GameObject.Instantiate(spherePrefab, Vector3.zero, Quaternion.identity) as GameObject;
                    sphere.AddComponent<WorldAnchor>();
                    sphereMaterial = sphere.GetComponent<MeshRenderer>().material;
                    sphereMaterial.color = Color.green;

                    // Get the WorldAnchor from the CloudSpatialAnchor and use it to position the sphere.
                    sphere.GetComponent<UnityEngine.XR.WSA.WorldAnchor>().SetNativeSpatialAnchorPtr(args.Anchor.LocalAnchor);

                    // Clean up state so that we can start over and create a new anchor.
                    cloudSpatialAnchorId = "";
                    tapExecuted = false;
                });
                break;
            case LocateAnchorStatus.AlreadyTracked:
                Debug.Log("ASA Info: Anchor already tracked. Identifier: " + args.Identifier);
                break;
            case LocateAnchorStatus.NotLocated:
                Debug.Log("ASA Info: Anchor not located. Identifier: " + args.Identifier);
                break;
            case LocateAnchorStatus.NotLocatedAnchorDoesNotExist:
                Debug.LogError("ASA Error: Anchor not located does not exist. Identifier: " + args.Identifier);
                break;
        }
    }

    private void CloudSpatialAnchorSession_LocateAnchorsCompleted(object sender, LocateAnchorsCompletedEventArgs args)
    {
        Debug.Log("ASA Info: Locate anchors completed. Watcher identifier: " + args.Watcher.Identifier);
    }

    /// <summary>
    /// Called by GestureRecognizer when a tap is detected.
    /// </summary>
    /// <param name="tapEvent">The tap.</param>    
    public void HandleTap(TappedEventArgs tapEvent)
    {
        if (tapExecuted)
        {
            return;
        }
        tapExecuted = true;

        // We have saved an anchor, so we will now look for it.
        if (!String.IsNullOrEmpty(cloudSpatialAnchorId))
        {
            Debug.Log("ASA Info: We will look for a placed anchor.");
            tapExecuted = true;

            ResetSession(() =>
            {
                InitializeSession();

                // Create a Watcher to look for the anchor we created.
                AnchorLocateCriteria criteria = new AnchorLocateCriteria();
                criteria.Identifiers = new string[] { cloudSpatialAnchorId };
                cloudSpatialAnchorSession.CreateWatcher(criteria);

                Debug.Log("ASA Info: Watcher created. Number of active watchers: " + cloudSpatialAnchorSession.GetActiveWatchers().Count);
            });
            return;
        }

        Debug.Log("ASA Info: We will create a new anchor.");

        // Clean up any anchors that have been placed.
        CleanupObjects();

        // Construct a Ray using forward direction of the HoloLens.
        Ray GazeRay = new Ray(tapEvent.headPose.position, tapEvent.headPose.forward);

        // Raycast to get the hit point in the real world.
        RaycastHit hitInfo;
        Physics.Raycast(GazeRay, out hitInfo, float.MaxValue);

        this.CreateAndSaveSphere(hitInfo.point);
    }

    /// <summary>
    /// Creates a sphere at the hit point, and then saves a CloudSpatialAnchor there.
    /// </summary>
    /// <param name="hitPoint">The hit point.</param>
    protected virtual void CreateAndSaveSphere(Vector3 hitPoint)
    {
        // Create a white sphere.
        sphere = GameObject.Instantiate(spherePrefab, hitPoint, Quaternion.identity) as GameObject;
        sphere.AddComponent<WorldAnchor>();
        sphereMaterial = sphere.GetComponent<MeshRenderer>().material;
        sphereMaterial.color = Color.white;
        Debug.Log("ASA Info: Created a local anchor.");

        // Create the CloudSpatialAnchor.
        currentCloudAnchor = new CloudSpatialAnchor();

        // Set the LocalAnchor property of the CloudSpatialAnchor to the WorldAnchor component of our white sphere.
        WorldAnchor worldAnchor = sphere.GetComponent<WorldAnchor>();
        if (worldAnchor == null)
        {
            throw new Exception("ASA Error: Couldn't get the local anchor pointer.");
        }

        // Save the CloudSpatialAnchor to the cloud.
        currentCloudAnchor.LocalAnchor = worldAnchor.GetNativeSpatialAnchorPtr();
        Task.Run(async () =>
        {
            // Wait for enough data about the environment.
            while (recommendedForCreate < 1.0F)
            {
                await Task.Delay(330);
            }

            bool success = false;
            try
            {
                QueueOnUpdate(() =>
                {
                    // We are about to save the CloudSpatialAnchor to the Azure Spatial Anchors, turn it yellow.
                    sphereMaterial.color = Color.yellow;
                });

                await cloudSpatialAnchorSession.CreateAnchorAsync(currentCloudAnchor);
                success = currentCloudAnchor != null;

                if (success)
                {
                    // Allow the user to tap again to clear state and look for the anchor.
                    tapExecuted = false;

                    // Record the identifier to locate.
                    cloudSpatialAnchorId = currentCloudAnchor.Identifier;

                    QueueOnUpdate(() =>
                    {
                        // Turn the sphere blue.
                        sphereMaterial.color = Color.blue;
                    });

                    Debug.Log("ASA Info: Saved anchor to Azure Spatial Anchors! Identifier: " + cloudSpatialAnchorId);
                }
                else
                {
                    sphereMaterial.color = Color.red;
                    Debug.LogError("ASA Error: Failed to save, but no exception was thrown.");
                }
            }
            catch (Exception ex)
            {
                QueueOnUpdate(() =>
                {
                    sphereMaterial.color = Color.red;
                });
                Debug.LogError("ASA Error: " + ex.Message);
            }
        });
    }
}

次の手順Next steps

このチュートリアルでは、新しい Unity HoloLens アプリで Azure Spatial Anchors を使用する方法について説明しました。In this tutorial, you've learn more about how to use Azure Spatial Anchors in a new Unity HoloLens app. 新しい Android アプリで Azure Spatial Anchors を使用する方法について詳しくは、次のチュートリアルに進んでください。To learn more about how to use Azure Spatial Anchors in a new Android app, continue to the next tutorial.