Skip to content

Commit 6d92665

Browse files
authored
Additional feedback for pr611 (#659)
* Improving Hover-Proximity event behavior as per PR 611 feedback.
1 parent 95db47b commit 6d92665

11 files changed

Lines changed: 142 additions & 121 deletions

File tree

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,27 @@
1+
// Copyright (c) Mixed Reality Toolkit Contributors
2+
// Licensed under the BSD 3-Clause
3+
14
using System;
2-
using UnityEngine;
3-
using UnityEngine.XR.Interaction.Toolkit;
45

56
namespace MixedReality.Toolkit.Input
67
{
78
/// <summary>
8-
/// Event data associated with proximity events triggered by a Collider and an Interactor combo.
9+
/// Event data associated with proximity events triggered by a NearInteractionModeDetector.
910
/// </summary>
10-
public abstract partial class BaseProximityEventArgs : EventArgs
11+
public abstract class BaseProximityEventArgs : EventArgs
1112
{
1213
/// <summary>
1314
/// Constructor for BaseProximityEventArgs.
1415
/// </summary>
15-
/// <param name="sender">Source of event.</param>
16-
/// <param name="collider">Collider that triggers proximity event.</param>
17-
/// <param name="interactor">XRBaseInteractor that triggers proximity event.</param>
18-
public BaseProximityEventArgs(object sender, Collider collider, XRBaseInteractor interactor)
16+
/// <param name="nearInteractionModeDetector">NearInteractionModeDetector that triggers proximity event.</param>
17+
public BaseProximityEventArgs(NearInteractionModeDetector nearInteractionModeDetector)
1918
{
20-
this.sender = sender;
21-
this.collider = collider;
22-
this.interactor = interactor;
19+
NearInteractionModeDetector = nearInteractionModeDetector;
2320
}
2421

2522
/// <summary>
26-
/// The object that triggered the proximity event.
27-
/// </summary>
28-
public object sender { get; private set; }
29-
30-
/// <summary>
31-
/// The collider associated with the interaction event.
32-
/// </summary>
33-
public Collider collider { get; private set; }
34-
35-
/// <summary>
36-
/// The interactor associated with the interaction event.
23+
/// The NearInteractionModeDetector associated with the proximity interaction event.
3724
/// </summary>
38-
public XRBaseInteractor interactor { get; private set; }
25+
public NearInteractionModeDetector NearInteractionModeDetector { get; private set; }
3926
}
4027
}

org.mixedrealitytoolkit.input/InteractionModes/BaseProximityEventArgs.cs.meta

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

org.mixedrealitytoolkit.input/InteractionModes/IXRProximityInteractable.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,15 @@ namespace MixedReality.Toolkit.Input
1616
public interface IXRProximityInteractable
1717
{
1818
/// <summary>
19-
/// Registers the duple Collider + XRBaseInteractor as triggering proximity.
19+
/// Registers the Detector as triggering proximity.
2020
/// </summary>
21-
/// <param name="collider">Collider triggering proximity.</param>
22-
/// <param name="xrBaseInteractor">Interactor triggering proximity.</param>
21+
/// <param name="args">ProximityEnteredEventArgs that has the Detector triggering the proximity event.</param>
2322
void OnProximityEntered(ProximityEnteredEventArgs args);
2423

2524
/// <summary>
26-
/// Unregisters the duple Collider + XRBaseInteractor as triggering proximity.
25+
/// Unregisters the Detector as triggering proximity.
2726
/// </summary>
28-
/// <param name="collider">Collider that in combination with the interactor was triggering proximity.</param>
29-
/// <param name="xrBaseInteractor">Interactor that in combination with the collider was triggering proximity.</param>
27+
/// <param name="args">ProximityExitedEventArgs that has the Detector triggering the proximity event.</param>
3028
void OnProximityExited(ProximityExitedEventArgs args);
3129
}
3230
}

org.mixedrealitytoolkit.input/InteractionModes/NearInteractionModeDetector.cs

