Hololens空间锚与场景保持

来源:互联网 发布:mac版千牛多账号登陆 编辑:程序博客网 时间:2024/06/16 04:28

Hololens入门之空间锚与场景保持

World Anchor(空间锚)提供了一种能够将物体保留在特定位置和旋转状态上的方法。这保证了全息对象的稳定性,同时提供了后续在真实世界中保持全息对象位置的能力。简单地说,你可以为全息物体来添加空间锚点,这样就能在后续步骤中将全息物体准确恢复到它原来的位置。

场景保持是HoloLens全息体验的一个关键特性,当用户离开原场景中时,原场景中全息对象会保持在特定位置,当用户回到原场景时,能够准确还原原场景的全息内容。WorldAnchorStore类是实现此特性的关键API,这保证了用户能够将任何全息对象贴到任何他们想要放置的位置。

WorldAnchorStore能够允许你保持场景中空间锚的位置,为了能够真正保持全息对象,你需要单独使用特定的空间锚来追踪每一个对象。通常创建一个根GameObject并附上空间锚,同时对它的子GameObject也附上具有相对位置偏移的空间锚组件。

本文实现以下功能

移动两个Cube的位置,关闭应用,,然后通过场景保持在下一次启动应用时,能够恢复应用关闭前Cube所处的位置

本文示例在 Hololens入门之凝视 的基础上进行修改开发

1、在Manager中添加GestureManager.cs脚本组件(直接使用HoloToolkit中的GestureManager.cs)


[csharp] view plain copy
  1. // Copyright (c) Microsoft Corporation. All rights reserved.  
  2. // Licensed under the MIT License. See LICENSE in the project root for license information.  
  3.   
  4. using UnityEngine;  
  5. using UnityEngine.VR.WSA.Input;  
  6.   
  7. namespace HoloToolkit.Unity  
  8. {  
  9.     /// <summary>  
  10.     /// GestureManager creates a gesture recognizer and signs up for a tap gesture.  
  11.     /// When a tap gesture is detected, GestureManager uses GazeManager to find the game object.  
  12.     /// GestureManager then sends a message to that game object.  
  13.     /// </summary>  
  14.     [RequireComponent(typeof(GazeManager))]  
  15.     public partial class GestureManager : Singleton<GestureManager>  
  16.     {  
  17.         /// <summary>  
  18.         /// Key to press in the editor to select the currently gazed hologram  
  19.         /// </summary>  
  20.         public KeyCode EditorSelectKey = KeyCode.Space;  
  21.   
  22.         /// <summary>  
  23.         /// To select even when a hologram is not being gazed at,  
  24.         /// set the override focused object.  
  25.         /// If its null, then the gazed at object will be selected.  
  26.         /// </summary>  
  27.         public GameObject OverrideFocusedObject  
  28.         {  
  29.             getset;  
  30.         }  
  31.   
  32.         /// <summary>  
  33.         /// Gets the currently focused object, or null if none.  
  34.         /// </summary>  
  35.         public GameObject FocusedObject  
  36.         {  
  37.             get { return focusedObject; }  
  38.         }  
  39.   
  40.         private GestureRecognizer gestureRecognizer;  
  41.         private GameObject focusedObject;  
  42.   
  43.         void Start()  
  44.         {  
  45.             // Create a new GestureRecognizer. Sign up for tapped events.  
  46.             gestureRecognizer = new GestureRecognizer();  
  47.             gestureRecognizer.SetRecognizableGestures(GestureSettings.Tap);  
  48.   
  49.             gestureRecognizer.TappedEvent += GestureRecognizer_TappedEvent;  
  50.   
  51.             // Start looking for gestures.  
  52.             gestureRecognizer.StartCapturingGestures();  
  53.         }  
  54.   
  55.         private void OnTap()  
  56.         {  
  57.             if (focusedObject != null)  
  58.             {  
  59.                 focusedObject.SendMessage("OnSelect");  
  60.             }  
  61.         }  
  62.   
  63.         private void GestureRecognizer_TappedEvent(InteractionSourceKind source, int tapCount, Ray headRay)  
  64.         {  
  65.             OnTap();  
  66.         }  
  67.   
  68.         void LateUpdate()  
  69.         {  
  70.             GameObject oldFocusedObject = focusedObject;  
  71.   
  72.             if (GazeManager.Instance.Hit &&  
  73.                 OverrideFocusedObject == null &&  
  74.                 GazeManager.Instance.HitInfo.collider != null)  
  75.             {  
  76.                 // If gaze hits a hologram, set the focused object to that game object.  
  77.                 // Also if the caller has not decided to override the focused object.  
  78.                 focusedObject = GazeManager.Instance.HitInfo.collider.gameObject;  
  79.             }  
  80.             else  
  81.             {  
  82.                 // If our gaze doesn't hit a hologram, set the focused object to null or override focused object.  
  83.                 focusedObject = OverrideFocusedObject;  
  84.             }  
  85.   
  86.             if (focusedObject != oldFocusedObject)  
  87.             {  
  88.                 // If the currently focused object doesn't match the old focused object, cancel the current gesture.  
  89.                 // Start looking for new gestures.  This is to prevent applying gestures from one hologram to another.  
  90.                 gestureRecognizer.CancelGestures();  
  91.                 gestureRecognizer.StartCapturingGestures();  
  92.             }  
  93.  
  94. #if UNITY_EDITOR  
  95.             if (Input.GetMouseButtonDown(1) || Input.GetKeyDown(EditorSelectKey))  
  96.             {  
  97.                 OnTap();  
  98.             }  
  99. #endif  
  100.         }  
  101.   
  102.         void OnDestroy()  
  103.         {  
  104.             gestureRecognizer.StopCapturingGestures();  
  105.             gestureRecognizer.TappedEvent -= GestureRecognizer_TappedEvent;  
  106.         }  
  107.     }  
  108. }  

