öğretici: Azure uzamsal bağlayıcıları kullanarak yeni bir HoloLens Unity uygulaması oluşturmaya yönelik adım adım yönergeler

bu öğreticide, Azure uzamsal bağlayıcılarla yeni bir HoloLens Unity uygulamasının nasıl oluşturulacağı gösterilmektedir.

Önkoşullar

Bu öğreticiyi tamamlamak için şunlar sahip olduğunuzdan emin olun:

  1. geliştirici modu etkin HoloLens bir cihaz. bu makalede, Windows 10 2020 güncelleştirmesiHoloLens bir cihaz gerekir. HoloLens en son sürüme güncelleştirmek için Ayarlar uygulamasını açın, güncelleştirme & güvenlik' e gidin, sonra güncelleştirmeleri denetle düğmesini seçin.
  2. Evrensel Windows Platformu geliştirme iş yükü ve Windows 10 SDK (10.0.18362.0 veya daha yeni) bileşeniyle Visual Studio 2017 + yüklü Windows makine ve Windows Git.
  3. Visual Studio için C++/wınrt Visual Studio uzantısı (vsıx) Visual Studio marketi'nden yüklenmelidir.
  4. Unity yüklemesi. Desteklenen sürümler ve gerekli yetenekler için Unity proje kurulumu sayfasınıziyaret edin.

Başlarken

İlk olarak proje ve Unity sahümüzü ayarlayacağız:

  1. Unity 'yi başlatın.
  2. Yeni'yi seçin.
  3. 3B 'in seçili olduğundan emin olun.
  4. Projenizi adlandırın ve bir kaydetme konumu girin.
  5. Create project (Proje oluştur) öğesini seçin.
  6. Boş varsayılan sahneyi kullanarak yeni bir dosyaya kaydedin: Dosya > farklı kaydet.
  7. Yeni sahneyi Main olarak adlandırın ve Kaydet düğmesine basın.

Proje ayarlarını ayarlama

şimdi, geliştirme için Windows Holographic SDK 'sını hedefmamıza yardımcı olan bazı Unity proje ayarlarını ayarlayacağız.

İlk olarak, uygulamamız için kalite ayarlarını ayarlayalım.

  1. Project Ayarlar > kalitesini düzenle ' yi seçin

  2. Windows deposu logosunun altındaki sütunda, varsayılan satırdaki oka tıklayın ve çok düşük' ı seçin. Windows deposu sütunundaki box ve çok düşük satır yeşil olduğunda ayarın doğru uygulandığını bilirsiniz.

Unity uygulamamızı 2B görünüm yerine bir tam ekran görünümüyle yapılandırmamız gerekir. Windows 10 SDK 'sını hedefleyen Unity üzerinde sanal gerçeklik desteğini etkinleştirerek bir derinlikli görünüm oluşturabiliyoruz.

  1. Project Ayarlar > oynatıcıyı düzenle ' ye gidin.

  2. oynatıcı Ayarlar denetçi panelinde Windows simgesini seçin.
  3. xr Ayarlar grubunu genişletin.
  4. Oluşturma bölümünde, yeni bir sanal gerçeklik SDK listesi eklemek için sanal gerçeklik destekleniyor onay kutusunu işaretleyin.
  5. Windows Mixed Reality listede göründüğünü doğrulayın. aksi takdirde, + listenin altındaki düğmeyi seçin ve Windows Mixed Reality' yi seçin.

Not

Windows simgesini görmüyorsanız, yüklemeden önce Windows .net betiği arka ucunu seçtiğinizden emin olmak için çift işaretleyin. aksi takdirde, Unity 'yi doğru Windows yüklemesiyle yeniden yüklemeniz gerekebilir.

Komut dosyası arka uç yapılandırmasını doğrula

  1. Project Ayarlar > player 'ı düzenleme sayfasına gidin (hala player 'ın önceki adımdan açık olmasına devam edebilirsiniz).

  2. oynatıcı Ayarlar için ınspector panelinde Windows Store simgesini seçin.
  3. diğer Ayarlar yapılandırma bölümünde, komut dosyası arka ucunun IL2CPP olarak ayarlandığından emin olun.

Özellikleri ayarla

  1. Project Ayarlar > player 'ı düzenleme sayfasına gidin (hala player 'ın önceki adımdan açık olmasına devam edebilirsiniz).

  2. oynatıcı Ayarlar için ınspector panelinde Windows Store simgesini seçin.
  3. yayımlama Ayarlar yapılandırma bölümünde, ınternetclientserver ve spatialperception' yı denetleyin.

