PICO+Unity MR空间锚点
官方链接:空间锚点 | PICO 开发者平台
注意:该功能只能打包成APK在PICO 4 Ultra上真机运行,无法通过串流或PICO developer center在PC上运行。使用之前要开启视频透视。
在 Inspector 窗口中的 PXR_Manager (Script) 面板上,勾选 Spatial Anchor 选框,为应用开启空间锚点能力。然后,你可以调用空间锚点相关接口,在应用内实现空间锚点功能。
新建一个空物体名为SpatialAnchor,添加SpatialAnchor组件(指定地方放置物体)、SeethroughManager代码(开启透视)
编写代码SpatialAnchor
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Unity.XR.PXR;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;
public class SpatialAnchor : MonoBehaviour
{
public GameObject prerefAnchor;
public GameObject anchorPreview;
public GameObject firePoint;
public Text textPrompt;
public Button btnLoadAllAnchors;
public Button btnClearAllAnchors;
[SerializeField]
private InputActionReference rightGrip;
public Dictionary<ulong, AnchorInfo> anchorList = new Dictionary<ulong, AnchorInfo>();
// Start is called before the first frame update
void Start()
{
btnLoadAllAnchors.onClick.AddListener(OnBtnPressedLoadAllAnchors);
btnClearAllAnchors.onClick.AddListener(OnBtnPressedClearAllAnchors);
StartSpatialAnchorProvider();
}
private void OnEnable()
{
rightGrip.action.started += OnRightGripPressed;
rightGrip.action.canceled += OnRightGripReleased;
}
private void OnDisable()
{
rightGrip.action.started -= OnRightGripPressed;
rightGrip.action.canceled -= OnRightGripReleased;
}
//called on action.started
private void OnRightGripPressed(InputAction.CallbackContext callback)
{
ShowAnchorPreview();
}
//called on action.release
private void OnRightGripReleased(InputAction.CallbackContext callback)
{
CreateAnchor();
}
private void ShowAnchorPreview()
{
//Show anchor
anchorPreview.SetActive(true);
}
private async void StartSpatialAnchorProvider()
{
var result0 = await PXR_MixedReality.StartSenseDataProvider(PxrSenseDataProviderType.SpatialAnchor);
Debug.unityLogger.Log($"StartSenseDataProvider: {result0}");
}
private async void CreateAnchor()
{
anchorPreview.SetActive(false);
//Use Spatial Anchor Api to create anchor
//This will trigger AnchorEntityCreatedEvent
var result1 = await PXR_MixedReality.CreateSpatialAnchorAsync(firePoint.transform.position, firePoint.transform.rotation);
if (result1.result == PxrResult.SUCCESS)
{
GameObject anchorObject = Instantiate(prerefAnchor);
anchorObject.SetActive(true);
anchorObject.transform.localScale = new Vector3(0.1f, 0.1f, 0.1f);
anchorObject.transform.rotation = firePoint.transform.rotation;
anchorObject.transform.position = firePoint.transform.position;
AnchorInfo info = anchorObject.GetComponent<AnchorInfo>();
var result2 = await PXR_MixedReality.PersistSpatialAnchorAsync(result1.anchorHandle);
if (result2 == PxrResult.SUCCESS)
{
info.ShowSaveIcon(true);
}
else
{
info.ShowSaveIcon(false);
}
anchorList.Add(result1.anchorHandle, info); // 添加到锚点列表
}
}
// 异步加载所有锚点
private async void OnBtnPressedLoadAllAnchors()
{
anchorList.Clear();
var result = await PXR_MixedReality.QuerySpatialAnchorAsync(); // 查询所有空间锚点
//SetLogInfo("LoadSpatialAnchorAsync:" + result.result.ToString() + result.anchorHandleList.Count); // 记录日志
if (result.result == PxrResult.SUCCESS) // 成功查询
{
int i = 0;
foreach (var key in result.anchorHandleList) // 遍历锚点句柄
{
if (!anchorList.ContainsKey(key)) // 如果锚点列表中不存在该锚点
{
i++;
PXR_MixedReality.LocateAnchor(key, out var position, out var orientation);
GameObject anchorObject = Instantiate(prerefAnchor);
anchorObject.SetActive(true);
anchorObject.transform.localScale = new Vector3(0.1f, 0.1f, 0.1f);
anchorObject.transform.rotation = orientation;
anchorObject.transform.position = position;
AnchorInfo anchor = anchorObject.GetComponent<AnchorInfo>(); // 获取锚点组件
anchor.SetAnchorHandle(key); // 设置锚点句柄
// 定位锚点
anchorList.Add(key, anchor); // 添加到锚点列表
anchorList[key].ShowSaveIcon(true); // 显示保存图标
}
else
{
textPrompt.text = "无法加载:" + i.ToString();
}
}
}
else
{
textPrompt.text = "查询失败...";
}
}
// 异步删除所有锚点
private async void OnBtnPressedClearAllAnchors()
{
List<ulong> keys = anchorList.Keys.ToList();
for(int i = 0; i < keys.Count; i++)
{
ulong key = keys[i];
await PXR_MixedReality.UnPersistSpatialAnchorAsync(anchorList[key].anchorHandle);
textPrompt.text = "正在删除..."+i.ToString();
DestroyImmediate(anchorList[key].gameObject);
}
anchorList.Clear();
textPrompt.text = "删除完成";
}
}
锚点信息类
using System.Collections;
using System.Collections.Generic;
using Unity.XR.PXR;
using UnityEngine;
using UnityEngine.UI;
public class AnchorInfo : MonoBehaviour
{
public Text text;
public GameObject savedIcon;
[HideInInspector]
public ulong anchorHandle;
// 设置锚点句柄并更新 UI 显示
public void SetAnchorHandle(ulong handle)
{
anchorHandle = handle;
text.text = "ID: " + anchorHandle;
}
// 显示保存图标
public void ShowSaveIcon(bool show)
{
savedIcon.SetActive(show);
}
private void LateUpdate()
{
// 尝试定位空间锚点
var result = PXR_MixedReality.LocateAnchor(anchorHandle, out var position, out var rotation);
if (result == PxrResult.SUCCESS)
{
// 如果成功,更新当前对象的位置和旋转
transform.position = position;
transform.rotation = rotation;
}
}
}
rightGrip输入赋值