HoloLens開發學習記錄--- 8.World Anchor 空間錨(場景固定到某位置)

        Hololens 實現全息體驗的一個特性就是場景保持。當用戶離開場景或關閉應用時,場景中的全息圖會被保存在所放置的位置,當用戶回到場景或重新打開應用時,能夠準確的還原之前場景內的全息內容。

        World Anchor(空間錨)提供了一種能夠將物體保留在特定位置和旋轉狀態上的方法,以此來保證全息對象的穩定性(即靜止參考框架),也通過它來實現場景保持。

        腳本WorldAnchorStore.cs 是實現空間錨特性的關鍵 API,爲了能夠真正保持一個全息對象,通常爲根 GameObject 添加空間錨,同時對其子 GameObject 也附上具有相對位置偏移的空間錨組件。


一、實例程序

 (一)、 用     unity2018.4.9  vs2017    創建一個新的 Unity 項目 VoiceDemo,初始化項目:

1.導入 MRTK 包                      (版本 HoloToolkit-Unity-2017.4.2.0)

2.應用項目設置爲 MR 項目       (一鍵設置成爲可以部署的環境)

3.使用 HoloLensCamera 替代默認相機

4.添加 CursorWithFeedback       (識別並反饋手勢的光標控件)

5.添加 InputManager                   (作爲輸入源管理器,管理 gaze,gesture,speech等)

6.設置 InputManager 的 SimpleSinglePointerSelector 腳本的 Cursor 屬性爲添加的 CursorWithFeedback      (添加手勢源到inputmanger)

7.添加一個 Cube     改動z:4

最終 Hierarchy 結構如下:

(二)、編寫腳本 CubeCommand.cs 並將其添加到 Cube 上。

當添加腳本遇到錯誤時,更改腳本名稱試試。    

        項目實驗效果:  打開程序,立方體位於前方4m處。   點擊立方體時,立方體會隨着視野移動,再次點擊則被放置。 重新打開程序時,立方體的位置已經改變。

 

using UnityEngine;
using HoloToolkit.Unity.InputModule;
using UnityEngine.XR.WSA;
using UnityEngine.XR.WSA.Persistence;
using System.Linq;

public class CubeCommand1 : MonoBehaviour, IInputClickHandler     //因爲要《點擊跟隨視野》和《點擊放置》,引用該接口 IInputClickHandler
{
    // 定義對象:   被保存的錨點
    public string ObjectAnchorStoreName;
    //定義對象:  存儲錨點的倉庫     屬於UnityEngine.XR.WSA.Persistence  
    WorldAnchorStore anchorStore;
    // 是否可被移動
    bool HasMove = false;

    void Start()
    {
        WorldAnchorStore.GetAsync(AnchorStoreReady);   //WorldAnchorStore的靜態方法,獲取WorldAnchorStore實例。
    }

    private void AnchorStoreReady(WorldAnchorStore store)
    {
        anchorStore = store;

        if (anchorStore.GetAllIds().Contains(ObjectAnchorStoreName))  //GetAllIds獲取當前持久化的WorldAnchors的所有標識符,返回的時string。判斷是否有當前的錨點  
        {
            anchorStore.Load(ObjectAnchorStoreName, gameObject);   //若錨點存在,則加載到遊戲對象上。
        }
    }

    void Update()
    {
        // 如果立方體可移動,更新其位置
        if (HasMove)
        {
            gameObject.transform.position = Camera.main.transform.position + Camera.main.transform.forward * 2;
        }
    }

    public void OnInputClicked(InputClickedEventData eventData)  //   IInputClickHandler接口調用的方法  點擊就調用
    {
        if (anchorStore == null)
        {
            return;
        }

        if (HasMove)
        {
            // 當物體處於移動狀態,且再次被點擊後
            WorldAnchor anchor = gameObject.AddComponent<WorldAnchor>();

            if (anchor.isLocated)   
            {
                anchorStore.Save(ObjectAnchorStoreName, anchor);
            }
            else
            {
                anchor.OnTrackingChanged += Anchor_OnTrackingChanged;
            }
        }
        else
        {
            // 當物體處於不可移動,且再次被點擊後
            WorldAnchor anchor = gameObject.GetComponent<WorldAnchor>();
            if (anchor != null)
            {
                DestroyImmediate(anchor);
            }

            if (anchorStore.GetAllIds().Contains(ObjectAnchorStoreName))
            {
                anchorStore.Delete(ObjectAnchorStoreName);
            }
        }

        HasMove = !HasMove;
    }

