Ориентация гарнитуры Oculus Quest при установке влияет на * некоторые * объекты в игре
В очень простой демонстрации Quest, созданной в Unity, у меня есть лазерная указка, прикрепленная к каждому контроллеру. (Указатель основан на этой серии видеороликов YouTube. Я добавил дополнительную лазерную указку к правому контроллеру.) Каждый указатель должен начинаться с позиции контроллера и распространяться на некоторое расстояние в прямом направлении контроллера.
Однако, если я использую Quest и запускаю демоверсию, не изменяя центр обзора моего Quest, я не вижу такого поведения. Вместо этого лазерные указки смещены как по положению, так и по вращению от контроллера. Иногда они начинают на 1 мировую единицу выше контроллеров. Иногда они уходят налево. Иногда они указывают на 45 градусов от прямого направления. Иногда сочетание всех трех. При запросе Unity сообщает, что начальное положение и поворот точно такие же, как у контроллеров - смещение не учитывается.
После запуска демонстрации движение лазерных указателей правильно отображается на перемещение контроллеров: и перемещение, и вращение правильно фиксируются. (Другие внутриигровые объекты вращаются вокруг вертикальной оси в зависимости от ориентации Oculus Home при запуске игры. Например, если меню находится справа от меня, когда я запускаю файл apk, то и кнопки в игре будут.)
Я не понимаю, что могло вызвать смещение при запуске и как его исправить. Я рад предоставить всю папку Unity Assets (без папки Oculus объемом 500 МБ) по запросу.
Pointer.cs (скрипт, отвечающий за создание лазерных указателей)
using UnityEngine;
using UnityEngine.EventSystems;
public class Pointer : MonoBehaviour
{
[SerializeField] private float defaultLength = 5.0f;
[SerializeField] private float startOffset = 0.05f;
[SerializeField] private GameObject pointerTip = null; // Hard-coded to be PointerTip object associated with this pointer
[SerializeField] private bool isRightPointer; // Hard-coded from inspector window
public Camera Camera { get; private set; } = null;
private LineRenderer lineRenderer = null;
private InputModule_VR inputModule = null;
private void Awake()
{
Camera = GetComponent<Camera>();
lineRenderer = GetComponent<LineRenderer>();
}
private void Start()
{
Camera.enabled = false;
// current.currentInputModule does not work
inputModule = EventSystem.current.gameObject.GetComponent<InputModule_VR>();
}
private void Update()
{
UpdateLine();
}
private void UpdateLine()
{
// Use default or distance
PointerEventData data = inputModule.Data;
RaycastHit hit = CreateRaycast();
// If nothing is hit, set to default length
float colliderDistance = hit.distance == 0 ? defaultLength : hit.distance;
float canvasDistance = data.pointerCurrentRaycast.distance == 0 ? defaultLength : data.pointerCurrentRaycast.distance;
// Get the closer distance
float targetLength = Mathf.Min(colliderDistance, canvasDistance);
// Default
Vector3 endPosition = transform.position + (transform.forward * targetLength);
// Set position of the dot
pointerTip.transform.position = endPosition;
// Set the positions of the linerenderer
Vector3 startPosition = this.transform.position + (this.transform.forward * startOffset) +
(this.transform.up * -0.01f);
if (isRightPointer)
{
startPosition += this.transform.right * 0.01f;
}
else
{
startPosition -= this.transform.right * 0.01f;
}
lineRenderer.SetPosition(0, startPosition);
lineRenderer.SetPosition(1, endPosition);
}
private RaycastHit CreateRaycast()
{
RaycastHit hit;
Ray ray = new Ray(transform.position, transform.forward);
Physics.Raycast(ray, out hit, defaultLength);
return hit;
}
}
InputModule_VR.cs (скрипт, отвечающий за взаимодействие лучей с элементами интерфейса)
using UnityEngine;
using UnityEngine.EventSystems;
public class InputModule_VR : BaseInputModule
{
private Camera pointerCamera = null;
[SerializeField] private Pointer pointer = null;
public PointerEventData Data { get; private set; } = null;
protected override void Awake()
{
// Grab the camera we'll associate with all of our canvases
pointerCamera = GameObject.Find("LeftHandPointer").GetComponent<Camera>();
pointer = GameObject.Find("LeftHandPointer").GetComponent<Pointer>();
}
protected override void Start()
{
// Make sure every canvas knows to use the pointer camera for determining
// raycast hits
Canvas[] canvases = FindObjectsOfType<Canvas>();
foreach (Canvas canvas in canvases)
{
canvas.worldCamera = pointerCamera;
}
Data = new PointerEventData(eventSystem);
Data.position = new Vector2(pointer.Camera.pixelWidth / 2, pointer.Camera.pixelHeight / 2);
}
public override void Process()
{
eventSystem.RaycastAll(Data, m_RaycastResultCache);
Data.pointerCurrentRaycast = FindFirstRaycast(m_RaycastResultCache);
HandlePointerExitAndEnter(Data, Data.pointerCurrentRaycast.gameObject);
}
public void Press()
{
Data.pointerPressRaycast = Data.pointerCurrentRaycast;
Data.pointerPress = ExecuteEvents.GetEventHandler<IPointerClickHandler>(Data.pointerPressRaycast.gameObject);
ExecuteEvents.Execute(Data.pointerPress, Data, ExecuteEvents.pointerDownHandler);
}
public void Release()
{
GameObject pointerRelease = ExecuteEvents.GetEventHandler<IPointerClickHandler>(Data.pointerCurrentRaycast.gameObject);
if (Data.pointerPress == pointerRelease)
ExecuteEvents.Execute(Data.pointerPress, Data, ExecuteEvents.pointerClickHandler);
ExecuteEvents.Execute(Data.pointerPress, Data, ExecuteEvents.pointerUpHandler);
Data.pointerPress = null;
Data.pointerCurrentRaycast.Clear();
}
}