DEV Community

kurohuku
kurohuku

Posted on • Edited on

SteamVR Overlay with Unity: Create Overlay

Key and Name

Overlay needs two strings key and name.
key is a unique string among all overlays.
name is just an overlay name sometimes shown to users.
We use "WatchOverlayKey" and "WatchOverlay".

void Start()
{
    InitOpenVR();

+   var key = "WatchOverlayKey";
+   var name = "WatchOverlay";
}
Enter fullscreen mode Exit fullscreen mode

Overlay handle

Create a variable to save an overlay handle.
Similar to controlling a file with a file handle, overlays are controlled with an overlay handle.

void Start()
{
    InitOpenVR();

    var key = "WatchOverlayKey";
    var name = "WatchOverlay";
+   var overlayHandle = OpenVR.k_ulOverlayHandleInvalid;
}
Enter fullscreen mode Exit fullscreen mode

OpenVR.k_ulOverlayHandleInvalid means the overlay is not created. Overlay handle is ulong type.

Create an overlay and get a handle

Use CreateOverlay() to create an overlay. (read the wiki for details)
Pass the key and name and reference of overlayHandle to CreateOverlay().

void Start()
{
    InitOpenVR();

    var key = "WatchOverlayKey";
    var name = "WatchOverlay";
    var overlayHandle = OpenVR.k_ulOverlayHandleInvalid;
+   var error = OpenVR.Overlay.CreateOverlay(key, name, ref overlayHandle);
}
Enter fullscreen mode Exit fullscreen mode

CreateOverlay() sets overlay handle to the variable overlayHandle.
The return value is an overlay creation error. All overlay errors are defined in EVROverlayError.

Error handling

Add error handling for overlay creation.

void Start()
{
    InitOpenVR();

    var key = "WatchOverlayKey";
    var name = "WatchOverlay";
    var overlayHandle = OpenVR.k_ulOverlayHandleInvalid;
    var error = OpenVR.Overlay.CreateOverlay(key, name, ref overlayHandle);
+   if (error != EVROverlayError.None)
+   {
+       throw new Exception("Failed to create overlay: " + error);
+   }
}
Enter fullscreen mode Exit fullscreen mode

If there is no error, EVROverlayError.None is back.

Cleanup overlay

Add code to dispose of the overlay at the end of the application.

Move overlay handle

Move overlayHandle from Start() to the class member variable.

public class WatchOverlay : MonoBehaviour
{
+   private ulong overlayHandle = OpenVR.k_ulOverlayHandleInvalid;

    private void Start()
    {
        InitOpenVR();

        var key = "WatchOverlayKey";
        var name = "WatchOverlay";
-       var overlayHandle = OpenVR.k_ulOverlayHandleInvalid;
        var error = OpenVR.Overlay.CreateOverlay(key, name, ref overlayHandle);
        if (error != EVROverlayError.None)
        {
            throw new Exception("Failed to create overlay: " + error);
        }
    }

    ...
}
Enter fullscreen mode Exit fullscreen mode

Dispose of overlay

The function for overlay cleanup is DestroyOverlay(). (read the wiki for details)
Add OnApplicationQut() with overlay cleanup code.

public class WatchOverlay : MonoBehaviour
{
    private ulong overlayHandle = OpenVR.k_ulOverlayHandleInvalid;

    private void Start()
    {
        InitOpenVR();

        var key = "WatchOverlayKey";
        var name = "WatchOverlay";
        var error = OpenVR.Overlay.CreateOverlay(key, name, ref overlayHandle);
        if (error != EVROverlayError.None)
        {
            throw new Exception("Failed to create overlay: " + error);
        }
    }

+   private void OnApplicationQuit()
+   {
+       if (overlayHandle != OpenVR.k_ulOverlayHandleInvalid)
+       {
+           var error = OpenVR.Overlay.DestroyOverlay(overlayHandle);
+           if (error != EVROverlayError.None)
+           {
+               throw new Exception("Failed to dispose overlay: " + error);
+           }
+       }
+   }

    private void OnDestroy()
    {
        ShutdownOpenVR();
    }

    ...
}
Enter fullscreen mode Exit fullscreen mode

DestroyOverlay() must be called before Shutdown() so we put the overlay cleanup code inside OnApplicationQuit() that is called before OnDestroy().

Check created overlay