    void Anchor_OnTrackingChanged(WorldAnchor self, bool located)
    {
        if (located)
        {
            anchorStore.Save(ObjectAnchorStoreName, self);
            // 取消事件監聽
            self.OnTrackingChanged -= Anchor_OnTrackingChanged;
        }
    }
}

 

三、相關API解析

添加命名空間:

using UnityEngine.XR.WSA;
using UnityEngine.XR.WSA.Persistence;

(1)爲物體添加空間錨

WorldAnchor anchor = gameObject.AddComponent<WorldAnchor>();

(2)銷燬物體上的空間錨

當物體被添加空間錨後,該物體不能夠再移動。

假設要單純的銷燬空間錨,不需要移動物體,則使用Destroy():

Destroy(gameObject.GetComponent<WorldAnchor>());

假設銷燬空間錨,之後需要移動物體,使用 DestroyImmediate() 來銷燬空間錨

DestroyImmediate(gameObject.GetComponent<WorldAnchor>());

(3)移動已經添加空間錨的物體

   之前說過物體被添加空間錨後無法移動,因此步驟如下:

  1.    銷燬空間錨
  2.    移動物體
  3.     重新添加空間錨
DestroyImmediate(gameObject.GetComponent<WorldAnchor>());
gameObject.transform.position = new Vector3(0, 0, 2);
WorldAnchor anchor = gameObject.AddComponent<WorldAnchor>();

(4)讀取已保存的所有空間錨

通過調用 WorldAnchorStore.GetAsync() 來加載所有保存的空間錨。

void Start () {
    WorldAnchorStore.GetAsync(AnchorStoreReady);
}

private void AnchorStoreReady(WorldAnchorStore store)
{
    // 讀取所有已保存的空間錨
    WorldAnchorStore anchorStore = store;
    string[] ids = anchorStore.GetAllIds();
}

(5)保存空間錨

/**
 * 返回是否保存成功
 * @Param anchorName: 保存的錨點名
 * @Param anchor: 物體上的錨點組件
 */
bool saved = anchorStore.Save(anchorName, anchor);

(6)加載已保存的空間錨到物體上

/**
 * 當加載成功時返回錨點對象
 * @Param anchorName: 保存的錨點名
 * @Param gameObject: 被添加空間錨的目標對象
 */
WorldAnchor anchor = anchorStore.Load(anchorName, gameObject);

(7)刪除已保存的空間錨

/**
 * 返回是否刪除成功
 * @Param anchorName: 刪除的錨點名
 */
bool deleted = anchorStore.Delete(anchorName);

(8)OnTrackingChanged 事件

     當我們爲物體添加空間錨的情況下,有些情況空間錨會被立即定位到,即:

                 WorldAnchor anchor = gameObject.AddComponent<WorldAnchor>();
                 // anchor.isLocated == true
     但是有些情況下不會被立即定位到,我們可以爲空間錨綁定 OnTrackingChanged 事件,當它定位成功後,再繼續後面的邏輯。

                 anchor.OnTrackingChanged += Anchor_OnTrackingChanged;
      例如,我們需要爲物體添加空間錨,等到被定位後將其保存起來,那麼代碼大概如下:
 

void OnSelect() {
    WorldAnchor anchor = gameObject.AddComponent<WorldAnchor>();
    if(anchor.isLocated) {
        anchorStore.Save("測試錨點名", anchor);
    } else {
        anchor.OnTrackingChanged += Anchor_OnTrackingChanged;
    }
}

void Anchor_OnTrackingChanged(WorldAnchor self, bool located) {
    if(located) {
        anchorStore.Save("測試錨點名", self);
        // 取消事件監聽
        self.OnTrackingChanged -= Anchor_OnTrackingChanged;
    }
}

 

四、錨點共享

錨點可以在多個設備間共享,來使得不同設備可以使用相同的空間位置,可以通過 WorldAnchorTransferBatch將錨點信息導出爲byte數組,在另外一臺設備中加載這個數組並重新還原出錨點信息。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章