Handling SteamVR Controller Input
I’ve talked to quite a few developers recently who weren’t really sure how to get started with input in their VR projects. While it’s not too hard to get started, there are some things that are important to understand. In this post, I’ll cover some of the fundamentals of the SteamVR controller inputs and interactions.
Project Setup
Create a new project and import the SteamVR plugin from the Asset Store.
Now find the [CameraRig] prefab in your project view and place it into your scene.
Next, delete the “Main Camera” that was in our scene by default. The [CameraRig] already has a Camera component for us that tracks to our head, having another camera in here will just mess things up.
Expand the [CameraRig] and select the left & right controllers.
Add the “SteamVR Tracked Controller” component.
Play Time
Now save your scene and press play.
With the game in play mode, select just the left controller.
Now grab the controller and start pressing buttons.
The TrackedController Script
The TrackedController script is great and actually gives us a bunch of events we can register for in our code.
Let’s take a look at those events.
Code Time
We’re going to hook into these events with our own class.
Create a new folder named “Code“, then create a new script named “PrimitiveCreator” in that folder.
Paste this code in for the class.
Add the Script to your controllers
Before we go over the code, add the script to your controllers and try it out.
Press play and start placing primitives with the trigger. Use the trackpad to select which type of primitive the controller should place.
How does it work?
Event Registration
We’re using the OnEnable & OnDisable methods of the MonoBehaviour here to cache our TrackedController and register for events.
The events we care about are TriggerClicked & PadClicked. When the player clicks the Trigger we call into our HandleTriggerClicked() method [line 7], and when they click the Pad, we call HandlePadClicked() [line 8].
It’s important to pay attention to the OnDisable method though. Since we’re registering for events when our gameobject is enabled, we need to be sure to deregister them when it’s disabled. To deregister, we just call with the same syntax as registration, but use a minus instead of a plus.
Events not being deregistered is one of the few ways you can leak memory in a garbage collected language. In fact it’s the most common leak I see in c# code.
Our Event Handlers – The Trigger
Our first event handler throws away the event arguments and calls into SpawnCurrentPrimitiveAtController(). We do this for a few reasons. The main one is to have a method named for what it does and taking only the parameters it needs.
If you’re comfortable with linq, you could re-write the event registration to avoid this method, but for this example I wanted to keep it simple.
The SpawnCurrentPrimitiveAtController() method does what it says. It uses CreatePrimitive, passing in the _currentPrimitiveType that we’ve previously set to PrimitiveType.Sphere. [line 9]
Then it adjusts the position and rotation to match our controller. [lines 10 & 11] Finally, it adjusts the scale to be a size we can work with. For planes, we need to get even smaller to avoid it covering our entire view. [lines 13-15]
Our Event Handlers – The Trackpad
The other event we registered for was PadClicked. That event is registered to our HandlePadClicked() method. In this method, we check the event argument’s padY variable.
padY represents the Y axis on your touchpad. It’s value can range from -1 to +1, with negative values being above the center and 0 being exactly in the middle.
We check the padY value to see if it’s less than 0, which means the player clicked on the upper part of the pad. If they did, we increment our primitive type. If not, we decrement our primitive type. In each of these methods, we do a quick bounds check for wrapping, but the key part is that we’ve updated the _currentPrimitiveType value that was used when we pull the trigger.
Where to go from here
If you’ve already read my post on Getting Started with SteamVR, you may be considering some other options for your trigger click. There are countless things you could do just to this project. You could modify the system so that the initial press of the trigger spawns the primitive, but keeps it at the controllers position and rotation (perhaps by making it a child of the controller), then hook into the TriggerUnclicked method to drop it in it’s position.
Try hooking into a few of the other events and see what you can come up with!
If you really want to jumpstart your VR project though and get a game out quickly, I’m building a VR development course where we go over all the steps to build a full game using real world proper practices.
Tell me how and I’ll contact you as soon as there’s more info.