Önemli

Özel olarak yapılandırılmış bir ağ kullanılıyorsa, PrivateNetworkClientServer özelliğini de etkinleştirmeniz gerekir.

Ana sanal kamerayı ayarlama

  1. Hiyerarşi panelinde, ana kamera' ı seçin.
  2. Denetçisinde, dönüştürme konumunu 0, 0, 0 olarak ayarlayın.
  3. Clear Flags özelliğini bulun ve açılan menüyü ufuk kutusundan düz renk olarak değiştirin.
  4. Bir renk seçici açmak için arka plan alanına tıklayın.
  5. R, G, B ve A 'yı 0 olarak ayarlayın.
  6. Bileşen Ekle ' yi seçin ve uzamsal eşleme Collider eklemek için arama yapın.

Betiğimizi oluşturun

  1. Project bölmesinde, varlıklar klasörü altında yeni bir klasör, komut dosyaları oluşturun.
  2. Klasöre sağ tıklayın ve ardından >, C# betiği Oluştur' u seçin. Başlık AzureSpatialAnchorsScript.
  3. Oyun nesnesi -> Oluştur boş öğesine gidin.
  4. Bunu seçin ve Inspector 'Da Gameobject Iken mixedrealitycloud olarak yeniden adlandırın. Bileşen Ekle ' yi seçin ve AzureSpatialAnchorsScript ekleyin ve ekleyin.

Sphere prefab oluşturma

  1. Gameobject -> 3B nesne -> Sphere öğesine gidin.
  2. Denetçisinde, ölçeğini 0,25, 0,25, 0,25 olarak ayarlayın.
  3. Hiyerarşi bölmesinde Sphere nesnesini bulun. üzerine tıklayın ve Project bölmesinde varlıklar klasörüne sürükleyin.
  4. Hiyerarşi bölmesinde oluşturduğunuz orijinal küreyi sağ tıklayıp silin .

artık Project bölmesinizdeki bir sphere prefab sahibi olmanız gerekir.

Deneniyor

Her şeyin çalıştığını test etmek için, uygulamanızı Unity 'de oluşturun ve Visual Studio dağıtın. Mr temel kuralları 100: Unity kursu ile çalışmaya başlama başlıklı Bölüm 6 ' yı izleyin. Unity başlangıç ekranı ' nı ve ardından temiz bir ekran görmeniz gerekir.

Gerçek dünyaya bir nesne yerleştirme

Uygulamanızı kullanarak bir nesne oluşturalım &. uygulamamızı dağıttığımızdaoluşturduğumuz Visual Studio çözümünü açın.

İlk olarak, aşağıdaki içeri aktarmaları içine ekleyin Assembly-CSharp (Universal Windows)\Scripts\AzureSpatialAnchorsScript.cs :


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

Ardından, aşağıdaki üye değişkenlerini AzureSpatialAnchorsScript sınıfınıza ekleyin:

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>
    /// Set this string to the Spatial Anchors account domain 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>

Devam etmeden önce, Spburprefab üye değişkenimizde oluşturduğumuz Sphere ön kümesini ayarlamanız gerekir. Unity'ye geri dönün.

  1. Unity'de hiyerarşi bölmesinde mixedrealitycloud nesnesini seçin.
  2. Project bölmesine kaydettiğiniz Sphere prefab öğesine tıklayın. Tıklattığınız Sphere öğesini Inspector bölmesindeki Azure uzamsal bağlayıcı betiği (betik) altındaki Sphere prefab alanına sürükleyin.

Artık betiğinizdeki ön küm kümesine sahip olmanız gerekir. Unity 'den derleyin ve ardından elde edilen Visual Studio çözümünü yeniden deneyerekyaptığınız gibi tekrar açın.

Visual Studio, AzureSpatialAnchorsScript.cs tekrar açın. Aşağıdaki kodu Start() yöntemine ekleyin. Bu kod GestureRecognizer , HandleTap bir hava dokunusını algıladığında çağracaktır.

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

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

    recognizer.StartCapturingGestures();

    recognizer.Tapped += HandleTap;

Şimdi aşağıdaki yöntemi eklememiz gerekir HandleTap() Update() . Bu, bir Ray cast ve bir kürenin yerleştirileceği bir isabet noktası alır.

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

Şimdi Sphere öğesini oluşturuyoruz. Sphere başlangıçta beyaz olur, ancak bu değer daha sonra üzerinde ayarlanır. Aşağıdaki yöntemi ekleyin CreateAndSaveSphere() :

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

