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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sun_t89/article/details/52432807

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

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

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

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

本文实现以下功能

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

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

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


// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using UnityEngine;
using UnityEngine.VR.WSA.Input;

namespace HoloToolkit.Unity
{
    /// <summary>
    /// GestureManager creates a gesture recognizer and signs up for a tap gesture.
    /// When a tap gesture is detected, GestureManager uses GazeManager to find the game object.
    /// GestureManager then sends a message to that game object.
    /// </summary>
    [RequireComponent(typeof(GazeManager))]
    public partial class GestureManager : Singleton<GestureManager>
    {
        /// <summary>
        /// Key to press in the editor to select the currently gazed hologram
        /// </summary>
        public KeyCode EditorSelectKey = KeyCode.Space;

        /// <summary>
        /// To select even when a hologram is not being gazed at,
        /// set the override focused object.
        /// If its null, then the gazed at object will be selected.
        /// </summary>
        public GameObject OverrideFocusedObject
        {
            get; set;
        }

        /// <summary>
        /// Gets the currently focused object, or null if none.
        /// </summary>
        public GameObject FocusedObject
        {
            get { return focusedObject; }
        }

        private GestureRecognizer gestureRecognizer;
        private GameObject focusedObject;

        void Start()
        {
            // Create a new GestureRecognizer. Sign up for tapped events.
            gestureRecognizer = new GestureRecognizer();
            gestureRecognizer.SetRecognizableGestures(GestureSettings.Tap);

            gestureRecognizer.TappedEvent += GestureRecognizer_TappedEvent;

            // Start looking for gestures.
            gestureRecognizer.StartCapturingGestures();
        }

        private void OnTap()
        {
            if (focusedObject != null)
            {
                focusedObject.SendMessage("OnSelect");
            }
        }

        private void GestureRecognizer_TappedEvent(InteractionSourceKind source, int tapCount, Ray headRay)
        {
            OnTap();
        }

        void LateUpdate()
        {
            GameObject oldFocusedObject = focusedObject;

            if (GazeManager.Instance.Hit &&
                OverrideFocusedObject == null &&
                GazeManager.Instance.HitInfo.collider != null)
            {
                // If gaze hits a hologram, set the focused object to that game object.
                // Also if the caller has not decided to override the focused object.
                focusedObject = GazeManager.Instance.HitInfo.collider.gameObject;
            }
            else
            {
                // If our gaze doesn't hit a hologram, set the focused object to null or override focused object.
                focusedObject = OverrideFocusedObject;
            }

            if (focusedObject != oldFocusedObject)
            {
                // If the currently focused object doesn't match the old focused object, cancel the current gesture.
                // Start looking for new gestures.  This is to prevent applying gestures from one hologram to another.
                gestureRecognizer.CancelGestures();
                gestureRecognizer.StartCapturingGestures();
            }

#if UNITY_EDITOR
            if (Input.GetMouseButtonDown(1) || Input.GetKeyDown(EditorSelectKey))
            {
                OnTap();
            }
#endif
        }

        void OnDestroy()
        {
            gestureRecognizer.StopCapturingGestures();
            gestureRecognizer.TappedEvent -= GestureRecognizer_TappedEvent;
        }
    }
}

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

CubeScript.cs如下

using UnityEngine;
using System.Collections;
using UnityEngine.VR.WSA.Persistence;
using System;
using UnityEngine.VR.WSA;

public class CubeScript : MonoBehaviour {

    public string ObjectAnchorStoreName;

    WorldAnchorStore anchorStore;

    bool Placing = false;
    void Start () {
        //获取WorldAnchorStore 对象
        WorldAnchorStore.GetAsync(AnchorStoreReady);
    }

    private void AnchorStoreReady(WorldAnchorStore store)
    {
        anchorStore = store;
        string[] ids = anchorStore.GetAllIds();
        //遍历之前保存的空间锚,载入指定id场景对象信息
        for (int index = 0; index < ids.Length; index++)
        {
            if (ids[index] == ObjectAnchorStoreName)
            {
                WorldAnchor wa = anchorStore.Load(ids[index], gameObject);
                break;
            }
        }
    }

    // Update is called once per frame
    void Update () {
        if (Placing)
        {
            //当Cube处于可移动状态,根据凝视射线的位置,更新Cube的位置
            gameObject.transform.position = Camera.main.transform.position + Camera.main.transform.forward * 2;
        }
    }

    void OnSelect()
    {
        if (anchorStore == null)
        {
            return;
        }

        if (Placing)
        {
            //当再次点击全息对象时,保存空间锚信息
            WorldAnchor attachingAnchor = gameObject.AddComponent<WorldAnchor>();
            if (attachingAnchor.isLocated)
            {
                bool saved = anchorStore.Save(ObjectAnchorStoreName, attachingAnchor);
            }
            else
            {
                //有时空间锚能够立刻被定位到。这时候,给对象添加空间锚后,空间锚组件的isLocated属性
                //值将会被设为true,这时OnTrackingChanged事件将不会被触发。因此,在添加空间锚组件
                //后,推荐立刻使用初始的isLocated状态去调用OnTrackingChanged事件
                attachingAnchor.OnTrackingChanged += AttachingAnchor_OnTrackingChanged;
            }
        }
        else
        {
            //当全息对象已附加空间锚组件后,它不能被移动。如果你需要移动全息对象的话,那么你必须这样做:
            //1.立刻销毁空间锚组件
            //2.移动全息对象
            //3.添加一个新的空间锚到全息对象上
            WorldAnchor anchor = gameObject.GetComponent<WorldAnchor>();
            if (anchor != null)
            {
                DestroyImmediate(anchor);
            }

            string[] ids = anchorStore.GetAllIds();
            for (int index = 0; index < ids.Length; index++)
            {
                if (ids[index] == ObjectAnchorStoreName)
                {
                    bool deleted = anchorStore.Delete(ids[index]);
                    break;
                }
            }
        }

        Placing = !Placing;
    }
    
    private void AttachingAnchor_OnTrackingChanged(WorldAnchor self, bool located)
    {
        if (located)
        {
            bool saved = anchorStore.Save(ObjectAnchorStoreName, self);
            self.OnTrackingChanged -= AttachingAnchor_OnTrackingChanged;
        }
    }
}


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

4、运行测试

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


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


3)关闭应用


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






没有更多推荐了,返回首页