7
\$\begingroup\$

The Oculus and the Vive have this light proximity sensor that detects whether you have your VR headset placed on your head currently or not.

Is there any way to tap into that for our own game checks? E.g. We could pause or restart the game depending on whether the headset's being worn or not.

I'm using Unity/C#, but any (driver / API level) help is much appreciated.

\$\endgroup\$
1

10 Answers 10

7
\$\begingroup\$

Yes, OpenVR has events for this:

VREvent_TrackedDeviceUserInteractionStarted fires when the devices transitions from Standby -> UserInteraction or Idle -> UserInteraction. VREvent_TrackedDeviceUserInteractionEnded fires when the devices transitions from UserInteraction_Timeout -> Idle

Additionally, you can poll the status of the headset with GetTrackedDeviceActivityLevel from Valve.VR.IVRSystem. Which will return one of the following values:

public enum EDeviceActivityLevel
{
    k_EDeviceActivityLevel_Unknown = -1,
    k_EDeviceActivityLevel_Idle = 0,
    k_EDeviceActivityLevel_UserInteraction = 1,
    k_EDeviceActivityLevel_UserInteraction_Timeout = 2,
    k_EDeviceActivityLevel_Standby = 3,
}

There's also some level of this introduced into the Unity scripts (Scripts/Core/VRView.cs:GetIsUserPresent()

\$\endgroup\$
4
  • \$\begingroup\$ Beautiful. In the meantime I also found the Oculus OVRManager.HMDMounted event in case anyone needs that. \$\endgroup\$ Commented May 23, 2017 at 8:08
  • \$\begingroup\$ In Unity using the OpenVR plugin, the function to check is: OpenVR.System.GetTrackedDeviceActivityLevel(0) However, i noticed it doesn't check when the headset is on someone's head, it just tells you when the headset has moved. 10s of no motion and it goes back to standby. \$\endgroup\$
    – niltoid
    Commented Apr 18, 2018 at 18:55
  • \$\begingroup\$ Likely depends on the device and how they've implemented their software to work with OpenVR. \$\endgroup\$
    – House
    Commented Apr 18, 2018 at 19:08
  • \$\begingroup\$ Makes sense, in my case it's the HTC Vive \$\endgroup\$
    – niltoid
    Commented Apr 18, 2018 at 19:40
2
\$\begingroup\$

Chiming in for checking a mounted / proximity sensor event for the Oculus OVR unity plugin. This is in OVRManager:

/// <summary>
/// Occurs when an HMD is put on the user's head.
/// </summary>
public static event Action HMDMounted;

/// <summary>
/// Occurs when an HMD is taken off the user's head.
/// </summary>
public static event Action HMDUnmounted;

Which is present in the source but for some reason does not come up by searching the oculus docs.

[EDIT]

Also, if you don't want Oculus to pause the game to a black screen when you take the headset off:

  • If you're using version 1.3 (2016) or below, of the Oculus SDK, then set runInBackground = true in OVRManager, which is independent from unity's own runInBackground setting.

  • In newer versions use Unity's Application.runInBackground = true and Camera.stereoTargetEye = StereoTargetEyeMask.None

[EDIT2]

The OVRManager events didn't work for me at all. What does work is using the OVRPlugin class and doing if (!OVRPlugin.userPresent && m_wasUserPresent) yourself.

\$\endgroup\$
2
\$\begingroup\$

In Unity, using the OpenVR unity plugin, the following works:

print(SteamVR_Controller.Input(0).GetPress(EVRButtonId.k_EButton_ProximitySensor));

BTW, in a response to the accepted answer i proposed: OpenVR.System.GetTrackedDeviceActivityLevel(0), but that's more for detecting recent motion within 10 seconds.

\$\endgroup\$
2
2
\$\begingroup\$

You can now also use UnityEngine.XR and XRDevice.userPresence without the need for additional API's : https://docs.unity3d.com/ScriptReference/XR.XRDevice-userPresence.html

For example :

if(trackingPresence == true)
    {
        if(XRDevice.userPresence == UserPresenceState.Present && playerPresent == false)
        {
            playerPresent = true;
            Debug.Log("[PLAYER PRESENCE DETECTOR] player is now present");
        }

        if(XRDevice.userPresence == UserPresenceState.NotPresent && playerPresent == true)
        {
            playerPresent = false;
            Debug.Log("[PLAYER PRESENCE DETECTOR] player is now absent");
        }
    }

Edit : for latest versions of Unity, you can use XRDisplaySubsystem.running : https://docs.unity3d.com/2020.3/Documentation/ScriptReference/XR.XRDisplaySubsystem.html

\$\endgroup\$
1
  • \$\begingroup\$ Nice. Finally. Heads up that it's high level API and not explicitly the proximity: "The exact behavior of this property will vary with each type of device -- some devices will have a sensor specifically to detect user proximity, and users will rely on movement" \$\endgroup\$ Commented Jun 17, 2019 at 9:02
1
\$\begingroup\$

In Android, there is the Proximity Sensor. This is used to disable the screen when you're calling someone. I'm not sure if you have access to the proximity sensor in Unity, but you can call the native method through Unity.

\$\endgroup\$
1
\$\begingroup\$

If anyone wants to force the Oculus VR application to not pause the game while the headset is turned off, you need to do some dll stuff. [source]

