Magic Leap 2 Controller Failing to Recognize Button Inputs

The summary of our problem is that the controller sometimes fails to detect all inputs (specifically from the trigger button, menu button, and trackpad) with the exception of the home button, which still registers.

I understand that in certain environmental conditions, controller tracking is degraded. However, within our Unity application, we do not consider the position or rotation of the controller, only the inputs from the menu button, the trackpad, and the trigger. We would expect similar behavior to the home button if the controller loses tracking–in testing, home button inputs are still received even in those degraded tracking environments. For example, putting the controller in my pocket results in a loss of tracking, but we are still receiving button inputs.

The OS version on our ML2 is 1.12, and the controller version is up to date.

Is the expectation that all button inputs should be working even when controller tracking is lost? I’m trying to determine if this is intended behavior or a bug.

When you are testing whether or not the inputs are working, are you using the input actions directly or reading it directly from the input device in c#?

The latter, reading it directly from the input device in C#. I’ve attached a screenshot for reference:

Is there a world in which we receive input from all buttons even when tracking is lost?

I wonder if this is a result of the unity input system refusing to read input if the “isTracked” flag is false.

—-

Do you mind trying to get the input based on the following example. Just make sure to change the logic on the bottom by removing the “is tracked” logic.

Tried your suggestion but to no avail, I’ve attached code snippet below.

using CommonCore.Utilities;
using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEngine.XR;
using InputDevice = UnityEngine.XR.InputDevice;
using XRCommonUsages = UnityEngine.XR.CommonUsages;

namespace ApplicationName
{
    public class MagicLeapControllerInput : SingletonBehavior<MagicLeapControllerInput>
    {
        private readonly StringBuilder _stringBuilder = new StringBuilder();
        private InputDevice _trackedDevice;

        public Action<Vector3> OnDevicePositionTracked;
        public Action<Quaternion> OnDeviceRotationTracked;
        public Action<Vector3> OnDeviceVelocityTracked;
        public Action<Vector3> OnDeviceAngularVelocityTracked;
        public Action<float> OnDeviceTriggerTracked;
        public Action<bool> OnDeviceBumperDownTracked;
        public Action<bool> OnDeviceMenuDownTracked;
        public Action<bool> OnDeviceIsTouchTracked;
        public Action<Vector2> OnDeviceTouchPosTracked;

        public Vector3 TrackedDevicePosition;
        public Quaternion TrackedDeviceRotation;
        public Vector3 TrackedDeviceVelocity;
        public Vector3 TrackedDeviceAngularVelocity;
        public float TrackedDeviceTrigger;
        public bool TrackedDeviceBumperDown;
        public bool TrackedDeviceMenuDown;
        public bool TrackedDeviceIsTouch;
        public Vector2 TrackedDeviceTouchPos; 

