Search Unity

Question How to detect if headset is available and initialize XR only if true?

Discussion in 'AR/VR (XR) Discussion' started by xofreshxo, Jul 7, 2020.

  1. xofreshxo

    xofreshxo

    Joined:
    Apr 12, 2017
    Posts:
    79
    Problem
    I'm working on a game that will be primarily played in VR. There are restrictions for some users that won't allow them to use VR. Those users need to be able to play using the desktop and an FPS controller. If I try to "Initialize XR on Startup" for the case that no VR headset is connected, I get errors.

    What I've tried
    I turned off the setting "Initialize XR on Startup" and this allows me to run in desktop mode when no headset is connected.
    I have a XR controller script that should 1) Check if hardware is present and 2)Initialize XR if hardware is present.

    Issues
    The only methods I came across for detecting if there are any headsets plugged in, seem to require XR be initialized.
    Code (CSharp):
    1.     public static bool isHardwarePresent()
    2.     {
    3.         var xrDisplaySubsystems = new List<XRDisplaySubsystem>();
    4.         SubsystemManager.GetInstances<XRDisplaySubsystem>(xrDisplaySubsystems);
    5.         foreach (var xrDisplay in xrDisplaySubsystems)
    6.         {
    7.             if (xrDisplay.running)
    8.             {
    9.                 return true;
    10.             }
    11.         }
    12.         return false;
    13.     }
    I also check InputDevices and got similar results. The list is empty unless XR is initialized.

    Code (CSharp):
    1.         List<InputDevice> deviceList = new List<InputDevice>();
    2.         InputDevices.GetDevices(deviceList);
    So if I don't initialize XR, I don't detect any headsets or inputs even when one is plugged in. If I attempt to initialize the XR loader, I get errors saying that it is unable to start the Oculus XR Plugin. A chicken and egg situation

    Code (CSharp):
    1.     public IEnumerator StartXR()
    2.     {
    3.         Debug.Log("Initializing XR...");
    4.         yield return XRGeneralSettings.Instance.Manager.InitializeLoader();
    5.  
    6.         if (XRGeneralSettings.Instance.Manager.activeLoader == null)
    7.         {
    8.             Debug.LogError("Initializing XR Failed. Check Editor or Player log for details.");
    9.         }
    10.         else
    11.         {
    12.             Debug.Log("Starting XR...");
    13.             XRGeneralSettings.Instance.Manager.StartSubsystems();
    14.         }
    15.     }
    Errors are given within InitializeLoader(), so I don't have the opportunity to provide an "else" statement in the case we cannot load.

    What I'm asking
    Is there a way to check if a headset is plugged in without initializing XR? Or is there a way to change the behavior within InitializeLoader() to not break the game if it cannot start plugins or load the subsystems? Any other feedback or suggestions are appreciated.

    Thanks
     
    Last edited: Jul 7, 2020
  2. jeromeatunity

    jeromeatunity

    Joined:
    Jul 1, 2018
    Posts:
    63
    In general, breaking the controllers into separate scenes or prefabs and loading or instantiating them based on the availability of an XR sub-system after initialization. The default behaviour is to initialize on load and check in the first scene if an XR subsystem is loaded.

    Iterate through the display subsystems to see if anything running, indicating the user has an XR device connected. If VR is active, there will be an object in that list that returns true for its running property

    Code (CSharp):
    1. List<XRDisplaySubsystem> displaySubsystems = new List<XRDisplaySubsystem>();
    2.  
    3. SubsystemManager.GetInstances<XRDisplaySubsystem>(displaySubsystems);
    4.             foreach (var subsystem in displaySubsystems)
    5.             {
    6.              ...
    7.             }
    If nothing was connected you can load your FPS controller scene otherwise load the XR controller scene.

    You can do that XR initialization manually as described here and then go through that same routine.

    hth.
     
  3. xofreshxo

    xofreshxo

    Joined:
    Apr 12, 2017
    Posts:
    79
    I try the loop that you propose in my isHardwarePresent() function. The problem is that if XR is turned off, no headsets are ever detected. If XR is initialized (manually or automatically) and no headset is connected, we get an error immediately. I need a way to see if there are any available XR headsets before XR is initialized unless there is a way to override the function for giving an error.
     
  4. badawim-idcc

    badawim-idcc

    Joined:
    Dec 10, 2015
    Posts:
    10
    I'm having the exact same problem, and would love to know if a solution is found.

    I'll keep digging on my end and post anything relevant I find.
     
  5. badawim-idcc

    badawim-idcc

    Joined:
    Dec 10, 2015
    Posts:
    10
    If you're using OpenVR,
    OpenVR.IsHMDPresent()
    seems to do the trick.

    Here's my HMDChecker code, I don't have the same needs as you do, but I think it will answer your needs.

    Code (CSharp):
    1. public class HMDChecker : MonoBehaviour
    2. {
    3.     public XRLoader OpenVRLoader;
    4.     public XRLoader MockVRLoader;
    5.  
    6.     void Start()
    7.     {
    8.         XRGeneralSettings.Instance.Manager.loaders.Clear();
    9.  
    10.         if ( OpenVR.IsHmdPresent() )
    11.         {
    12.             XRGeneralSettings.Instance.Manager.loaders.Add(OpenVRLoader);
    13.         }
    14.         else
    15.         {
    16.             XRGeneralSettings.Instance.Manager.loaders.Add(MockVRLoader);
    17.         }
    18.  
    19.         XRGeneralSettings.Instance.Manager.InitializeLoaderSync();
    20.         XRGeneralSettings.Instance.Manager.StartSubsystems();
    21.     }
    22. }
    I'm still curious if there is a way to do this using native XR stuff.
     
  6. xofreshxo

    xofreshxo

    Joined:
    Apr 12, 2017
    Posts:
    79
    Thanks for the suggestion, I guess we'll try that out. However, I wish there was a way to do it using the native XR package, like you mentioned. As of right now we would only be including the OpenVR package for checking the HMD status. It probably exists, how else are we supposed to handle players who don't plug in their headsets without crashing the game?
     
  7. badawim-idcc

    badawim-idcc

    Joined:
    Dec 10, 2015
    Posts:
    10
    Well that solution lasted an entire weekend!

    The OpenVR XR plugin got update yesterday (august 3rd) and does not support legacy input anymore. So now you alos have to import the Steam VR Plugin for inputs to work... and that causes a conflict on the
    OpenVR
    symbol:
    Error    CS0433    The type 'OpenVR' exists in both 'SteamVR, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' and 'Unity.XR.OpenVR, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'


    Back to the drawing board :'(
     
  8. Kleptine

    Kleptine

    Joined:
    Dec 23, 2013
    Posts:
    282
    This is pretty embarrassing. There doesn't seem to be any way to check for headsets without *enabling* an XRSubsystem, but enabling the Oculus subsystem immediately crashes if there is no headset connected.
     
  9. ErikWelling

    ErikWelling

    Joined:
    Dec 20, 2014
    Posts:
    1
    I might have found a solution that seems to be working (tested on 2019.4.5f1):


    Code (CSharp):
    1.     public XRLoader WMRLoader;
    2.     public XRLoader MockLoader;
    3.  
    4.     void Awake() {
    5.         XRGeneralSettings.Instance.Manager.loaders.Clear();
    6.  
    7.         //Initialize WMR.
    8.         XRGeneralSettings.Instance.Manager.loaders.Add(WMRLoader);
    9.         XRGeneralSettings.Instance.Manager.InitializeLoaderSync();
    10.         XRGeneralSettings.Instance.Manager.StartSubsystems();
    11.  
    12.         //Check if initialization was successfull.
    13.         var xrDisplaySubsystems = new List<XRDisplaySubsystem>();
    14.         SubsystemManager.GetInstances(xrDisplaySubsystems);
    15.         bool success = xrDisplaySubsystems[0].running;
    16.  
    17.         if (!success) {
    18.             //Initialization was not successfull, load mock instead.
    19.             print("loading mock");
    20.  
    21.             //Deinitialize WMR
    22.             XRGeneralSettings.Instance.Manager.loaders.Clear();
    23.             XRGeneralSettings.Instance.Manager.StopSubsystems();
    24.             XRGeneralSettings.Instance.Manager.DeinitializeLoader();
    25.  
    26.             //Initialize mock.
    27.             XRGeneralSettings.Instance.Manager.loaders.Add(MockLoader);
    28.             XRGeneralSettings.Instance.Manager.InitializeLoaderSync();
    29.             XRGeneralSettings.Instance.Manager.StartSubsystems();
    30.         }
    31.     }
    For this to work you need to disable "Initialize XR on Startup" in the XR Plug-in management settings.
    This one is made for Windows Mixed Reality but should work with any HMD.
     
  10. erlandu

    erlandu

    Joined:
    Jan 31, 2020
    Posts:
    1
    Thanks! This is the first working solution I have seen and it's easy to modify to fall back to "normal 2D" when no loader starts ok.
     
  11. nixcry

    nixcry

    Joined:
    Mar 13, 2017
    Posts:
    5
    Try this:
    Code (CSharp):
    1.         public IEnumerator StartXR()
    2.         {
    3.             Debug.Log("Initializing XR...");
    4.             yield return XRGeneralSettings.Instance.Manager.InitializeLoader();
    5.  
    6.             if (XRGeneralSettings.Instance.Manager.activeLoader == null)
    7.             {
    8.                 Debug.LogError("Initializing XR Failed. Directing to Normal Interaciton Mode...!.");
    9.                 StopXR();
    10.                 DirectToNormal();
    11.             }
    12.             else
    13.             {
    14.                 Debug.Log("Initialization Finished.Starting XR Subsystems...");
    15.  
    16.                 //Try to start all subsystems and check if they were all successfully started ( thus HMD prepared).
    17.                 bool loaderSuccess = XRGeneralSettings.Instance.Manager.activeLoader.Start();              
    18.                 if(loaderSuccess)
    19.                 {
    20.                     Debug.Log("All Subsystems Started!");
    21.                 }
    22.                 else
    23.                 {
    24.                     Debug.LogError("Starting Subsystems Failed. Directing to Normal Interaciton Mode...!");
    25.                     StopXR();
    26.                     DirectToNormal();
    27.                 }
    28.             }
    29.         }
    30.  
    31.         void StopXR()
    32.         {
    33.             ...
    34.             Debug.Log("XR stopped completely.");
    35.         }
    36.         void DirectToNormal()
    37.         {
    38.             ...
    39.             Debug.Log("Fell back to Mouse & Keyboard Interaciton!");
    40.         }
    Just use XRLoader.Start() to detect if the Hmd is prepared, as this method returns a bool indicating Whether or not all subsystems were successfully started.

    It works for me, in scenario:
    Unity 2019.4.11
    XR Plugin Management 3.2.16 with
    Oculus XR Plugin 1.4.3 and
    OpenVR XR Plugin 1.0.1 enabled.
     
  12. Banksy

    Banksy

    Joined:
    Mar 31, 2013
    Posts:
    376
    umm for XR check is active or not I use

    InputBridge.Instance.HMDActive
     
    Last edited: Apr 20, 2021
  13. creat327

    creat327

    Joined:
    Mar 19, 2009
    Posts:
    1,756
    What's inputbridge? there is no such thing in unity
     
    Erethan likes this.
  14. creat327

    creat327

    Joined:
    Mar 19, 2009
    Posts:
    1,756
    I'm having a similar problem now that we are moving to OpenXR. Since we don't install OpenVR at all, we can't find a solution. All I'm trying to do is to detect if my device is ready and then decide if I need to setup the game for VR or for regular view. Anyone got it working with OpenXR and without OpenVR?
     
  15. Cloudwalker_

    Cloudwalker_

    Joined:
    Jan 3, 2014
    Posts:
    140
    Don't think there is an easy way to do this, you probably need to try starting the XRPlugin, if it succeeds + headset check, then you know. Otherwise no HMD and stop the XRPlugin if it did load successfully.
     
  16. creat327

    creat327

    Joined:
    Mar 19, 2009
    Posts:
    1,756
    nope, doesn't work
     
  17. Sab_Rango

    Sab_Rango

    Joined:
    Aug 30, 2019
    Posts:
    121
    for new OpenXR package to check the XR devices
    link https://docs.unity3d.com/Manual/xr_input.html#AccessingInputDevices

    Code (CSharp):
    1. var inputDevices = new List<UnityEngine.XR.InputDevice>();
    2. UnityEngine.XR.InputDevices.GetDevices(inputDevices);
    3.  
    4. foreach (var device in inputDevices)
    5. {
    6.     Debug.Log(string.Format("Device found with name '{0}' and role '{1}'", device.name, device.role.ToString()));
    7. }
    8.  
     
  18. Riptide559

    Riptide559

    Joined:
    Feb 1, 2020
    Posts:
    9
    I used this solution for a while, but it doesn't seem to work with the latest OpenXR Plugin (1.3.1)
     
  19. Domcoc

    Domcoc

    Joined:
    Sep 1, 2017
    Posts:
    11
    BUMP
    Tryed all the solutions mentioned here, no one seems to work. Please, we need this feature as soon as possible!
     
  20. ePhaedrus

    ePhaedrus

    Joined:
    Dec 8, 2021
    Posts:
    1
    I've had success with the following code for detecting whether an XR device was successfully loaded:

    Code (CSharp):
    1. using UnityEngine.XR.Management;
    2. // ...
    3. if (XRGeneralSettings.Instance.Manager.activeLoader != null) {
    4.     // XR device detected/loaded
    5. }
    This assumes you've used the "XR Plug-in Management" tool in Project Settings to initialize XR on startup, as described in the OP. In my experience this value can reliably be checked in a MonoBehaviour's Awake(), though YMMV. I use this check to switch inputs etc. to custom desktop controls, so I can have a single build that works on both VR and desktop.

    If you're trying to actually manually initialize XR instead of using the standard init from the XR Plug-in Management UI, ErikWelling's post seems like a good answer.
     
    Daddis likes this.
  21. Domcoc

    Domcoc

    Joined:
    Sep 1, 2017
    Posts:
    11
    That seems to work. Thanks!!
     
  22. laikyfairres

    laikyfairres

    Joined:
    Aug 8, 2020
    Posts:
    3
    UnityEngine.XR.InputDevices.GetDeviceAtXRNode(UnityEngine.XR.XRNode.Head).isValid; works for me
     
    KrabbyQ and zhouzhuan88 like this.
  23. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    7,519
    To answer this with a modern update, it is still an issue. It's not possible to soft-check if XR will work when initialized, you can only try and fail - hoping it doesnt hard-lock. There doesn't seem to be a safe way to constantly check if the hardware is there or not since you have to initialize XR and risk a lockup to be able to see any potential hardware anyway.

    Here's some code that attempts to safely initialize it. The docs show basically the same pattern but I don't see a difference between sync and async approaches (they can both hang the app as if the main thread is locked up) so something is not working correctly in the library.

    If someone can get better feedback or something safer I'd love to see it.

    Code (csharp):
    1.         public virtual void SetVrMode(bool goToVrMode)
    2.         {
    3.             StartCoroutine(SetVrLazy(goToVrMode));
    4.         }
    5.  
    6.         private IEnumerator SetVrLazy(bool goToVrMode)
    7.         {
    8.             if (goToVrMode)
    9.             {
    10.                 yield return XRGeneralSettings.Instance.Manager.InitializeLoader();
    11.  
    12.                 // optionally run it synchronously, but it doesn't seem to change the thread lock effect.
    13.                 // XRGeneralSettings.Instance.Manager.InitializeLoaderSync();
    14.            
    15.                 float loadingTime = 0;
    16.                 while (XRGeneralSettings.Instance.Manager.activeLoader == null && loadingTime < 10)
    17.                 {
    18.                     loadingTime += Time.deltaTime;
    19.                     yield return null;
    20.                     Debug.Log("<color=orange>XR is trying to load. Unlikely to recover, but we'll wait 10 seconds...</color>");
    21.                 }
    22.  
    23.                 if (XRGeneralSettings.Instance.Manager.activeLoader == null)
    24.                 {
    25.                     Debug.Log("<color=red>XR load definitely failed.</color>");
    26.                     yield break;
    27.                 }
    28.  
    29.                 XRGeneralSettings.Instance.Manager.StartSubsystems();
    30.             }
    31.             else
    32.             {
    33.                 XRGeneralSettings.Instance.Manager.DeinitializeLoader();
    34.                 XRGeneralSettings.Instance.Manager.StopSubsystems();
    35.             }
    36.  
    37.             // do your vr init or whatever here.
    38.             Debug.Log($"<color=orange>XR Mode is now set to {(goToVrMode ? "ON" : "OFF")}.</color>");
    39.         }
    40.  
     
  24. KrabbyQ

    KrabbyQ

    Joined:
    Dec 19, 2016
    Posts:
    5
    This works for me