进场景后,头显朝向和编辑器里摆的 Main Camera 对不上,需要手动用home键重置。所以写了一个进场自动重置的脚本。
原因
运行时 Main Camera 的位姿由头显追踪驱动(TrackedPoseDriver),并不是在 Scene 里摆的那个 Transform。根据Unity官方对XR Origin的说明XR Origin | XR 核心工具 | 2.6.0 --- XR Origin | XR Core Utilities | 2.6.0:
- XR Origin 代表追踪空间的中心(0, 0, 0)
- XR Origin 在 Session 启动时由 XR 系统决定
- HMD Reset 时,XR Origin 本身不会移动; Camera 相对 Origin 的偏移会改变
解决
场景设置
使用BuildingBlock创建相机相关结构,或确保如下结构
XR Origin (XROrigin 组件)
└── Camera Offset
└── Main Camera (TrackedPoseDriver 组件+ Camera组件)
Tracking Origin Mode设置
- Device:以Session启动时设备位置为原点,不含用户身高
- Floor:以地面为原点,含用户身高,站立VR更推荐
脚本
思路:
XRInputSubsystem.TryRecenter()重定位,XROrigin.MatchOriginUpCameraForward() 旋转 XR Origin,让相机朝向场景期望方向
cs
using System.Collections;
using UnityEngine;
using UnityEngine.XR;
using Unity.XR.CoreUtils;
/// <summary>
/// 进场景时把 HMD 朝向对齐到场景期望方向。
/// 编辑器里 Main Camera 的旋转不会在真机上生效------运行时位姿由头显追踪驱动。
/// </summary>
public class PicoRecenter : MonoBehaviour
{
[SerializeField] XROrigin xrOrigin;
[Tooltip("期望的"正前方"。留空则使用世界 +Z。")]
[SerializeField] Transform sceneForwardReference;
[Tooltip("启动后先调用系统 Recenter,把当前头显朝向当作 forward=0°。")]
[SerializeField] bool recenterOnStart = true;
[Tooltip("Recenter 后是否旋转 XR Origin,使相机朝向 sceneForwardReference。")]
[SerializeField] bool alignToSceneForward = true;
[Tooltip("等待追踪就绪的帧数,避免过早 Recenter 无效。")]
[SerializeField] int waitFrames = 2;
void Start()
{
if (xrOrigin == null)
xrOrigin = FindFirstObjectByType<XROrigin>();
if (xrOrigin != null)
StartCoroutine(RecenterWhenReady());
}
IEnumerator RecenterWhenReady()
{
for (var i = 0; i < waitFrames; i++)
yield return null;
if (recenterOnStart)
TryRecenter();
yield return null;
if (alignToSceneForward)
AlignCameraToSceneForward();
}
/// <summary>等价于 Quest 长按 Home / Pico 长按 Home 的系统重定位。</summary>
public void TryRecenter()
{
var subsystems = new System.Collections.Generic.List<XRInputSubsystem>();
SubsystemManager.GetSubsystems(subsystems);
foreach (var subsystem in subsystems)
subsystem.TryRecenter();
}
/// <summary>旋转 XR Origin,使相机水平朝向与场景参考一致。</summary>
public void AlignCameraToSceneForward()
{
if (xrOrigin == null)
return;
var desiredForward = sceneForwardReference != null
? sceneForwardReference.forward
: Vector3.forward;
xrOrigin.MatchOriginUpCameraForward(Vector3.up, desiredForward);
}
}
