It turns out that some devices, like the Valve Index, don't initialize until you pick them up. Which can happen after the Start() method runs.
So to solve this, you need to write a function that is called later on during the Update() method until you have the device object(s) you need.
For example, if you want to find the left controller, you would run something like the following:
using System.Collections;
using System.Collections.Generic;
using UnityEditor.PackageManager;
using UnityEngine;
using UnityEngine.XR;
public class HandPresence : MonoBehaviour
{
private InputDevice _targetDevice;
void Start()
{
TryInitialize();
}
void TryInitialize()
{
var inputDevices = new List<InputDevice>();
InputDeviceCharacteristics rightControllerCharacteristics =
InputDeviceCharacteristics.Right | InputDeviceCharacteristics.Controller;
InputDevices.GetDevicesWithCharacteristics(rightControllerCharacteristics, inputDevices);
if (inputDevices.Count == 0)
{
return;
}
_targetDevice = inputDevices[0];
}
void Update()
{
if (!_targetDevice.isValid)
{
TryInitialize();
}
else
{
// Do what you would like with _targetDevice here
}
}
}
In this case, we use the TryInitialize() function to save the device into a private class variable. We run this function in the Start() method, but then check if we actually saved a device (by checking if the private class variable is valid) in the Update() method, and call TryInitialize() again if we did not.
This was tested and works in Unity 2020.2.7f1, with OpenXR 1.02.