World Anchor空间锚提供了一种能够将物体保留在特定位置和旋转状态上的方法。这保证了全息对象的稳定性,同时提供了后续在真实世界中保持全息对象位置的能力。简单地说,你可以为全息物体来添加空间锚点,这样就能在后续步骤中将全息物体准确恢复到它原来的位置。
Adding a World Anchor 添加空间锚
命名空间: UnityEngine.VR.WSA
类型: WorldAnchor
Unity中添加空间锚的方式很简单,如下:
WorldAnchor anchor = gameObject.AddComponent<WorldAnchor>();
Removing a World Anchor 移除空间锚
如果你不再想将GameObject固定在特定位置,同时在场景中也不想移动它,,那么可以调用Destroy()方法来销毁WorldAnchor组件,如下:
Destroy(gameObject.GetComponent<WorldAnchor>());
如果你想要立刻在场景中移动对象,那么需要调用DestroyImmediate()来销毁空间锚组件,如下:
DestroyImmediate(gameObject.GetComponent<WorldAnchor>());
Moving a World Anchored GameObject 移动锚定的全息对象
当全息对象已附加空间锚组件后,它不能被移动。如果你需要一定全息对象的话,那么你必须这样做:
- 立刻销毁空间锚组件
- 移动全息对象
- 添加一个新的空间锚到全息对象上
DestroyImmediate(gameObject.GetComponent<WorldAnchor>()); gameObject.transform.position = new Vector3(0, 0, 2); WorldAnchor anchor = gameObject.AddComponent<WorldAnchor>();
Handling Locatability Changes 处理可定位能力的变化
在某个时间点,空间锚可能不能够在世界中被定位到。如果这种情况发生,Unity将不能更新锚定对象的位置。在应用运行时,这也肯能会发生变化。不能够及时处理可定位能力的变化,可能会导致对象不会出现在准确的位置上。
为了追踪可定位能力的变化,需要采取如下做法:
- 订阅OnTrackingChanged事件
- 处理此事件
订阅事件
代码如下:
anchor.OnTrackingChanged += Anchor_OnTrackingChanged;
处理OnTrackingChanged事件
代码如下:
private void Anchor_OnTrackingChanged(WorldAnchor self, bool located) { // 根据定位状态简单的显示或者隐藏对象 self.gameObject.SetActiveRecursively(located); }
设定明确的初始状态
有时空间锚能够立刻被定位到。这时候,给对象添加空间锚后,空间锚组件的isLocated属性值将会被设为true,这是OnTrackingChanged事件将不会被触发。因此,在添加空间锚组件后,推荐立刻使用初始的isLocated状态去调用OnTrackingChanged事件,如下:
Anchor_OnTrackingChanged(anchor, anchor.isLocated)
具体Hoolens项目中WorldAnchor 使用流程
单例脚本中初始化WorldAnchorSotre,提取相关数据
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using UnityEngine.VR.WSA.Persistence;
using UnityEngine.UI;
using UnityEngine.VR.WSA;
public class ModelManager : Singleton<ModelManager> {
public WorldAnchorStore anchorStore;
IEnumerator Start(){
WorldAnchorStore.GetAsync(Ready);
yield return new WaitForEndOfFrame();
}
private void Ready(WorldAnchorStore store)
{
anchorStore = store;
string[] ids = anchorStore.GetAllIds();
ids.ToList().ForEach((s) =>
{
//根据数据加载相关模型
GameObject model = LoadModel(s);
//此处Load 包括Transform 位置与旋转信息
WorldAnchor wa = anchorStore.Load(s, model);
});
}
public GameObject LoadModel(string name) {
GameObject m_Model = Instantiate(Resources.Load(m_ModelPath + name)) as GameObject;
m_Model.transform.SetParent(ModelManager.Instance.transform);
m_Model.name = name;
return m_Model;
}
}
增加与删除空间锚点(具体到某个物体)
需定位的物体(可移动)
using UnityEngine;
using System;
using UnityEngine.VR.WSA.Persistence;
using UnityEngine.VR.WSA;
using System.Collections;
public class Model:Monobehaviour{
string ObjectAnchorStoreName;
public WorldAnchorStore anchorStore;
void Start(){
anchorStore = ModelManager.Instance.anchorStore;
ObjectAnchorStoreName = gameobject.name;
}
public void AnchorFunc(){
if (anchorStore == null){
return;
}
//此处不包含拖拽移动逻辑
if (!isDragging){
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);
}
DeleteAnchor(ObjectAnchorStoreName);
}
}
//根据锚点名字删除空间锚点
public void DeleteAnchor(string name) {
string[] ids = anchorStore.GetAllIds();
for (int index = 0; index < ids.Length; index++)
{
if (ids[index] == name)
{
bool deleted = anchorStore.Delete(ids[index]);
break;
}
}
}
private void AttachingAnchor_OnTrackingChanged(WorldAnchor self, bool located)
{
if (located)
{
bool saved = anchorStore.Save(ObjectAnchorStoreName, self);
self.OnTrackingChanged -= AttachingAnchor_OnTrackingChanged;
}
}
}