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
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
Add comment
All about bugs
View bugs we have successfully reproduced, and vote for the bugs you want to see fixed most urgently.
Latest issues
- Asset Bundles retain their previous hash and CRC values when an object within a bundle is changed and rebuilt
- APV Reflection Probe Normalization breaks when SSGI is enabled
- Default Custom Components in project have Library counterparts
- [iOS]"The destination host has an erroneous SSL certificate" error is thrown when using UnityWebRequest to connect to the server with a self-signed certificate
- Freeze/crash on DynamicHeapAllocator::Allocate when opening a specific project
Edvinas01
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
}
```