        private void Update()
        {
            _stringBuilder.Clear();

            if (GetControllerDevice(ref _trackedDevice))
            {

                if (_trackedDevice.TryGetFeatureValue(XRCommonUsages.devicePosition, out Vector3 devicePosition))
                {
                    TrackedDevicePosition = devicePosition;
                    OnDevicePositionTracked?.Invoke(devicePosition);
                    _stringBuilder.AppendLine($"ATTN: Device Position: {devicePosition}");
                }

                if (_trackedDevice.TryGetFeatureValue(XRCommonUsages.deviceRotation, out Quaternion deviceRotation))
                {
                    TrackedDeviceRotation = deviceRotation;
                    OnDeviceRotationTracked?.Invoke(deviceRotation);
                    _stringBuilder.AppendLine($"ATTN: Device Rotation: {deviceRotation}");
                }

                if (_trackedDevice.TryGetFeatureValue(XRCommonUsages.deviceVelocity, out Vector3 deviceVelocity))
                {
                    TrackedDeviceVelocity = deviceVelocity;
                    OnDeviceVelocityTracked?.Invoke(deviceVelocity);
                    _stringBuilder.AppendLine($"ATTN: Velocity: {deviceVelocity}"); 
                }

                if (_trackedDevice.TryGetFeatureValue(XRCommonUsages.deviceAngularVelocity, out Vector3 deviceAngularVelocity))
                {
                    TrackedDeviceAngularVelocity = deviceAngularVelocity;
                    OnDeviceAngularVelocityTracked?.Invoke(deviceAngularVelocity);
                    _stringBuilder.AppendLine($"ATTN: Angular Velocity: {deviceAngularVelocity}");
                }

                if (_trackedDevice.TryGetFeatureValue(XRCommonUsages.trigger, out float deviceTriggerValue))
                {
                    TrackedDeviceTrigger =deviceTriggerValue;
                    OnDeviceTriggerTracked?.Invoke(deviceTriggerValue);
                    _stringBuilder.AppendLine($"ATTN: Trigger Value: {deviceTriggerValue}");
                }

                if (_trackedDevice.TryGetFeatureValue(XRCommonUsages.gripButton, out bool deviceIsBumperDown))
                {
                    TrackedDeviceBumperDown = deviceIsBumperDown;
                    OnDeviceBumperDownTracked?.Invoke(deviceIsBumperDown);
                    _stringBuilder.AppendLine($"ATTN: IsBumperDown: {deviceIsBumperDown}");
                }

                if (_trackedDevice.TryGetFeatureValue(XRCommonUsages.menuButton, out bool deviceIsMenuDown))
                {
                    TrackedDeviceMenuDown = deviceIsMenuDown;
                    OnDeviceMenuDownTracked?.Invoke(deviceIsMenuDown);
                    _stringBuilder.AppendLine($"ATTN: IsMenuDown: {deviceIsMenuDown}");
                }

                if (_trackedDevice.TryGetFeatureValue(XRCommonUsages.primary2DAxisTouch, out bool deviceIsTouchActiveTracked))
                {
                    TrackedDeviceIsTouch = deviceIsTouchActiveTracked;
                    OnDeviceIsTouchTracked?.Invoke(deviceIsTouchActiveTracked);
                    _stringBuilder.AppendLine($"ATTN: IsTouchActive: {deviceIsTouchActiveTracked}");
                }

                if (_trackedDevice.TryGetFeatureValue(XRCommonUsages.primary2DAxis, out Vector2 deviceTouchPos))
                {
                    TrackedDeviceTouchPos = deviceTouchPos;
                    OnDeviceTouchPosTracked?.Invoke(deviceTouchPos);
                    _stringBuilder.AppendLine($"ATTN: TouchPos: {deviceTouchPos}");
                }

                //Log the controllers feature values
                Debug.Log(_stringBuilder);
            }
        }

        // Checks to see if the referenced Input Device is a tracked Controller.
        private bool GetControllerDevice(ref InputDevice controller)
        {
            _stringBuilder.AppendLine($"ATTN: Curr device is {controller.name}");

            if (IsTrackedController(controller))
            {
                return true;
            }
            else
            {
                _stringBuilder.AppendLine($"ATTN: No available device, finding new one");
            }

            // If we do not have reference to a valid input device or if it is not being tracked search for a new one.
            List<InputDevice> devices = new List<InputDevice>();
            InputDevices.GetDevicesWithCharacteristics(
                InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.HeldInHand, devices);

            for (int i = 0; i < devices.Count; i++)
            {
                if (IsTrackedController(devices[i]))
                {
                    controller = devices[i];
                    _stringBuilder.AppendLine($"ATTN: Found new input device {controller} {controller.name} {devices[i].characteristics} at index {i}");
                    return true;
                }
            }

            return true;
        }

        // Check to see if the controller is valid and tracked.
        private bool IsTrackedController(InputDevice controller)
        {
            // If we have a reference to an input device, make sure that it is tracked.
            if (controller.isValid)
            {
                controller.TryGetFeatureValue(XRCommonUsages.isTracked, out bool tracked);
                if (tracked)
                {
                    // If the device is not tracked, search for a new input device.
                    return true;
                }
            }
            return false;
        }
    }
}

You will need to change the following

To

   // Check to see if the controller is valid and tracked.
        private bool IsTrackedController(InputDevice controller)
        {
            // If we have a reference to an input device
            return controller.isValid;
        }

No luck still; can you confirm if receiving input from all buttons in any controller state is possible?

I just tested it. Sorry - It looks like having the Hand Interaction Profile affects how the controller is registered. I have submitted this to our voice of customer team. For now, you can get around the issue by removing the Hand Interaction Profile from the Input Profiles in the OpenXR settings