2、新增两个Cube GameObject,添加脚本组件CubeScript.cs

CubeScript.cs如下

[csharp] view plain copy
  1. using UnityEngine;  
  2. using System.Collections;  
  3. using UnityEngine.VR.WSA.Persistence;  
  4. using System;  
  5. using UnityEngine.VR.WSA;  
  6.   
  7. public class CubeScript : MonoBehaviour {  
  8.   
  9.     public string ObjectAnchorStoreName;  
  10.   
  11.     WorldAnchorStore anchorStore;  
  12.   
  13.     bool Placing = false;  
  14.     void Start () {  
  15.         //获取WorldAnchorStore 对象  
  16.         WorldAnchorStore.GetAsync(AnchorStoreReady);  
  17.     }  
  18.   
  19.     private void AnchorStoreReady(WorldAnchorStore store)  
  20.     {  
  21.         anchorStore = store;  
  22.         string[] ids = anchorStore.GetAllIds();  
  23.         //遍历之前保存的空间锚,载入指定id场景对象信息  
  24.         for (int index = 0; index < ids.Length; index++)  
  25.         {  
  26.             if (ids[index] == ObjectAnchorStoreName)  
  27.             {  
  28.                 WorldAnchor wa = anchorStore.Load(ids[index], gameObject);  
  29.                 break;  
  30.             }  
  31.         }  
  32.     }  
  33.   
  34.     // Update is called once per frame  
  35.     void Update () {  
  36.         if (Placing)  
  37.         {  
  38.             //当Cube处于可移动状态,根据凝视射线的位置,更新Cube的位置  
  39.             gameObject.transform.position = Camera.main.transform.position + Camera.main.transform.forward * 2;  
  40.         }  
  41.     }  
  42.   
  43.     void OnSelect()  
  44.     {  
  45.         if (anchorStore == null)  
  46.         {  
  47.             return;  
  48.         }  
  49.   
  50.         if (Placing)  
  51.         {  
  52.             //当再次点击全息对象时,保存空间锚信息  
  53.             WorldAnchor attachingAnchor = gameObject.AddComponent<WorldAnchor>();  
  54.             if (attachingAnchor.isLocated)  
  55.             {  
  56.                 bool saved = anchorStore.Save(ObjectAnchorStoreName, attachingAnchor);  
  57.             }  
  58.             else  
  59.             {  
  60.                 //有时空间锚能够立刻被定位到。这时候,给对象添加空间锚后,空间锚组件的isLocated属性  
  61.                 //值将会被设为true,这时OnTrackingChanged事件将不会被触发。因此,在添加空间锚组件  
  62.                 //后,推荐立刻使用初始的isLocated状态去调用OnTrackingChanged事件  
  63.                 attachingAnchor.OnTrackingChanged += AttachingAnchor_OnTrackingChanged;  
  64.             }  
  65.         }  
  66.         else  
  67.         {  
  68.             //当全息对象已附加空间锚组件后,它不能被移动。如果你需要移动全息对象的话,那么你必须这样做:  
  69.             //1.立刻销毁空间锚组件  
  70.             //2.移动全息对象  
  71.             //3.添加一个新的空间锚到全息对象上  
  72.             WorldAnchor anchor = gameObject.GetComponent<WorldAnchor>();  
  73.             if (anchor != null)  
  74.             {  
  75.                 DestroyImmediate(anchor);  
  76.             }  
  77.   
  78.             string[] ids = anchorStore.GetAllIds();  
  79.             for (int index = 0; index < ids.Length; index++)  
  80.             {  
  81.                 if (ids[index] == ObjectAnchorStoreName)  
  82.                 {  
  83.                     bool deleted = anchorStore.Delete(ids[index]);  
  84.                     break;  
  85.                 }  
  86.             }  
  87.         }  
  88.   
  89.         Placing = !Placing;  
  90.     }  
  91.       
  92.     private void AttachingAnchor_OnTrackingChanged(WorldAnchor self, bool located)  
  93.     {  
  94.         if (located)  
  95.         {  
  96.             bool saved = anchorStore.Save(ObjectAnchorStoreName, self);  
  97.             self.OnTrackingChanged -= AttachingAnchor_OnTrackingChanged;  
  98.         }  
  99.     }  
  100. }  


3、修改CubeScript中Cube的 ObjectAnchorStoreName(用来作为空间锚的key)

4、运行测试

1) 启动应用,可以看到Cube的初始位置


2) 选中Cube,移动头部,然后再次选中Cube将Cube放置到新的位置


3)关闭应用


4)再次打开应用,能够看到两个Cube所处的位置为关闭应用前的位置,非初始位置





原创粉丝点击