uygulamanızı bir kez daha doğrulamak için Visual Studio ' den çalıştırın. Bu kez, beyaz küetinizi istediğiniz yüzey üzerine yerleştirmek & oluşturmak için ekrana dokunun.

Dağıtıcı modelini ayarlama

Unity ile çalışırken, tüm Unity API 'Lerinin (örneğin, UI güncelleştirmelerini yapmak için kullandığınız API 'Ler) ana iş parçacığında gerçekleştirilmesi gerekir. Ancak yazılacak kodda, diğer iş parçacıklarında geri çağrılar yaptık. Bu geri çağırmalar içindeki kullanıcı arabirimini güncelleştirmek istiyoruz, bu nedenle bir yan iş parçacığından ana iş parçacığına gitmenin bir yolu olmalıdır. Bir yan iş parçacığından ana iş parçacığında kod yürütmek için dağıtıcı modelini kullanacağız.

Bir eylem sırası olan bir üye değişkeni ekleyelim dispatchQueue . Actions'ı kuyruğa iletip kuyrukta kuyruğa atıp Ana iş parçacığında Çalıştır'ı çalıştıracak.

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

/// <summary>
/// Set this string to the Spatial Anchors account domain provided in the Spatial Anchors resource.
/// </summary>
protected string SpatialAnchorsAccountDomain = "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>

Şimdi Kuyruğa Eylem eklemenin bir yolunu ekle bakalım. hemen QueueOnUpdate() sonra Update() ekleyin:

    }
}

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

Kuyruğa alınan bir Eylem olup olduğunu kontrol etmek için Update() döngüsü kullanabiliriz. Öyleyse, Eylemin sıralarını geri alıp çalıştıracak.

    InitializeSession();
}

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

Azure Spatial Anchors SDK'sı edinin

ASA sürümünü seçme

Unity projenizde hangi ASA SDK sürümünün kullanılacağını belirlemek için lütfen aşağıdaki Unity sürümünüzü seçin.

Unity 2020 desteği, ASA SDK 2.9.0 ile eklenmiştir. Unity 2020 geliştirme için en son ASA SDK 'sını kullanmanızı öneririz.

Önemli

Karma Gerçeklik OpenXR eklentisi kullanılıyorsa, ASA SDK 2.10.0 desteklenen en düşük sürümdür. Windows xr 4.5.1 veya üzeri kullanılıyorsa, ASA SDK 2.11.0 desteklenen en düşük sürümdür.

Paketleri indirme

Sonraki adım, Unity için Azure Spatial Anchors paketlerini indirmektir.

Unity'de Azure Spatial Anchors'i kullanmak için, desteklemeyi planladnız her platform için temel paketi ( ) ve com.microsoft.azure.spatial-anchors-sdk.core platforma özgü bir paketi indirmeniz gerekir.

Platform Paket adı
Tüm Platformlar com.microsoft.azure.spatial-anchors-sdk.core@<version_number>
Android com.microsoft.azure.spatial-anchors-sdk.android@<version_number>
iOS com.microsoft.azure.spatial-anchors-sdk.ios@<version_number>
HoloLens com.microsoft.azure.spatial-anchors-sdk.windows@<version_number>

Unity için Azure Spatial Anchors çekirdek paketini ( com.microsoft.azure.spatial-anchors-sdk.core ) burada bulun. İstediğiniz sürümü seçin ve İndir düğmesini kullanarak paketi indirin. Desteklemeyi planlayın her platform için paketi indirmek için bu adımı yinelayın.

Paketleri içeri aktarma

Unity projesinde Unity projesine indirdiğiniz Azure Spatial Anchors paketlerini Unity Paket Yöneticisi.

Kodu hazırlama

Bu Visual Studio aşağıdaki içeri aktarmayı dosyanıza <ProjectName>\Assets\Scripts\AzureSpatialAnchorsScript.cs ekleyin:

Tüm farklı öğeler `AzureSpatialAnchorsScript` bir araya geldikten sonra tam sınıf dosyasının nasıl olması gerektiği burada açıklanmıştır. Bunu kendi dosyanıza göre karşılaştırmak için bir başvuru olarak kullanabilir ve farklar kaldı mı?