Check whether the overlay is created.
Overlay Viewer included in the SteamVR is useful to check overlays.

Select Developer > Overlay Viewer in the SteamVR menu.

Image description

Image description

Overlay Viewer displays a list of created overlays. You can see many overlays already created by SteamVR system.

Image description

In this state, run the program from Unity.

Then the overlay key WatchOverlayKey is added to the list. You can see the overlay details by clicking the key.

Image description

The overlay preview is shown on the right side gray area but now we draw nothing to the overlay yet.

Here, we confirmed the overlay is created. Stop the program and close Overlay Viewer.


Optional: Overlay Viewer location

Overlay Viewer is located in the SteamVR install directory.
C:/ProgramFiles(x86)/Steam/steamapps/common/SteamVR/bin/win32/overlay_viewer.exe
We use this tool frequently during development so I recommend creating short cut.


Organize code

Create overlay

Move the overlay creation code intoCreateOverlay(). It gets key and name as arguments, and return created overlay handle. Please note that some variable names are changed in code organization.

public class WatchOverlay : MonoBehaviour
{
    private ulong overlayHandle = OpenVR.k_ulOverlayHandleInvalid;

    private void Start()
    {
        InitOpenVR();

-       var key = "WatchOverlayKey";
-       var name = "WatchOverlay";
-       var error = OpenVR.Overlay.CreateOverlay(key, name, ref overlayHandle);
-       if (error != EVROverlayError.None)
-       {
-           throw new Exception("Failed to create overlay: " + error);
-       }
+       overlayHandle = CreateOverlay("WatchOverlayKey", "WatchOverlay");
    }

    ...

+   private ulong CreateOverlay(string key, string name) {
+       // Some code changed here.
+       var handle = OpenVR.k_ulOverlayHandleInvalid;
+       var error = OpenVR.Overlay.CreateOverlay(key, name, ref handle);
+       if (error != EVROverlayError.None)
+       {
+           throw new Exception("Failed to create overlay: " + error);
+       }
+       return handle;
+   }
}
Enter fullscreen mode Exit fullscreen mode

Cleanup overlay

Similarly, move the overlay cleanup code into DestroyOverlay().

public class WatchOverlay : MonoBehaviour
{
    ...

    private void OnApplicationQuit()
    {
-       if (overlayHandle != OpenVR.k_ulOverlayHandleInvalid)
-       {
-           var error = OpenVR.Overlay.DestroyOverlay(overlayHandle);
-           if (error != EVROverlayError.None)
-           {
-               throw new Exception("Failed to dispose overlay: " + error);
-           }
-       }
+       DestroyOverlay(overlayHandle);
    }

    ...

+   // overlayHandle -> handle variable name changed.
+   private void DestroyOverlay(ulong handle)
+   {
+       if (handle != OpenVR.k_ulOverlayHandleInvalid)
+       {
+           var error = OpenVR.Overlay.DestroyOverlay(handle);
+           if (error != EVROverlayError.None)
+           {
+               throw new Exception("Failed to dispose overlay: " + error);
+           }
+       }
+   }
}
Enter fullscreen mode Exit fullscreen mode

Final code

using UnityEngine;
using Valve.VR;
using System;

public class WatchOverlay : MonoBehaviour
{
    private ulong overlayHandle = OpenVR.k_ulOverlayHandleInvalid;

    private void Start()
    {
        InitOpenVR();
        overlayHandle = CreateOverlay("WatchOverlayKey", "WatchOverlay");
    }

    private void OnApplicationQuit()
    {
        DestroyOverlay(overlayHandle);
    }

    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();
        }
    }

    private ulong CreateOverlay(string key, string name)
    {
        var handle = OpenVR.k_ulOverlayHandleInvalid;
        var error = OpenVR.Overlay.CreateOverlay(key, name, ref handle);
        if (error != EVROverlayError.None)
        {
            throw new Exception("Failed to create overlay: " + error);
        }
        return handle;
    }

    private void DestroyOverlay(ulong handle)
    {
        if (handle != OpenVR.k_ulOverlayHandleInvalid)
        {
            var error = OpenVR.Overlay.DestroyOverlay(handle);
            if (error != EVROverlayError.None)
            {
                throw new Exception("Failed to dispose overlay.: " + error);
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

With that, we have created and cleaned up an overlay. Next part, we will draw an image file to the overlay.

Top comments (0)