Here's how, and I have also included code for the SteamVR / OpenVR settings:

    using System.Runtime.InteropServices;
    [DllImport("OVRPlugin", CallingConvention = CallingConvention.Cdecl)]
    public static extern bool ovrp_SetAppIgnoreVrFocus(bool value);
    [DllImport("OVRPlugin", CallingConvention = CallingConvention.Cdecl)]
    public static extern uint ovrp_GetStatus2(uint query);

    void Start () {
        Application.runInBackground = true; 
        SteamVR_Render.instance.pauseGameWhenDashboardIsVisible = false;
        ovrp_SetAppIgnoreVrFocus(true);
        //ovrp_GetStatus2(uint query);
        //m_vrCam.stereoTargetEye = StereoTargetEyeMask.None;
    }
\$\endgroup\$
1
\$\begingroup\$

For OVR ovr_GetSessionStatus() tells you this, OVRSessionStatus has a field HmdMounted.

\$\endgroup\$
1
\$\begingroup\$

The SteamVR input has recently changed completely, it now uses Actions and here is a step by step tutorial from valve on how to set it up.

Here's one of the (best) ways to poll it in code:

    void Update(){


    //NOTE: I could have used one-off events. But I used these constant Update() polls instead because if the VR Dashboard is on while the headset is taken off or put on, 
    //then the OnDown and OnUp events get eaten by the dashboard and will never reach this code in unity. (github issue here: https://github.com/ValveSoftware/openvr/issues/628)

    // m_inputSourceDevice needs to be Any - there's no entry for "Headset", but the headset's sensors are reported under the Any "hand".
    SteamVR_Input_Sources m_inputSourceDevice = SteamVR_Input_Sources.Any;

    if(!m_isHeadsetOn && SteamVR_Input._default.inActions.Proximity_Sensor.GetState(m_inputSourceDevice)){
            Debug.Log("Headset On.");
            HeadsetOn();
    }
    else if(m_isHeadsetOn && !SteamVR_Input._default.inActions.Proximity_Sensor.GetState(m_inputSourceDevice)){
        Debug.Log("Headset Off (cancellable)");
        StartCoroutine(HeadsetOff());
    }

}

Note that as of right now, there is a bug that prevents you from using SteamVR to assign the Proximity Sensor to an action if you have a HTC Vive or Vive Pro, even though ironically it works as intended if you're using it with an Oculus headset. There is an easy fix on this steamvr github issues page.

Also note that even though the builtin unity OpenVR usually implements what SteamVR comes up with, for now it does not let you access the proximity sensor. The only thing you can do with OpenVR is to check the EDeviceActivityLevel as MichaelHouse explained in his answer. However that is useless because the only level that involves the prox sensor is the k_EDeviceActivityLevel_UserInteraction but it's a union of both the prox and the gyro, so according to OpenVR the headset is always interacting with the user forever as long as a gentle breeze or vibration tilts the headset a little bit once every 10 seconds.

[EDIT]

Some things got fixed in the meantime. For instance now there is an entry for the Hmd, instead of Any; and OpenVR does have an XRDevice.userPresence value (though it might not explicitly be the proximity sensor).

\$\endgroup\$
1
  • \$\begingroup\$ Thanks for creating a new answer with the latest info! \$\endgroup\$
    – House
    Commented Oct 24, 2018 at 19:44
0
\$\begingroup\$

Add this code to new gameobject in the scene and it will check and print the user is occupying the headset or not. Based on that you can do your changes.

 public class HeadsetDetection : MonoBehaviour
        {       
        public SteamVR_Action_Boolean headsetOnHead = SteamVR_Input.GetBooleanAction("HeadsetOnHead");
                public bool IsHeadsetOnHead;
                public Action OnHeadsetOnHead, OnHeadsetOffHead;
            void Update()
                {
             if (headsetOnHead != null)
                    {
                        if (headsetOnHead.GetStateDown(SteamVR_Input_Sources.Head))
                        {
                            Debug.Log("Headset placed on head");
            
                            IsHeadsetOnHead = true;
            
                            if (OnHeadsetOnHead != null)
                            {
                                OnHeadsetOnHead();
            
                            }
                        }
                        else if (headsetOnHead.GetStateUp(SteamVR_Input_Sources.Head))
                        {
                            Debug.Log("Headset removed");
            
                            IsHeadsetOnHead = false;
                            if (OnHeadsetOffHead != null)
                            {
                                OnHeadsetOffHead();
                            }
                        }
                    }
            }
    }
\$\endgroup\$
2
  • 1
    \$\begingroup\$ While good code can be self-explanatory, you could improve your current answer by expanding on how your solution works. \$\endgroup\$
    – liggiorgio
    Commented Aug 5, 2022 at 7:33
  • \$\begingroup\$ Especially given that this question already has several existing answers, some text can help explain why or in what situations a reader should use (and upvote) this solution over the others posted here. \$\endgroup\$
    – DMGregory
    Commented Aug 5, 2022 at 11:42
0
\$\begingroup\$

i ran into the same issue of pausing the game when user takes the headset off, and resumes when puts back on. after trying alot of options, i found the best thing to do is to put your code where oculus is detecting that action. for my case i added it in OVR Manager script's update function.

enter image description here

\$\endgroup\$
1
  • 3
    \$\begingroup\$ Can you please post the code as text instead of as an image? \$\endgroup\$
    – Philipp
    Commented Oct 17, 2023 at 9:43

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .