VR Buttons in Unity

by SlideFactory

Overview

Here we’ll cover two quick ways we can get pressable vr buttons working inside of Unity using OpenVR. The first leverages SteamVR’s Unity integration and the second a custom script without any dependencies. This post assumes you are running Unity with SteamVR/OpenVR and are comfortable setting up a basic VR environment. Let’s get started!

If you are coming from the video, you can jump straight to the scripts section.

Setup Video

If you are a visual learner, here is a setup video that will walk you through how to setup the scene and the two VR button components that are covered below. It doesn’t cover creating the Button.cs and Events.cs scripts but shows you how to use them.

VR Buttons Setup in Video Format

SteamVR’s Hover Button

If you are already using the SteamVR Asset Store package, then you can get pressable VR buttons very quickly using the packages provided scripts.

Environment Setup

  1. First, delete the Main Camera and Search for the “Player” prefab inside the SteamVR packages.
  2. Drag the Player Prefab into the Hierarchy.
  3. Create a Cube for the ground and set its position to 0, -.5, 1 and scale to 100, 1, 100
  4. Create a Cube for a table and set its position to 0, 0.75, 1, and scale to 1, 1.5, 1

Now you should be able to play your scene and look around in VR! You may need to adjust your table cube’s z position slightly if it’s too close.

Button

  1. Create a new empty game object named Button and give it the “Interactable” and “Hover Button” scripts from the SteamVR Unity package.
  2. Under your Button gameobject add a new Cube named Moving Part and scale it down to .1, .2, .1.
  3. Click back on your Button gameobject and drag your Moving Part gameobject into the
    “Moving Part” area of its hover button script.
  4. Under the “Local Move Distance” define how far you want it to go down until it is considered a push, try something like -0.05 for the Y value.
  5. Create the Events.cs script, found below, that will have a public method that you can attach to the On Button Down handler and call when pressed.
  6. Attach your ButtonHandler script to the Hover Button’s On Button Down.
  7. Position your button on the table and test.

Your button should now respond to your hands and move down when pressure is applied, calling your ButtonHandler’s method when it reaches the distance you put in at step 4.

Custom VR Buttons Using Springs

In this section, we’ll cover a very fast way to get the pushing feel and functionality on a VR button with very little setup and code needed. You’ll need the Button script below as well as Events script to handle what happens when your button is pressed. You can find these in the scripts section.

Environment

Follow the SteamVR’s Hover Button Environment section above. If you aren’t using the SteamVR prefab, that’s ok! Just make sure that your controllers/hands have a collider attached that isn’t a trigger and can interact with our button.

Button

  1. First, create an empty gameobject and name it Anchor and add a rigidbody, then check Is Kinematic and uncheck Use Gravity.
  2. Create a cube and name it Button, make the scale .1, .2, .1.
  3. Add a rigidbody and uncheck Use Gravity. Under the Constraints dropdown menu, Freeze everything except for Position Y (we want to allow moving up and down only)
  4. Add a Spring Joint and drag the Anchor gameobject from step 1 into the “Connected Body” area.
  5. Set the Spring to 100, Damper to 10 and Tolerance to 0, (tolerance will likely turn into 1e-20, this should be fine)
  6. On the Button, add the Button.cs script from below and set the Press length something like .05.
  7. Create a script with a public method that logs a message, or use Events.cs from below.
  8. Add the Events.cs script to the Ground gameobject to test then hook it into the Button.cs script’s Down Event. Use the OnCustomButtonPress function.
  9. Finally, Drag your button into position.

You should now be able to push the button down with your hand, or collider on your controller and see that it runs the Events.cs method when it gets to the bottom. Cheers!

Custom VR Button Caveats

Because this approach uses a spring and active colliders, you can’t have it go through other game objects with colliders on them. You’ll have to either turn off the Table’s box collider or set the button on a different layer and under Edit -> Project Settings -> Physics -> Layer Collision Matrix, disable their ability to collide.

VR Button Scripts

Button.cs

using UnityEngine;
using UnityEngine.Events;

public class Button : MonoBehaviour
{
    [System.Serializable]
    public class ButtonEvent : UnityEvent { }

    public float pressLength;
    public bool pressed;
    public ButtonEvent downEvent;

    Vector3 startPos;
    Rigidbody rb;

    void Start()
    {
        startPos = transform.position;
        rb = GetComponent<Rigidbody>();
    }

    void Update()
    {
        // If our distance is greater than what we specified as a press
        // set it to our max distance and register a press if we haven't already
        float distance = Mathf.Abs(transform.position.y - startPos.y);
        if (distance >= pressLength)
        {
            // Prevent the button from going past the pressLength
            transform.position = new Vector3(transform.position.x, startPos.y - pressLength, transform.position.z);
            if (!pressed)
            {
                pressed = true;
                // If we have an event, invoke it
                downEvent?.Invoke();
            }
        } else
        {
            // If we aren't all the way down, reset our press
            pressed = false;
        }
        // Prevent button from springing back up past its original position
        if (transform.position.y > startPos.y)
        {
            transform.position = new Vector3(transform.position.x, startPos.y, transform.position.z);
        }
    }
}

Events.cs

using UnityEngine;
using Valve.VR.InteractionSystem;

public class Events : MonoBehaviour
{
    public void OnPress(Hand hand)
    {
        Debug.Log("SteamVR Button pressed!");
    }

    public void OnCustomButtonPress()
    {
        Debug.Log("We pushed our custom button!");
    }
}

Share this article:

You might also like: