Search Unity

[Quest] Headset orientation when mounted affects *some* objects in-game

Discussion in 'VR' started by Grundulum, Nov 21, 2019.

  1. Grundulum

    Grundulum

    Joined:
    Nov 21, 2019
    Posts:
    1
    This thread is a duplicate of a Stack Overflow question asked here.

    ------------------------------------------------------------

    In a very barebones Quest demo built in Unity, I have a laser pointer attached to each controller. (The pointer is based off of this series of YouTube videos. I have added an additional laser pointer to the right controller.) Each pointer is supposed to start at the controller's position, and propagate some distance along the controller's forward direction.



    However, if I put on the Quest and launch the demo without recentering my Quest's view, that is not the behavior I see. Instead, the laser pointers are offset in both position and rotation from the controller. Sometimes they begin 1 world unit above the controllers. Sometimes they are off to the left. Sometimes they point 45 degrees away from the forward direction. Sometimes a combination of all three. When queried, Unity reports that the start position and rotation are precisely that of the controllers -- it does not observe the offset.



    After the demo starts, the laser pointers' movement maps correctly to that of the controllers: both translation and rotation are correctly captured. (Other in-game objects are rotated around the vertical axis based on the orientation of Oculus Home when the game was launched. For example, if the menu is to my right when I launch the apk file, so will be the buttons in-game.)

    I am at a loss for what could be causing the offset at launch, and how I could correct it. I am happy to provide the entire Unity Assets folder (without the 500MB Oculus folder) upon request.

    ------------------------------------------------------------

    Pointer.cs (script responsible for creating the laser pointers)
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.EventSystems;
    3.  
    4. public class Pointer : MonoBehaviour
    5. {
    6.     [SerializeField] private float defaultLength = 5.0f;
    7.     [SerializeField] private float startOffset = 0.05f;
    8.     [SerializeField] private GameObject pointerTip = null; // Hard-coded to be PointerTip object associated with this pointer
    9.     [SerializeField] private bool isRightPointer; // Hard-coded from inspector window
    10.  
    11.     public Camera Camera { get; private set; } = null;
    12.  
    13.     private LineRenderer lineRenderer = null;
    14.     private InputModule_VR inputModule = null;
    15.  
    16.     private void Awake()
    17.     {
    18.         Camera = GetComponent<Camera>();
    19.  
    20.         lineRenderer = GetComponent<LineRenderer>();
    21.     }
    22.  
    23.     private void Start()
    24.     {
    25.         Camera.enabled = false;
    26.         // current.currentInputModule does not work
    27.         inputModule = EventSystem.current.gameObject.GetComponent<InputModule_VR>();
    28.     }
    29.  
    30.     private void Update()
    31.     {
    32.         UpdateLine();
    33.     }
    34.  
    35.     private void UpdateLine()
    36.     {
    37.         // Use default or distance
    38.         PointerEventData data = inputModule.Data;
    39.         RaycastHit hit = CreateRaycast();
    40.  
    41.         // If nothing is hit, set to default length
    42.         float colliderDistance = hit.distance == 0 ? defaultLength : hit.distance;
    43.         float canvasDistance = data.pointerCurrentRaycast.distance == 0 ? defaultLength : data.pointerCurrentRaycast.distance;
    44.  
    45.         // Get the closer distance
    46.         float targetLength = Mathf.Min(colliderDistance, canvasDistance);
    47.  
    48.         // Default
    49.         Vector3 endPosition = transform.position + (transform.forward * targetLength);
    50.  
    51.         // Set position of the dot
    52.         pointerTip.transform.position = endPosition;
    53.  
    54.         // Set the positions of the linerenderer
    55.         Vector3 startPosition = this.transform.position + (this.transform.forward * startOffset) +
    56.             (this.transform.up * -0.01f);
    57.         if (isRightPointer)
    58.         {
    59.             startPosition += this.transform.right * 0.01f;
    60.         }
    61.         else
    62.         {
    63.             startPosition -= this.transform.right * 0.01f;
    64.         }
    65.         lineRenderer.SetPosition(0, startPosition);
    66.         lineRenderer.SetPosition(1, endPosition);
    67.     }
    68.  
    69.     private RaycastHit CreateRaycast()
    70.     {
    71.         RaycastHit hit;
    72.         Ray ray = new Ray(transform.position, transform.forward);
    73.         Physics.Raycast(ray, out hit, defaultLength);
    74.  
    75.         return hit;
    76.     }
    77. }
    ------------------------------------------------------------

    InputModule_VR.cs (script responsible for interactions between rays and UI elements)
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.EventSystems;
    3.  
    4. public class InputModule_VR : BaseInputModule
    5. {
    6.     private Camera pointerCamera = null;
    7.     [SerializeField] private Pointer pointer = null;
    8.  
    9.     public PointerEventData Data { get; private set; } = null;
    10.  
    11.     protected override void Awake()
    12.     {
    13.         // Grab the camera we'll associate with all of our canvases
    14.         pointerCamera = GameObject.Find("LeftHandPointer").GetComponent<Camera>();
    15.  
    16.         pointer = GameObject.Find("LeftHandPointer").GetComponent<Pointer>();
    17.     }
    18.  
    19.     protected override void Start()
    20.     {
    21.         // Make sure every canvas knows to use the pointer camera for determining
    22.         //   raycast hits
    23.         Canvas[] canvases = FindObjectsOfType<Canvas>();
    24.         foreach (Canvas canvas in canvases)
    25.         {
    26.             canvas.worldCamera = pointerCamera;
    27.         }
    28.  
    29.         Data = new PointerEventData(eventSystem);
    30.         Data.position = new Vector2(pointer.Camera.pixelWidth / 2, pointer.Camera.pixelHeight / 2);
    31.     }
    32.  
    33.     public override void Process()
    34.     {
    35.         eventSystem.RaycastAll(Data, m_RaycastResultCache);
    36.         Data.pointerCurrentRaycast = FindFirstRaycast(m_RaycastResultCache);
    37.  
    38.         HandlePointerExitAndEnter(Data, Data.pointerCurrentRaycast.gameObject);
    39.     }
    40.  
    41.     public void Press()
    42.     {
    43.         Data.pointerPressRaycast = Data.pointerCurrentRaycast;
    44.  
    45.         Data.pointerPress = ExecuteEvents.GetEventHandler<IPointerClickHandler>(Data.pointerPressRaycast.gameObject);
    46.  
    47.         ExecuteEvents.Execute(Data.pointerPress, Data, ExecuteEvents.pointerDownHandler);
    48.     }
    49.  
    50.     public void Release()
    51.     {
    52.         GameObject pointerRelease = ExecuteEvents.GetEventHandler<IPointerClickHandler>(Data.pointerCurrentRaycast.gameObject);
    53.  
    54.         if (Data.pointerPress == pointerRelease)
    55.             ExecuteEvents.Execute(Data.pointerPress, Data, ExecuteEvents.pointerClickHandler);
    56.  
    57.         ExecuteEvents.Execute(Data.pointerPress, Data, ExecuteEvents.pointerUpHandler);
    58.  
    59.         Data.pointerPress = null;
    60.  
    61.         Data.pointerCurrentRaycast.Clear();
    62.     }
    63. }