MRTK2 to MRTK3 - a MUCH SIMPLER way to intercept a raw air tap like with IMixedRealityPointerHandler

3 minute read

Sometimes the gods themselves descend from the Olympos - or in this case, Mount Rainier maybe - to explain to you things can be done a lot simpler ;). When I wrote this I already had the feeling there must be a simpler way than looking up the hand controllers, continually checking values using the update loop in a service, etc. - but I could not find it. I will cheerfully admit I am still very much in the process of tying to get to grips the Unity XR Interaction Toolkit. I can find lots of long rambling videos about it, as well as Unity’s nearly sample-free API documentation - that is nearly useless, as Intellisense works just as well for showing available API calls. But complete, simple, how-to-samples? Almost none to be found. This I why I write them, as I have been doing for nearly 15 years now. I don’t mind looking like an idiot in the process, doing things the long way around, as long as I can help people getting forward and get things done.

Case in point: I got explained getting an air tap using a way simpler method, but not how. It took me a while searching around and piece a bit of code together. And my immediate reflex is than: make a short sample, blog it, be done with it forever and I (and the rest of the world) finally have a - hopefully - easy to find sample.

Enough talk, let’s code

After this rather long introduction to a very short piece of code: here is “Intercepting an air tap, the XR Interaction Toolkit way”. That is, the first part:

public class DirectInputAirTapDisplayer : MonoBehaviour
{
    [SerializeField]
    private GameObject leftHand;
    [SerializeField]
    private GameObject rightHand;

    [SerializeField]
    private InputActionReference leftHandReference;
    [SerializeField]
    private InputActionReference rightHandReference;
    
    private void Start()
    {
        leftHand.SetActive(false);
        rightHand.SetActive(false);
        leftHandReference.action.performed += ProcessLeftHand;
        rightHandReference.action.performed += ProcessRightHand;
    }
}

So I now have four editor configurable fields, the two new ones are actually InputActionReference just as Finn explained. What I had to find out myself, and good luck doing so: it turns out, those have an action property, which in turn have a performed event that you can trap. Which brings us to the next part:

private void ProcessRightHand(InputAction.CallbackContext ctx)
{
    ProcessHand(ctx, rightHand);
}

private void ProcessLeftHand(InputAction.CallbackContext ctx)
{
    ProcessHand(ctx, leftHand);
}

private void ProcessHand(InputAction.CallbackContext ctx, GameObject g)
{
    g.SetActive(ctx.ReadValue<float>() > 0.95f);
}

The event handler gets a InputAction.CallbackContext parameter that allows you to read information about the event. Now if you use the Select input action as Finn suggest, you can’t - that’s just an event. I need the SelectValue event. And in case you have no idea what the hell I am talking about and where to find that - like my yesterday self:

  • Search for “Input Actions” in Packages,

  • Select “MRTK Default Input Actions”
  • After you have selected that, click the little cross all the way to the right in the select box to cancel the search
  • Expand the MRTK Default Input Actions, then drag the “MRTK LeftHand/Select Value” node on top of “Left Hand Reference”, and the “MRTK RightHand/Select Value” node on top of “Right Hand Reference”

All very evident - once you know how, and once you know this stuff exists at all.

Of course, I could have done this all with two little lambdas attached to the event handler, but when I see event handler attachments, the “memory leak warning” light in my brain flashes on immediately - and although I never was with the boy scouts, I like to behave like one, and leave no litter laying around:

private void OnDestroy()
{
    leftHandReference.action.performed -= ProcessLeftHand;
    rightHandReference.action.performed -= ProcessRightHand;
}

Concluding words

As I said, I am still trying to wrap my head around the XR Interaction Toolkit. I hope I don’t sound condescending about or angry on Finn for ‘not properly explaining’ - I hugely appreciate reaching out to me, putting me on the right track, and not beating around the bush about documentation still leaving a lot to be desired. And in any case, this is the place where I like to be most - trail blazing, finding out how - even when I sometimes take a roundabout way to get somewhere -, running into and filing bugs… and sometimes even fixing them before the team themselves have found them. To boldly go where no-one (or at least very few) have gone before ;)

(Yeah, I am Star Trek fan. Although a moderate one. You won’t find me learning Klingon or dressing up like one).

Anyhew, you can find the MRTKAirTap with the DirectInputAirTapDisplayer here.