Unity Tutorial #7: Custom Actions in OpenXR

@Brian Kim (3/14/2023)

Requirements

Installation of Unity

Creating a Custom Action

Unity and OpenXR have built-in support for several actions, including locomotion, teleportation, grabbing objects, and working with buttons. However, if you are trying to include actions that are not in the standard library, it might be difficult to find the relevant documentation.

So, for this tutorial, we'll go over a fairly basic custom interaction in OpenXR. We'll create an object in a Unity Scene, and have the object change color when we click our trigger key. This will require custom actions on the part of a controller and an object, requiring code to be written.

Create Project

In Unity Hub, click on "New Project."

From the options, you can choose either a 3D template or a VR template. In this case, I'll create a 3D template.

Name the project something relevant, and we're good to go!

Installing OpenXR Plugin

To get the OpenXR Plugin working on our project, we'll go to the "Window" tab at the top of the screen and, scrolling down, click on "Package Manager."

Click on the gear button next to the search bar, and from the drop-down, select "Advanced Project Settings."

At the bottom should be "XR Plugin Management." Clicking on that will take you to the page above, where you can hit "Install XR Plugin Management."

Once installation is done, you should find yourself here:

Here, you just need to click the box next to "OpenXR" and have it added to your project, though you may need to restart the editor.

After this, we'll need to enable the XR Interaction Toolkit. To get to this, go back to the Package Manager and click on "Packages: In Project" at the top left.

Click on "Unity Registry". You will find the XR Interaction toolkit in the Unity Registry, at the bottom of the list. Once you find it, click "Install."

Congratulations! You are now ready to begin developing with OpenXR.

Updating the Hierarchy

Before we go further, we'll need to update the basic scene setup to enable VR interactions. Right now, the scene hierarchy should look like the right.

First up, right click on the hierarchy, scroll down the options, and click on the XR tag. This should give you the option to switch the main camera - the default for Unity's 3D system - to an XR rig. 

Once that change is made, we will also want an Interaction Manager, again from the XR tag.

Finally, we want to include something to interact with, so go to the 3D object option and pull in a cube.

The final hierarchy should look like this:

Implementing New Actions

At this point, we'll want to add the ability for our program to take in new inputs from the controllers.

So, go to the files portion of the Unity interface, and go down Packages > Input System > InputSystem > Plugins > PlayerInput.

At that location, there should be file called "DefaultInputActions." Clicking that will open a set of action mappings:

Feel free to explore the actions briefly. As you will see, these are intended to provide "folders" of specific inputs from controllers - defined generically so that they can be brought in from multiple headsets - and connect them to specific actions in the VR scene defined by Unity.

For now, let's create our own InputActions mapping. Go to Assets, create a new Folder called "Samples," navigate inside the folder, right-click, go to "Create," and create new "Input Mappings." The results should look like this:

At this point, we want to create a new action mapping to explore how this feature works, so open the "CustomActions" mapping and click on the "+" next to "Action Maps." This will allow us to create our own action mappings, which we can title whatever we want. For now, I'll go with "Custom."


Within "Custom", let's create a new action and binding. I'll call the action "Toggle" and I'll add two bindings. To do this, I click the arrow on the right of the action to get the bindings, then I click on the "Path" and go down XR Controller > XR Controller (LeftHand) > Optional Controls > triggerPressed. I do the same for the right hand, leaving me with two bindings for the Toggle action.

This means that, when I activate either of the bindings in my controller, as long as it is recognized by OpenXR, it will activate the associated action.

Note that this is a place where mistakes can easily arise. If the tutorial isn't working for you device, try searchiing for inputs specific to your controller.

If you check the settings for the "Toggle" action itself, you should see the options on the right. For this tutorial, nothing needs to be changed. The main feature to know is the "Action Type," which can be changed to include a wider range of numerical input values.

Creating Action Code

Now, close the action mappings window, making sure to save the new changes, and go to the Assets folder. There, create a new subfolder called "Scripts," and in that folder, create a new C# file, which I will name "ToggleExample."

For the file, copy the following code:


using System.Collections;

using System.Collections.Generic;

using UnityEngine;

using UnityEngine.InputSystem;


public class ToggleExample : MonoBehaviour

{

    public InputActionReference toggleRef = null;


    void Awake()

    {

        toggleRef.action.started += Toggle;

    }


    // Update is called once per frame

    void Update()

    {

        toggleRef.action.started -= Toggle;

    }


    private void Toggle(InputAction.CallbackContext context)

    {

        bool active = !gameObject.activeSelf;

        gameObject.SetActive(active);

    }

}

Next, create a new C# file called "ColorExample" and write the following code:

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

using UnityEngine.InputSystem;


public class ColorExample : MonoBehaviour

{

    public InputActionReference colorRef = null;


    private MeshRenderer meshRenderer = null;


    void Awake()

    {

        meshRenderer = GetComponent<MeshRenderer>();

    }


    // Update is called once per frame

    void Update()

    {

        float val = colorRef.action.ReadValue<float>();

        UpdateColor(val);

    }


    private void UpdateColor(float val)

    {

        meshRenderer.material.color = new Color(val, val, val);

    }

}

"ToggleExample" will detect input actions from a controller input that is bound to it, and "ColorExample" will update the color of the object that it is attached to.

Attaching C# Inputs

We return to the Unity scene. In the screen hierarchy, we'll select the Cube and, scrolling down its settings box, click "Add Component."

Next, we go to the new Scripts folder, and select "Color Example".

After this, we add the "ToggleExample" script, which should leave us with the current situation.

After this, we set the Color Ref and Toggle Ref values to the Custom/Toggle actions that we created.

Once we've finished this, our project is now ready to go! Only one more step left. Go to the build settings with File > Build Settings.

I use Android builds, then move the project to my headset using SideQuest, but other options are possible

If you are using an Android output for an Oculus build, then make sure that the Oculus is targeted for the Android build in project settings:

If "Build and Run" is greyed out, you might need to hit "Switch Platform" first.

And if everything has gone right, you should now be able to enter a VR scene and toggle the cube in the scene between black and white by pressing the trigger button on your controller!

ToggleCube.mp4