Lines changed: 78 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -16,58 +16,104 @@ namespace MixedReality.Toolkit.Input
1616
[AddComponentMenu("MRTK/Input/Near Interaction Mode Detector")]
1717
public class NearInteractionModeDetector : ProximityDetector
1818
{
19+
/// <summary>
20+
/// The set of near interactors that belongs to near interaction
21+
/// </summary>
1922
[SerializeField]
2023
[Tooltip("The set of near interactors that belongs to near interaction")]
2124
private List<XRBaseInteractor> nearInteractors;
2225

2326
/// <summary>
24-
/// Used to keep track of the previously detected colliders so that we can know which
25-
/// colliders stopped being detected and update their buttons front plate and rounded
26-
/// rect if they are labeled as dynamic (based on proximity).
27+
/// Keeps track of the previously detected interactables so that we can know which
28+
/// interactable stopped being detected and trigger corresponding event.
29+
/// </summary>
30+
private readonly HashSet<IXRProximityInteractable> previouslyDetectedInteractables = new();
31+
32+
/// <summary>
33+
/// Stores the currently detected interactables (updated every frame).
2734
/// </summary>
28-
List<Collider> previouslyDetectedColliders = new List<Collider>();
35+
private readonly List<IXRProximityInteractable> currentlyDetectedInteractables = new();
36+
37+
/// <summary>
38+
/// Stores the interactables that are no longer detected (updated every frame).
39+
/// </summary>
40+
private readonly List<IXRProximityInteractable> noLongerDetectedInteractables = new();
2941

3042
/// <inheritdoc />
3143
public override bool IsModeDetected()
3244
{
3345
bool result = base.IsModeDetected() || IsNearInteractorSelecting();
3446
if (result)
3547
{
36-
for (int i = 0; i < previouslyDetectedColliders.Count; i++)
48+
UpdateCurrentlyDetectedInteractables();
49+
UpdateProximityExited();
50+
UpdateProximityEntered();
51+
}
52+
return result;
53+
}
54+
55+
/// <summary>
56+
/// Calls OnProximityExited for all interactables that were previously ProximityHover detected but are no longer detected.
57+
/// </summary>
58+
private void UpdateProximityExited()
59+
{
60+
noLongerDetectedInteractables.Clear();
61+
62+
foreach (IXRProximityInteractable previouslyDetectedInteractable in previouslyDetectedInteractables)
63+
{
64+
if (!currentlyDetectedInteractables.Contains(previouslyDetectedInteractable))
3765
{
38-
Collider previouslyDetectedCollider = previouslyDetectedColliders[i];
39-
if (!DetectedColliders.Contains(previouslyDetectedCollider) && previouslyDetectedCollider != null)
40-
{
41-
IXRProximityInteractable nearInteractionMode = previouslyDetectedCollider.GetComponent<IXRProximityInteractable>();
42-
if (nearInteractionMode != null)
43-
{
44-
foreach (XRBaseInteractor xrBaseInteractor in nearInteractors)
45-
{
46-
previouslyDetectedCollider.GetComponent<IXRProximityInteractable>().OnProximityExited(new ProximityExitedEventArgs(this, previouslyDetectedCollider, xrBaseInteractor));
47-
}
48-
}
49-
previouslyDetectedColliders.Remove(previouslyDetectedCollider);
50-
}
66+
noLongerDetectedInteractables.Add(previouslyDetectedInteractable);
5167
}
52-
foreach (Collider collider in DetectedColliders)
68+
}
69+
70+
foreach (IXRProximityInteractable noLongerDetectedInteractable in noLongerDetectedInteractables)
71+
{
72+
if (noLongerDetectedInteractable != null)
5373
{
54-
if (!previouslyDetectedColliders.Contains(collider))
55-
{
56-
IXRProximityInteractable nearInteractionMode = collider.GetComponent<IXRProximityInteractable>();
57-
if (nearInteractionMode != null)
58-
{
59-
foreach (XRBaseInteractor xrBaseInteractor in nearInteractors)
60-
{
61-
collider.GetComponent<IXRProximityInteractable>().OnProximityEntered(new ProximityEnteredEventArgs(this, collider, xrBaseInteractor));
62-
}
63-
}
64-
previouslyDetectedColliders.Add(collider);
65-
}
74+
noLongerDetectedInteractable.OnProximityExited(new ProximityExitedEventArgs(this));
6675
}
76+
previouslyDetectedInteractables.Remove(noLongerDetectedInteractable);
6777
}
68-
return result;
6978
}
7079

80+
/// <summary>
81+
/// Call OnProximityEntered for all interactables that are currently detected but were not detected previously.
82+
/// </summary>
83+
private void UpdateProximityEntered()
84+
{
85+
foreach (IXRProximityInteractable currentlyDetectedInteractable in currentlyDetectedInteractables)
86+
{
87+
if (previouslyDetectedInteractables.Add(currentlyDetectedInteractable))
88+
{
89+
currentlyDetectedInteractable.OnProximityEntered(new ProximityEnteredEventArgs(this));
90+
}
91+
}
92+
}
93+
94+
/// <summary>
95+
/// Returns a hashset of all unique interactables that are currently detected.
96+
/// </summary>
97+
/// <returns>Hashset with unique interactables currently detected</returns>
98+
private void UpdateCurrentlyDetectedInteractables()
99+
{
100+
currentlyDetectedInteractables.Clear();
101+
102+
foreach (Collider collider in DetectedColliders)
103+
{
104+
if (InteractionManager.TryGetInteractableForCollider(collider, out IXRInteractable xrInteractable) &&
105+
xrInteractable is IXRProximityInteractable xrProximityInteractable &&
106+
!currentlyDetectedInteractables.Contains(xrProximityInteractable))
107+
{
108+
currentlyDetectedInteractables.Add(xrProximityInteractable);
109+
}
110+
}
111+
}
112+
113+
/// <summary>
114+
/// Indicates if there is an interactor with selection.
115+
/// </summary>
116+
/// <returns>True if an interactor has selection, false otherwise.</returns>
71117
private bool IsNearInteractorSelecting()
72118
{
73119
foreach (XRBaseInteractor nearInteractor in nearInteractors)

org.mixedrealitytoolkit.input/InteractionModes/ProximityDetector.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,24 @@ public class ProximityDetector : MonoBehaviour, IInteractionModeDetector
5959
/// </summary>
6060
public HashSet<Collider> DetectedColliders => colliders;
6161

62+
/// <summary>
63+
/// The interaction manager to use to query interactors and their registration events.
64+
/// Currently protected internal, may be exposed in a future update.
65+
/// </summary>
66+
internal protected XRInteractionManager InteractionManager
67+
{
68+
get
69+
{
70+
if (interactionManager == null)
71+
{
72+
interactionManager = ComponentCache<XRInteractionManager>.FindFirstActiveInstance();
73+
}
74+
75+
return interactionManager;
76+
}
77+
set => interactionManager = value;
78+
}
79+
6280
/// <summary>
6381
/// A Unity event function that is called when an enabled script instance is being loaded.
6482
/// </summary>
Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,19 @@
1+
// Copyright (c) Mixed Reality Toolkit Contributors
2+
// Licensed under the BSD 3-Clause
3+
14
using UnityEngine;
2-
using UnityEngine.XR.Interaction.Toolkit;
35

46
namespace MixedReality.Toolkit.Input
57
{
68
public class ProximityEnteredEventArgs : BaseProximityEventArgs
79
{
810
/// <summary>
9-
/// Constructor for ProximityEnteredArgs.
11+
/// Constructor for ProximityEnteredEventArgs.
1012
/// </summary>
11-
/// <param name="sender">Source of event.</param>
12-
/// <param name="collider">Collider associated with the Proximity Entered event.</param>
13-
/// <param name="interactor">Interactor associated with the Proximity Entered event.</param>
14-
public ProximityEnteredEventArgs(object sender, Collider collider, XRBaseInteractor interactor) : base(sender, collider, interactor)
13+
/// <param name="nearInteractionModeDetector">NearInteractionModeDetector that triggers proximity entered event.</param>
14+
public ProximityEnteredEventArgs(NearInteractionModeDetector nearInteractionModeDetector) : base(nearInteractionModeDetector)
1515
{
1616
//Empty on purpose
1717
}
18-
19-
/// <summary>
20-
/// The collider associated with the proximity entered event.
21-
/// </summary>
22-
public new Collider collider => base.collider;
23-
24-
/// <summary>
25-
/// The Interactor associated with the proximity entered event.
26-
/// </summary>
27-
public new XRBaseInteractor interactor => base.interactor;
2818
}
2919
}

org.mixedrealitytoolkit.input/InteractionModes/ProximityEnteredEventArgs.cs.meta

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,19 @@
1+
// Copyright (c) Mixed Reality Toolkit Contributors
2+
// Licensed under the BSD 3-Clause
3+
14
using UnityEngine;
2-
using UnityEngine.XR.Interaction.Toolkit;
35

46
namespace MixedReality.Toolkit.Input
57
{
68
public class ProximityExitedEventArgs : BaseProximityEventArgs
79
{
810
/// <summary>
9-
/// Constructor for ProximityExitedArgs.
11+
/// Constructor for ProximityExitedEventArgs.
1012
/// </summary>
11-
/// <param name="sender">Source of event.</param>
12-
/// <param name="collider">Collider associated with the Proximity Exited event.</param>
13-
/// <param name="interactor">Interactor associated with the Proximity Exited event.</param>
14-
public ProximityExitedEventArgs(object sender, Collider collider, XRBaseInteractor interactor) : base(sender, collider, interactor)
13+
/// <param name="nearInteractionModeDetector">NearInteractionModeDetector that triggers proximity exited event.</param>
14+
public ProximityExitedEventArgs(NearInteractionModeDetector nearInteractionModeDetector) : base(nearInteractionModeDetector)
1515
{
1616
//Empty on purpose
1717
}
18-
19-
/// <summary>
20-
/// The Collider associated with the proximity exited event.
21-
/// </summary>
22-
public new Collider collider => base.collider;
23-
24-
/// <summary>
25-
/// The Interactor associated with the proximity exited event.
26-
/// </summary>
27-
public new XRBaseInteractor interactor => base.interactor;
2818
}
2919
}

org.mixedrealitytoolkit.input/InteractionModes/ProximityExitedEventArgs.cs.meta

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

org.mixedrealitytoolkit.uxcomponents.noncanvas/Tests/Runtime/PressableButtonTests.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -855,16 +855,16 @@ public IEnumerator ChangeSpeechSettingsDisabled([ValueSource(nameof(PressableBut
855855
}
856856

857857
/// <summary>
858-
/// Test the PressableButton script has the activeCollidersWithInteractor hashset.
858+
/// Test the PressableButton script has the activeDetectors hashset.
859859
/// </summary>
860860
[UnityTest]
861-
public IEnumerator TestPressableButtonHasActiveCollidersWithInteractorHashset()
861+
public IEnumerator TestPressableButtonHasActiveDetectorsWithInteractorHashset()
862862
{
863863
FieldInfo[] fieldInfos;
864-
System.Type pressableButtonType = typeof(PressableButton);
864+
Type pressableButtonType = typeof(PressableButton);
865865

866866
fieldInfos = pressableButtonType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
867-
var result = fieldInfos.Where(fieldInfo => fieldInfo.Name.Equals("activeCollidersWithInteractor")).ToArray();
867+
var result = fieldInfos.Where(fieldInfo => fieldInfo.Name.Equals("activeDetectors")).ToArray();
868868

869869
Assert.AreEqual(1, result.Length);
870870

0 commit comments

Comments
 (0)