Unity 中的本地定位点传输
在无法使用 Azure 空间定位点的情况下,HoloLens 设备可通过局部定位点传输功能导出要由另一个 HoloLens 设备导入的定位点。
注意
本地定位点传输功能提供的定位点回收没有 Azure 空间定位点可靠,且此方法不支持 iOS 和 Android 设备。
设置 SpatialPerception 功能
为了使应用能够传输空间定位点,需要启用 SpatialPerception 功能。
如何启用 SpatialPerception 功能:
- 在 Unity 编辑器中,打开“播放器设置”窗格(“编辑”>“项目设置”>“播放器”)
- 单击“Microsoft Store”选项卡
- 展开“发布设置”并勾选“功能”列表中的“SpatialPerception”功能
注意
如果已将 Unity 项目导出到 Visual Studio 解决方案,则需要导出到新文件夹或在 Visual Studio 的 AppxManifest 中手动设置此功能。
定位点传输
命名空间:UnityEngine.XR.WSA.Sharing
类型:WorldAnchorTransferBatch
若要传输 WorldAnchor,需要建立要传输的定位点。 某个 HoloLens 的用户扫描其环境,以手动或编程方式选择空间中的一个点作为共享体验的定位点。 然后,可以对表示此点的数据进行序列化,并将其传输到在体验中共享的其他设备。 然后,每个设备都会反序列化定位点数据,并尝试在空间中定位该点。 为了使“定位点传输”正常运行,每个设备都需要扫描足够多的环境信息,以便可以标识定位点表示的点。
安装
此页上的示例代码中有几个字段需要初始化:
- GameObject rootGameObject 是 Unity 中的一个 GameObject,其中包含 WorldAnchor 组件。 共享体验中的某个用户将放置此 GameObject,并将数据导出给其他用户。
- WorldAnchor gameRootAnchor 是位于 rootGameObject 上的 UnityEngine.XR.WSA.WorldAnchor。
- byte [] importedData 是每个客户端通过网络接收的序列化定位点的字节数组。
public GameObject rootGameObject;
private UnityEngine.XR.WSA.WorldAnchor gameRootAnchor;
void Start ()
{
gameRootAnchor = rootGameObject.GetComponent<UnityEngine.XR.WSA.WorldAnchor>();
if (gameRootAnchor == null)
{
gameRootAnchor = rootGameObject.AddComponent<UnityEngine.XR.WSA.WorldAnchor>();
}
}
导出
若要进行导出,我们只需要一个 WorldAnchor,并知晓我们要如何称呼它来使它对接收的应用有意义。 共享体验中的一个客户端将执行以下步骤来导出共享定位点:
- 创建 WorldAnchorTransferBatch
- 添加要传输的 WorldAnchors
- 开始导出
- 在数据变为可用时处理 OnExportDataAvailable 事件
- 处理 OnExportComplete 事件
创建一个 WorldAnchorTransferBatch 来封装要传输的内容,然后将其导出为字节:
private void ExportGameRootAnchor()
{
WorldAnchorTransferBatch transferBatch = new WorldAnchorTransferBatch();
transferBatch.AddWorldAnchor("gameRoot", this.gameRootAnchor);
WorldAnchorTransferBatch.ExportAsync(transferBatch, OnExportDataAvailable, OnExportComplete);
}
数据变得可用时,将字节发送到客户端,或在数据段可用时进行缓冲,然后通过任何所需的方法进行发送:
private void OnExportDataAvailable(byte[] data)
{
TransferDataToClient(data);
}
导出完成后,如果已开始传输数据但序列化失败,则让客户端丢弃数据。 如果序列化成功,则告知客户端所有数据已传输,可以开始导入了:
private void OnExportComplete(SerializationCompletionReason completionReason)
{
if (completionReason != SerializationCompletionReason.Succeeded)
{
SendExportFailedToClient();
}
else
{
SendExportSucceededToClient();
}
}
导入
接收到发送方发来的所有字节后,可以将数据导回到 WorldAnchorTransferBatch 中,并将根游戏对象锁定到相同的物理位置。 注意:导入有时会暂时性失败,这时需要重试:
// This byte array should have been updated over the network from TransferDataToClient
private byte[] importedData;
private int retryCount = 3;
private void ImportRootGameObject()
{
WorldAnchorTransferBatch.ImportAsync(importedData, OnImportComplete);
}
private void OnImportComplete(SerializationCompletionReason completionReason, WorldAnchorTransferBatch deserializedTransferBatch)
{
if (completionReason != SerializationCompletionReason.Succeeded)
{
Debug.Log("Failed to import: " + completionReason.ToString());
if (retryCount > 0)
{
retryCount--;
WorldAnchorTransferBatch.ImportAsync(importedData, OnImportComplete);
}
return;
}
this.gameRootAnchor = deserializedTransferBatch.LockObject("gameRoot", this.rootGameObject);
}
通过 LockObject 调用锁定 GameObject 后,它将具有一个 WorldAnchor,这会将其保持在世界中的同一物理位置,但在 Unity 坐标空间中它可能会位于与其他用户不同的位置。