Thanks for the update. However, I believe we already do not have the Hand Interaction Profile from the Input Profiles in the OpenXR settings.

Try this script instead:


using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.XR;
using InputDevice = UnityEngine.XR.InputDevice;
using XRCommonUsages = UnityEngine.XR.CommonUsages;

public class Inputcontrollertester : MonoBehaviour
{
    private readonly StringBuilder _stringBuilder = new StringBuilder();
    private InputDevice _trackedDevice;
    public Text text;
    private void Update()
    {
        
        List<InputDevice> devices = new List<InputDevice>();
        InputDevices.GetDevicesWithCharacteristics(
            InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.HeldInHand, devices);

        for (int i = 0; i < devices.Count; i++)
        {
            _trackedDevice = devices[i];
            Debug.Log(devices[i].name);
        }
        
        if (_trackedDevice.isValid)
        {
            _stringBuilder.Clear();

            if (_trackedDevice.TryGetFeatureValue(XRCommonUsages.devicePosition, out Vector3 devicePosition))
                _stringBuilder.AppendLine($"Device Position: {devicePosition}");

            if (_trackedDevice.TryGetFeatureValue(XRCommonUsages.deviceRotation, out Quaternion deviceRotation))
                _stringBuilder.AppendLine($"Device Rotation: {deviceRotation}");
            
            if (_trackedDevice.TryGetFeatureValue(XRCommonUsages.trigger, out float deviceTriggerValue))
                _stringBuilder.AppendLine($"Trigger Value: {deviceTriggerValue}");

            if (_trackedDevice.TryGetFeatureValue(XRCommonUsages.gripButton, out bool deviceIsBumperDown))
                _stringBuilder.AppendLine($"IsBumperDown: {deviceIsBumperDown}");

            if (_trackedDevice.TryGetFeatureValue(XRCommonUsages.menuButton, out bool deviceIsMenuDown))
                _stringBuilder.AppendLine($"IsMenuDown: {deviceIsMenuDown}");

            if (_trackedDevice.TryGetFeatureValue(XRCommonUsages.primary2DAxisTouch, out bool deviceIsTouchActive))
                _stringBuilder.AppendLine($"IsTouchActive: {deviceIsTouchActive}");
            

            //Log the controllers feature values
            Debug.Log(_stringBuilder);
            text.text = _stringBuilder.ToString();
        }
    }
}

Does not work with it, the untracked controller still does not send any data to the Unity application, and only registers the home button to exit the app.

In the All features setting on OpenXR can you double check to see that “Hand Subsystem” and “Hand Interaction” are disabled?

Yes, they are disabled

Hi, I would like to add to this thread because I think that I am experiencing a similar problem.

During client visits I have frequently experienced that all input except the Home button would stop responding for a while. But it was strange because this never happened for me during development.

Upon further testing I have now come to the conclusion that the controller keeps tracking but looses inputs when holding the controller in my left hand. I have been able to consistently reproduce this behavior. It even applies to moments where the right hand holding the controller is obstructed by the left hand which I noticed some clients of mine did when they held the controller with both hands. Right hand over left hand is fine but not the other way around.

Maybe you have a similar issue in your case? If not then I will continue my search somewhere else.

I checked my input settings and I am listening for both right and left hand controller inputs. But I haven’t figured out yet how to fix this. Any ideas what could be causing this @kbabilinski?

Thanks!

Can you verify that this issue persists after removing the Hand Interaction Profile from the list of OpenXR interaction profiles and adding the generic binding <MagicLeapController>/triggerPressed ?

Thank you for your hint! My settings are correct but I then double checked which InputActionAsset was used and it turned out I used the asset from Assets/Packages/Magic Leap SDK and that had only the right hand version of all magic leap controller bindings.

I then went on and generated a new C# class from my input actions following the guide from the documentation: Inputs Sample | MagicLeap Developer Documentation
My issue has been fixed! I hope this can help the original poster too.
_

Might I suggest updating the MLInput asset in the next SDK release to use the generic bindings like <MagicLeapController>/triggerPressed etc. to prevent others from running into the same issue?

1 Like

Thank you very much for sharing your solution. I have recorded your feedback to the Voice of Customer team.