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

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.

    public static bool isHardwarePresent()
    {
        var xrDisplaySubsystems = new List<XRDisplaySubsystem>();
        SubsystemManager.GetInstances<XRDisplaySubsystem>(xrDisplaySubsystems);
        foreach (var xrDisplay in xrDisplaySubsystems)
        {
            if (xrDisplay.running)
            {
                return true;
            }
        }
        return false;
    }

I also check InputDevices and got similar results. The list is empty unless XR is initialized.

        List<InputDevice> deviceList = new List<InputDevice>();
        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

    public IEnumerator StartXR()
    {
        Debug.Log("Initializing XR...");
        yield return XRGeneralSettings.Instance.Manager.InitializeLoader();

        if (XRGeneralSettings.Instance.Manager.activeLoader == null)
        {
            Debug.LogError("Initializing XR Failed. Check Editor or Player log for details.");
        }
        else
        {
            Debug.Log("Starting XR...");
            XRGeneralSettings.Instance.Manager.StartSubsystems();
        }
    }

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

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

List<XRDisplaySubsystem> displaySubsystems = new List<XRDisplaySubsystem>();

SubsystemManager.GetInstances<XRDisplaySubsystem>(displaySubsystems);
            foreach (var subsystem in displaySubsystems)
            {
             ...
            }

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.

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.

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.

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.

public class HMDChecker : MonoBehaviour
{
    public XRLoader OpenVRLoader;
    public XRLoader MockVRLoader;

    void Start()
    {
        XRGeneralSettings.Instance.Manager.loaders.Clear();

        if ( OpenVR.IsHmdPresent() )
        {
            XRGeneralSettings.Instance.Manager.loaders.Add(OpenVRLoader);
        }
        else
        {
            XRGeneralSettings.Instance.Manager.loaders.Add(MockVRLoader);
        }

        XRGeneralSettings.Instance.Manager.InitializeLoaderSync();
        XRGeneralSettings.Instance.Manager.StartSubsystems();
    }
}

I’m still curious if there is a way to do this using native XR stuff.

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?

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 :cry:

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.

I might have found a solution that seems to be working (tested on 2019.4.5f1):

    public XRLoader WMRLoader;
    public XRLoader MockLoader;

    void Awake() {
        XRGeneralSettings.Instance.Manager.loaders.Clear();

        //Initialize WMR.
        XRGeneralSettings.Instance.Manager.loaders.Add(WMRLoader);
        XRGeneralSettings.Instance.Manager.InitializeLoaderSync();
        XRGeneralSettings.Instance.Manager.StartSubsystems();

        //Check if initialization was successfull.
        var xrDisplaySubsystems = new List<XRDisplaySubsystem>();
        SubsystemManager.GetInstances(xrDisplaySubsystems);
        bool success = xrDisplaySubsystems[0].running;

        if (!success) {
            //Initialization was not successfull, load mock instead.
            print("loading mock");

            //Deinitialize WMR
            XRGeneralSettings.Instance.Manager.loaders.Clear();
            XRGeneralSettings.Instance.Manager.StopSubsystems();
            XRGeneralSettings.Instance.Manager.DeinitializeLoader();

            //Initialize mock.
            XRGeneralSettings.Instance.Manager.loaders.Add(MockLoader);
            XRGeneralSettings.Instance.Manager.InitializeLoaderSync();
            XRGeneralSettings.Instance.Manager.StartSubsystems();
        }
    }

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.

4 Likes

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.

Try this:

        public IEnumerator StartXR()
        {
            Debug.Log("Initializing XR...");
            yield return XRGeneralSettings.Instance.Manager.InitializeLoader();

            if (XRGeneralSettings.Instance.Manager.activeLoader == null)
            {
                Debug.LogError("Initializing XR Failed. Directing to Normal Interaciton Mode...!.");
                StopXR();
                DirectToNormal();
            }
            else
            {
                Debug.Log("Initialization Finished.Starting XR Subsystems...");

                //Try to start all subsystems and check if they were all successfully started ( thus HMD prepared).
                bool loaderSuccess = XRGeneralSettings.Instance.Manager.activeLoader.Start();               
                if(loaderSuccess)
                {
                    Debug.Log("All Subsystems Started!");
                }
                else
                {
                    Debug.LogError("Starting Subsystems Failed. Directing to Normal Interaciton Mode...!");
                    StopXR();
                    DirectToNormal();
                }
            }
        }

        void StopXR()
        {
            ...
            Debug.Log("XR stopped completely.");
        }
        void DirectToNormal()
        {
            ...
            Debug.Log("Fell back to Mouse & Keyboard Interaciton!");
        }

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.

umm for XR check is active or not I use

InputBridge.Instance.HMDActive

What’s inputbridge? there is no such thing in unity

1 Like

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?

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.

nope, doesn’t work

for new OpenXR package to check the XR devices
link Unity - Manual: Unity XR Input

var inputDevices = new List<UnityEngine.XR.InputDevice>();
UnityEngine.XR.InputDevices.GetDevices(inputDevices);

foreach (var device in inputDevices)
{
    Debug.Log(string.Format("Device found with name '{0}' and role '{1}'", device.name, device.role.ToString()));
}
5 Likes

I used this solution for a while, but it doesn’t seem to work with the latest OpenXR Plugin (1.3.1)

BUMP
Tryed all the solutions mentioned here, no one seems to work. Please, we need this feature as soon as possible!

I’ve had success with the following code for detecting whether an XR device was successfully loaded:

using UnityEngine.XR.Management;
// ...
if (XRGeneralSettings.Instance.Manager.activeLoader != null) {
    // XR device detected/loaded
}

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.

1 Like