SteamVR Overlay with Unity: Initialize OpenVR
Tags: unity3d, steamvr, vr, openvr
Published: 2024-06-16 10:57:44

## Create new script
Create a folder named Scripts inside Assets. Then create a file named **WatchOverlay.cs** inside the Scripts folder. We will add our overlay code to this script.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/toltg8o29v2c4qj8sw0z.png)

Copy the following code to **WatchOverlay.cs**.

```cs
using UnityEngine;

public class WatchOverlay : MonoBehaviour
{
    private void Start()
    {
    }
}
```

## Put the script to the scene
In the hierarchy window, **right click > Create Empty**.
Change the object name to **WatchOverlay**, then drag the **WatchOverlay.cs** file from the project window to the newly created object.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mk5ziw5ou0p3h6b51czi.png)

## Add using directive
Add using Valve.VR to **WatchOverlay.cs** to use the OpenVR API. This namespace is included in the SteamVR Plugin.

```diff
using UnityEngine;
+ using Valve.VR;

public class WatchOverlay : MonoBehaviour
{
   private void Start()
   {
   }
}
```

## Initialize OpenVR
Initialize OpenVR with [Init()](https://valvesoftware.github.io/steamvr_unity_plugin/api/Valve.VR.OpenVR.html#Valve_VR_OpenVR_Init_Valve_VR_EVRInitError__Valve_VR_EVRApplicationType_System_String_) to use the API. (read the [wiki](https://github.com/ValveSoftware/openvr/wiki/API-Documentation#initialization-and-cleanup) for details)

```diff
using UnityEngine;
using Valve.VR;

public class WatchOverlay : MonoBehaviour
{
    private void Start()
    {
+       var error = EVRInitError.None;
+       OpenVR.Init(ref error, EVRApplicationType.VRApplication_Overlay);
    }
}
```

We pass two arguments to `Init()`.

The first argument is a reference. If an error occurs during initialization, `Init()` sets the variable's value referenced to the error. All errors are defined in [EVRInitError](https://valvesoftware.github.io/steamvr_unity_plugin/api/Valve.VR.EVRInitError.html).

The second argument is the application type, which will be `EVRApplicationType.VRApplication_Overlay` for overlay applications. All application types are defined in [EVRApplicationType](https://valvesoftware.github.io/steamvr_unity_plugin/api/Valve.VR.EVRApplicationType.html).

If the application has been initialized as an overlay, it can be run with other VR applications at the same time.

## Initialize error handling
Add error handling.

```diff
using UnityEngine;
using Valve.VR;
+ using System;

public class WatchOverlay : MonoBehaviour
{
    private void Start()
    {
        var error = EVRInitError.None;
        OpenVR.Init(ref error, EVRApplicationType.VRApplication_Overlay);
+       if (error != EVRInitError.None)
+       {
+           throw new Exception("Failed to initialize OpenVR: " + error);
+       }
    }
}
```

If the initialization succeeds, the error value will remain `EVRInitError.None`. We will throw an `Exception` if this is not the case. Note that you need to add `using System` to use `Exception`.

## If OpenVR is already initialized
If OpenVR has already been initialized, skip the initialization.

```diff
using UnityEngine;
using Valve.VR;
using System;

public class WatchOverlay : MonoBehaviour
{
    private void Start()
    {
+       if (OpenVR.System != null) return;

        var error = EVRInitError.None;
        OpenVR.Init(ref error, EVRApplicationType.VRApplication_Overlay);
        if (error != EVRInitError.None)
        {
            throw new Exception("Failed to initialize OpenVR: " + error);
        }
    }
}
```

If OpenVR has not been initialized, `OpenVR.System` is `null`.
The API is broken down into parts such as the following. They are `null` before initialization.

- OpenVR.System
- OpenVR.Chaperone
- OpenVR.Compositor
- OpenVR.Overlay
- OpenVR.RenderModels
- OpenVR.Screenshots
- OpenVR.Input

## Cleanup
Add cleanup to dispose of the initialized OpenVR.
Call [Shutdown()](https://valvesoftware.github.io/steamvr_unity_plugin/api/Valve.VR.OpenVR.html#Valve_VR_OpenVR_Shutdown) at the end of the program. (read the [wiki](https://github.com/ValveSoftware/openvr/wiki/API-Documentation#initialization-and-cleanup) for details)

```diff
using UnityEngine;
using Valve.VR;
using System;

public class WatchOverlay : MonoBehaviour
{
    private void Start()
    {
        if (OpenVR.System != null) return;
       
        var error = EVRInitError.None;
        OpenVR.Init(ref error, EVRApplicationType.VRApplication_Overlay);
        if (error != EVRInitError.None)
        {
            throw new Exception("Failed to initialize OpenVR: " + error);
        }
    }

+   private void OnDestroy()
+   {
+       if (OpenVR.System != null)
+       {
+           OpenVR.Shutdown();
+       }
+   }
}
```

## Check the initialization works
Let's access the initialized OpenVR API.

```diff
using UnityEngine;
using Valve.VR;
using System;

public class WatchOverlay : MonoBehaviour
{
    private void Start()
    {
        if (OpenVR.System != null) return;

+       Debug.Log(OpenVR.Overlay);

        var error = EVRInitError.None;
        OpenVR.Init(ref error, EVRApplicationType.VRApplication_Overlay);
        if (error != EVRInitError.None)
        {
            throw new Exception("Failed to initialize OpenVR: " + error);
            return;
        }

+       Debug.Log(OpenVR.Overlay);
    }

    private void OnDestroy()
    {
        if (OpenVR.System != null)
        {
            OpenVR.Shutdown();
        }
    }
}
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gqrm248xukf63nyu6pbm.png)

We can see that the API is available after initialization.
If the SteamVR has not launched, it should be automatically launched by `Init()`.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k2d4sgyprgdvhq0703xb.png)

---

### Optional: Initialize OpenVR by SteamVR Plugin setting
We don’t use it in this tutorial, but the SteamVR Unity Plugin has an automatic initialization setting.
Check **Initialize XR on Project Settings > Startup In XR Plug-in Management**.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rwi9ns39wlipixs82269.png)

Select Overlay as the **Application Typ**e in the **OpenVR** setting.

---

Delete the `Debug.Log()`.

```diff
private void Start()
{
    if (OpenVR.System != null) return;

-   Debug.Log(OpenVR.Overlay);
    var error = EVRInitError.None;
    OpenVR.Init(ref error, EVRApplicationType.VRApplication_Overlay);
    if (error != EVRInitError.None)
    {
        throw new Exception("Failed to initialize OpenVR: " + error);
        return;
    }
-   Debug.Log(OpenVR.Overlay);
}
```

## Organize code
We have initialized and cleaned up OpenVR. Let’s organize the code here.
This tutorial will organize code periodically to keep methods small for clarity.

### Initialize
Move the initialization code into `InitOpenVR()`.

```diff
using UnityEngine;
using Valve.VR;
using System;

public class WatchOverlay : MonoBehaviour
{
    private void Start()
    {
+       InitOpenVR();
-       if (OpenVR.System != null) return;
-
-       var error = EVRInitError.None;
-       OpenVR.Init(ref error, EVRApplicationType.VRApplication_Overlay);
-       if (error != EVRInitError.None)
-       {
-           throw new Exception("Failed to initialize OpenVR: " + error);
-       }
    }

    private void OnDestroy()
    {
        if (OpenVR.System != null)
        {
            OpenVR.Shutdown();
        }
    }

+   private void InitOpenVR()
+   {
+       if (OpenVR.System != null) return;
+
+       var error = EVRInitError.None;
+       OpenVR.Init(ref error, EVRApplicationType.VRApplication_Overlay);
+       if (error != EVRInitError.None)
+       {
+           throw new Exception("Failed to initialize OpenVR: " + error);
+       }
+   }
}
```

---

### Optional: If you want to use `return` instead of `throw`

This tutorial uses `throw` for readability of error handling. If you prefer `return` to return an error code or a bool value, replace the error handling code with the following.

```cs
// Return error code
private bool InitOpenVR()
{
    if (OpenVR.System != null) return;

    var error = EVRInitError.None;
    OpenVR.Init(ref error, EVRApplicationType.VRApplication_Overlay);
    if (error != EVRInitError.None)
    {
        Debug.LogError("Failed to initialize OpenVR: " + error);
    }
    return error;
}
```

```cs
// Return bool value
private bool InitOpenVR()
{
    if (OpenVR.System != null) return;

    var error = EVRInitError.None;
    OpenVR.Init(ref error, EVRApplicationType.VRApplication_Overlay);
    if (error != EVRInitError.None)
    {
        Debug.LogError("Failed to initialize OpenVR: " + error);
        return false;
    }
    return true;
}
```

---

### Cleanup
Similarly, move the cleanup code to `ShutdownOpenVR()`.

```diff
using UnityEngine;
using Valve.VR;
using System;

public class WatchOverlay : MonoBehaviour
{
    private void Start()
    {
       InitOpenVR();
    }

    private void OnDestroy()
    {
-       if (OpenVR.System != null)
-       {
-           OpenVR.Shutdown();
-       }
+       ShutdownOpenVR();
    }

    private void InitOpenVR()
    {
        if (OpenVR.System != null) return;

        var error = EVRInitError.None;
        OpenVR.Init(ref error, EVRApplicationType.VRApplication_Overlay);
        if (error != EVRInitError.None)
        {
            throw new Exception("Failed to initialize OpenVR: " + error);
        }
    }

+   private void ShutdownOpenVR()
+   {
+       if (OpenVR.System != null)
+       {
+           OpenVR.Shutdown();
+       }
+   }
}
```

## Final code
```cs
using UnityEngine;
using Valve.VR;
using System;

public class WatchOverlay : MonoBehaviour
{
    private void Start()
    {
       InitOpenVR();
    }

    private void OnDestroy()
    {
       ShutdownOpenVR();
    }

    private void InitOpenVR()
    {
        if (OpenVR.System != null) return;

        var error = EVRInitError.None;
        OpenVR.Init(ref error, EVRApplicationType.VRApplication_Overlay);
        if (error != EVRInitError.None)
        {
            throw new Exception("Failed to initialize OpenVR: " + error);
        }
    }

   private void ShutdownOpenVR()
   {
       if (OpenVR.System != null)
       {
           OpenVR.Shutdown();
       }
   }
}
```

Here, we have initialized and cleaned up OpenVR. Now we can access OpenVR APIs like overlay. In the next part, we will create an overlay with this API.
```