```csharp
using Microsoft.Azure.SpatialAnchors;

Ardından aşağıdaki üye değişkenlerini sınıfınıza AzureSpatialAnchorsScript ekleyin:

/// </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>

Yerel yer bağlantısına yerel bir Azure Spatial Anchor ekleme

Şimdi Azure Spatial Anchor'ın CloudSpatialAnchorSession'larını ayarlayabiliyoruz. İlk olarak sınıfınıza aşağıdaki InitializeSession() yöntemi AzureSpatialAnchorsScript ekleyelim. Çağrıldıktan sonra, bir Azure Spatial Anchors oturumunun oluşturularak düzgün bir şekilde başlatılmasını sağlar.

    }
}

/// <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.Configuration.AccountDomain = SpatialAnchorsAccountDomain.Trim();

    cloudSpatialAnchorSession.LogLevel = SessionLogLevel.All;

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

    cloudSpatialAnchorSession.Start();

Şimdi temsilci çağrılarını işlemek için kod yazmamız gerekiyor. Devam ettiyken buna daha fazlasını ekley edeceğiz.

    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)
{

Şimdi yönteminizi initializeSession() yönteminize Start() bağlayın.

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

Son olarak aşağıdaki kodu yönteminize CreateAndSaveSphere() ekleyin. Gerçek dünyaya yerleştirilen küreye yerel bir Azure Spatial Anchor eklemektedir.

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

Devam etmeden önce hesap Tanımlayıcısı, Anahtar ve Etki Alanı'Spatial Anchors bir Azure Spatial Anchors hesabı oluşturmanız gerekir. Bu değerler henüz yoksa, bunları almak için sonraki bölümü izleyin.

Uzamsal bağlayıcı kaynağı oluşturma

Azure Portal gidin.

Sol bölmede kaynak oluştur' u seçin.

Uzamsal bağlantıları aramak için arama kutusunu kullanın.

Uzamsal Tutturucuların aramasının sonuçlarını gösteren ekran görüntüsü.

Uzamsal bağlayıcıları seçin ve ardından Oluştur' u seçin.

Uzamsal bağlayıcı hesabı bölmesinde şunları yapın:

  • Normal alfasayısal karakterleri kullanarak benzersiz bir kaynak adı girin.

  • Kaynağı iliştirmek istediğiniz aboneliği seçin.

  • Yeni oluştur seçeneğini belirleyerek bir kaynak grubu oluşturun. Myresourcegroup olarak adlandırın ve ardından Tamam' ı seçin.

    Kaynak grubu , Web uygulamaları, veritabanları ve depolama hesapları gibi Azure kaynaklarının dağıtıldığı ve yönetildiği bir mantıksal kapsayıcıdır. Örneğin, daha sonra tek bir basit adımda kaynak grubun tamamını silmeyi seçebilirsiniz.

  • Kaynağın yerleştirileceği bir konum (bölge) seçin.

  • Kaynağı oluşturmaya başlamak için Oluştur ' u seçin.

Kaynak oluşturmak için uzamsal bağlayıcı bölmesinin ekran görüntüsü.

Kaynak oluşturulduktan sonra, Azure portal dağıtımınızın tamamlandığını gösterir.

Kaynak dağıtımının tamamlandığını gösteren ekran görüntüsü.

Kaynağa git’i seçin. Artık kaynak özelliklerini görüntüleyebilirsiniz.

Kaynağın hesap kimliği değerini daha sonra kullanmak üzere bir metin düzenleyicisine kopyalayın.

Kaynak özellikleri bölmesinin ekran görüntüsü.

Ayrıca, kaynağın hesap etki alanı değerini daha sonra kullanmak üzere bir metin düzenleyicisine kopyalayın.

Kaynağın hesap etki alanı değerini gösteren ekran görüntüsü.

Ayarlar altında erişim anahtarı' nı seçin. Birincil anahtar değerini, hesap anahtarını daha sonra kullanmak üzere bir metin düzenleyicisine kopyalayın.

Hesap için anahtarlar bölmesinin ekran görüntüsü.

Upload yer bağlantınızı buluta sabitle

Azure Hesap Tanımlayıcısı Spatial Anchors Ve Etki Alanı'nızı edin, adresine , içine ve içine Account Id SpatialAnchorsAccountId Account Key SpatialAnchorsAccountKey Account Domain SpatialAnchorsAccountDomain yapıştırın.

Son olarak, her şeyi birlikte bağlayacağız. yönteminize CreateAndSaveSphere() aşağıdaki kodu ekleyin. Küreniz oluşturulur CreateAnchorAsync() oluşturulmaz yöntemini çağırır. Yöntem geri döndüğünde aşağıdaki kod, kürenizi son bir kez güncelleştirerek rengini mavi olarak değiştirir.

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

Uygulamayı bir kez daha Visual Studio çalıştırın. Kafanızı hareket ettirin ve ardından havadan dokunarak kürenizi yer. Yeterli karemiz olduktan sonra, küre sarıya döner ve buluta yükleme başlar. Karşıya yükleme tamam olduktan sonra küreniz maviye döner. İsteğe bağlı olarak, uygulamanın gönderdiği günlük iletilerini izlemek için Visual Studio içinde hata ayıklarken Çıkış penceresini de kullanabilirsiniz. Günlük iletilerini görmek için Debug uygulamanızın yapılandırmasını Visual Studio emin olun. 'i izleyebilirsiniz ve karşıya yükleme tamamlandıktan sonra buluttan döndürülen yer noktası RecommendedForCreateProgress tanımlayıcısını görebilirsiniz.

Not

"DllNotFoundException: 'AzureSpatialAnchors' DLL'si yüklenemedi: Belirtilen modül bulunamadı." hatası alırsanız, Çözümlerinizi Temizlemeli ve Yeniden Derlemelisiniz.

Bulut uzamsal yer bağlantınızı bulma

Yer yer bağlantınız buluta yüklendi, biz de yeniden bulma girişiminde bulunduk. Şimdi aşağıdaki kodu yönteminize HandleTap() ekleriz. Bu kod şunları sağlar:

  • çağrısıyla ResetSession() durdurun ve mevcut mavi CloudSpatialAnchorSession küremizi ekrandan kaldırın.
  • Yeniden CloudSpatialAnchorSession başlat. Bunu, yerini belirleyeceğimiz yer noktasının oluşturduğu yerel yer noktası yerine buluttan geldiğinden emin olmak için bunu yapacağız.
  • Azure Spatial Anchors'a yüklediğiniz yer noktasının ne olduğunu 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.");

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

Şimdi ve yöntemlerimizi ResetSession() CleanupObjects() ekleriz. Bunları aşağıya koyabilirsiniz 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.");
            }

Şimdi sorgulamamız gereken yer noktası bulunduğu zaman çağrılan kodu oluşturmamız gerekiyor. içine InitializeSession() aşağıdaki geri çağırmaları ekleyin:


cloudSpatialAnchorSession.LogLevel = SessionLogLevel.All;

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

Şimdi CloudSpatialAnchor & yeşil bir küre oluşturmak için kod ek oalr. Ayrıca ekran dokunmayı yeniden etkinleştirerek senaryonun tamamını bir kez daha tekrarlayın: başka bir yerel yer bağlantısı oluşturun, karşıya yükleyin ve yeniden bulun.

    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)

İşte bu kadar! Senaryonun tamamını Visual Studio denemek için uygulamalarınızı son bir kez çalıştırabilirsiniz. Cihazınızın etrafında hareket edin ve beyaz kürenizi yer. Ardından, küre sarıya dönüşene kadar ortam verilerini yakalamak için kafanızı taşımaya devam edin. Yerel yer bağlantınız karşıya yükser ve küreniz maviye döner. Son olarak, ekranınıza bir kez daha dokunarak yerel yer yer imlinizi kaldırın ve buluta göre bir sorguya başlayabilirsiniz. Bulut uzamsal sabit noktası bulunana kadar cihazınızı taşımaya devam edin. Doğru konumda yeşil bir kürenin görünmesi gerekir ve senaryonun tamamını tekrarlamanız gerekir.

Her şeyi bir araya koyma

Tüm farklı öğeler AzureSpatialAnchorsScript bir araya geldikten sonra tam sınıf dosyasının nasıl olması gerektiği burada açıklanmıştır. Bunu kendi dosyanıza göre karşılaştırmak için bir başvuru olarak kullanabilir ve farklar kaldı mı?

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>
    /// Set this string to the Spatial Anchors account domain provided in the Spatial Anchors resource.
    /// </summary>
    protected string SpatialAnchorsAccountDomain = "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.Configuration.AccountDomain = SpatialAnchorsAccountDomain.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.");

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

Sonraki adımlar

Bu öğreticide, yeni bir Unity HoloLens uygulamasında Azure Spatial Anchors kullanma hakkında daha fazla HoloLens edinebilirsiniz. Yeni bir Android uygulamasında Azure Spatial Anchors hakkında daha fazla bilgi edinmek için sonraki öğreticiye devam edin.