Don’t Let Go and Cross HMD Development

This week I launched the commercial version of my Dev-Kit demo Don’t Let Go! On WEARVR.
It’s a small but fun experience that is a great way to introduce friends to VR and getting a laugh out of their reactions.

Don’t Let Go! Works on the HTC Vive and the Oculus Rift, both using their own SDK’s.

header

I’ve gotten a few questions from fellow developers on how I made that work, so I decided to write a blogpost on Cross HMD development using Unity.

Versions
The development tools for working in VR are in constant development. So by the time you are reading this, this blogpost is likely already out of date. For Don’t Let Go I used the following:

Unity 5.4.0b15
Oculus SDK 1.3.2 (install instructions here)
SteamVR Unity Plugin 1.0.9 (1.1.0 has been released since)

Theory
So in theory this is the only thing you need to do to create a Cross HMD application. In the Player Settings, make sure the application is Virtual Reality Supported and add both SDKs.

settings

With this setup Unity will use the appropriate SDK whether a user is wearing the Oculus Rift or the HTC Vive.
And this works very well, I am not using any of the prefabs that come with either the Oculus Utilities or OpenVR package.

Practice
In practice however there are a couple of things that you will want to implement for the Oculus SDK and OpenVR differently. So on a couple of places in the code we will need to know if the user is wearing an Oculus Rift or a Vive (or other OpenVR supported device in the future).

To know which sdk is being used by Unity you can use this bit of code:

if (UnityEngine.VR.VRSettings.loadedDeviceName == "OpenVR")

Loading
Loading screens in VR can be a little tricky because losing frame rate during loading is very common. OpenVR and Oculus have different approaches to tackle this problem so you need to implement your loading screen differently for each HMD. There are plenty of tutorials on how to make a loading screen so I won’t go into detail.

The main thing to note is that OpenVR has a compositor which you can fade to during loading. This is a small empty scene which will always keep up a smooth framerate. For Don’t Let Go I changed the color to black for comfort. You can use it like this:
Valve.VR.OpenVR.Compositor.FadeToColor(0.5f, 0, 0, 0, 1, false);

I believe it is also possible to add a custom cubemap, but for Don’t Let Go! I opted to just go with the default black scene.

On the Oculus side I do things a little differently. I simply display a loading message during asynchronous loading and fade to complete black before activating the loaded scene. Scene activation can still cause stalls, but it’s not noticeable when the screen is completely black.

But if the stall takes too long, the Oculus SDK will render an hourglass symbol in the bottom right of your vision. So the user knows there is loading going on.

Reset Seated Position
When making a seated experience like Don’t Let Go it’s often necessary to have the user Reset the Orientation / position. This is to make sure the user’s virtual and real body line up correctly.

To do this you can call the Unity Implementation like this:
UnityEngine.VR.InputTracking.Recenter();

This will work on the Oculus, but does nothing on OpenVR.
So for the OpenVR SDK I used the following:

Valve.VR.OpenVR.System.ResetSeatedZeroPose();
Valve.VR.OpenVR.Compositor.SetTrackingSpace(Valve.VR.ETrackingUniverseOrigin.TrackingUniverseSeated);

Gaze Input and UI
To interact with UI in Don’t Let Go! I use a gaze based input module. It is a modified version of this system I found on the Oculus forums
The problem is that this doesn’t work with OpenVR. The OpenVR SDK does something to the camera that messes with the input module.

Fortunately there is a pretty easy fix for this.

Directly under your main Camera you create a new Camera and call it ViveDummyCamera.
Now set the clear flags to Don’t Clear and the Culling mask to Nothing. Set the Target Display to Display 1 and the Target Eye to None (Main Display).

ViveDummyCam

Now on your Canvasses make sure the Camera is set to this new ViveDummyCamera

canvas

I don’t use the Vive Remotes in Don’t Let Go! But if you could use a similar trick by adding camera dummies for each remote to make them work on the UI.

Conclusion
And that is all the code I needed to make my game cross HDM. Unity has worked hard to make the process as easy as possible, and in theory it’s as simple as adding the SDK’s to the player settings. In practice there are a couple of things that still need some custom code and SDK calls. But it’s very doable to create cross HMD games with Unity right now!

Spread the word. Share this post!

Leave a Reply

Your email address will not be published. Required fields are marked *