Search Issue Tracker

Fixed in 1.0.0-pre.4

Votes

1

Found in [Package]

1.0.0-pre.3

Issue ID

1325375

Regression

No

[XR Interaction Toolkit] XRDirectInteractor does not always grab an object when the object consists of multiple colliders

Package: XR Interaction

-

Reproduction steps:
1. Open the attached "case_1325375.zip" project
2. Load SampleScene
3. Enter Play mode
4. Grab and release a cube multiple times using the Right Hand XR Controller

Expected result: the cube is grabbed consistently
Actual result: the cube is not grabbed approximately 1 out of 5 attempts (see "dAFR4gkN2M.mp4" video)

Reproducible with: XR Interaction Toolkit 1.0.0-pre.3 (2019.4.24f1, 2020.3.4f1, 2021.1.2f1, 2021.2.0a12)
Could not test with 2018.4.30f1 (XR Interaction Toolkit not available)

Tested with:
- Oculus Rift

Comments (1)

  1. EdEddnEddy

    Apr 12, 2021 08:08

    The issue lies in the following file:
    com.unity.xr.interaction.toolkit@1.0.0-pre.2\Runtime\Interaction\Interactors\XRDirectInteractor.cs

    The culprit is the `OnTriggerEnter` and `OnTriggerExit` methods, which don't take into account that the colliding object might be made out of multiple colliders:
    ```
    /// <summary>
    /// See <see cref="MonoBehaviour"/>.
    /// </summary>
    /// <param name="other">The other <see cref="Collider"/> involved in this collision.</param>
    protected void OnTriggerEnter(Collider other)
    {
    if (interactionManager == null)
    return;

    var interactable = interactionManager.TryGetInteractableForCollider(other);
    if (interactable != null && !m_ValidTargets.Contains(interactable))
    m_ValidTargets.Add(interactable);
    }

    /// <summary>
    /// See <see cref="MonoBehaviour"/>.
    /// </summary>
    /// <param name="other">The other <see cref="Collider"/> involved in this collision.</param>
    protected void OnTriggerExit(Collider other)
    {
    if (interactionManager == null)
    return;

    var interactable = interactionManager.TryGetInteractableForCollider(other);
    if (interactable != null)
    m_ValidTargets.Remove(interactable);
    }
    ```

    A workaround is to check if the collider belongs to the initial interactor that has entered the collider, and keep track of the colliders that are overlapping. For example:
    ```cs
    public class PhysicsInteractor : XRDirectInteractor
    {
    #region Fields

    private XRBaseInteractable currentInteractable;

    // List of colliders from "currentInteractable" that are currently hovered.
    private readonly List<Collider> colliders = new List<Collider>();

    #endregion

    #region Unity Lifecycle

    protected override void Awake()
    {
    base.Awake();
    if (interactionManager == null)
    {
    interactionManager = FindObjectOfType<XRInteractionManager>();
    }
    }

    protected override void OnEnable()
    {
    base.OnEnable();
    interactionManager.interactableUnregistered += OnInteractableUnregistered;
    }

    protected override void OnDisable()
    {
    base.OnDisable();
    interactionManager.interactableUnregistered -= OnInteractableUnregistered;
    ClearCurrentInteractable();
    }

    private new void OnTriggerEnter(Collider other)
    {
    var interactable = interactionManager.TryGetInteractableForCollider(other);
    if (interactable == null)
    {
    return;
    }

    if (currentInteractable == null)
    {
    currentInteractable = interactable;
    validTargets.Add(currentInteractable);
    }

    if (currentInteractable == interactable)
    {
    colliders.Add(other);
    }
    }

    private new void OnTriggerExit(Collider other)
    {
    RemoveInteractableCollider(other);
    }

    #endregion

    #region Methods

    /// <summary>
    /// Remove collider from the interactor. E.g. when the collider is detached or removed.
    /// </summary>
    public void RemoveInteractableCollider(Collider other)
    {
    colliders.Remove(other);
    if (colliders.Count != 0)
    {
    return;
    }

    ClearCurrentInteractable();
    }

    protected override void OnSelectExited(SelectExitEventArgs args)
    {
    base.OnSelectExited(args);
    ClearCurrentInteractable();
    }

    private void ClearCurrentInteractable()
    {
    currentInteractable = null;
    validTargets.Clear();
    colliders.Clear();
    }

    private void OnInteractableUnregistered(InteractableUnregisteredEventArgs obj)
    {
    foreach (var interactableCollider in obj.interactable.colliders)
    {
    RemoveInteractableCollider(interactableCollider);
    }
    }

    #endregion
    }
    ```

Add comment

Log in to post comment

All about bugs

View bugs we have successfully reproduced, and vote for the bugs you want to see fixed most urgently.