Skip to content

Commit f985d45

Browse files
authored
Introduce Interactable reset API and behaviour using it (#144)
* Define API to reset an interactable * Implement AutoResetBehaviour based on new API * Docs
1 parent f032e67 commit f985d45

27 files changed

+319
-20
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright (c) Reality Collective. All rights reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
using RealityToolkit.Input.InteractionBehaviours;
5+
using UnityEditor;
6+
using UnityEditor.UIElements;
7+
using UnityEngine.UIElements;
8+
9+
namespace RealityToolkit.Editor.Inspectors
10+
{
11+
[CustomEditor(typeof(AutoResetBehaviour), true)]
12+
public class AutoResetBehaviourInspector : BaseInteractionBehaviourInspector
13+
{
14+
private const string resetDelay = "resetDelay";
15+
private const string resetPose = "resetPose";
16+
17+
/// <summary>
18+
/// The <see cref="AutoResetBehaviour"/> does not care what the interactor handedness
19+
/// is and thus we can hide this setting from the user.
20+
/// </summary>
21+
protected override bool ShowHandedness => false;
22+
23+
public override VisualElement CreateInspectorGUI()
24+
{
25+
var inspector = base.CreateInspectorGUI();
26+
27+
inspector.Add(new PropertyField(serializedObject.FindProperty(resetDelay)));
28+
inspector.Add(new PropertyField(serializedObject.FindProperty(resetPose)));
29+
30+
return inspector;
31+
}
32+
}
33+
}

Editor/Inspectors/AutoResetBehaviourInspector.cs.meta

+11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Editor/Inspectors/BaseInteractionBehaviourInspector.cs

+12-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@ public abstract class BaseInteractionBehaviourInspector : UnityEditor.Editor
1111
private const string sortingOrderBindingPath = "sortingOrder";
1212
private const string targetHandednessBindingPath = "targetHandedness";
1313

14+
/// <summary>
15+
/// Used internally by some <see cref="BaseInteractionBehaviourInspector"/>
16+
/// implementations to hide the handedness field from the inspector, likely because that
17+
/// behaviour works without that setting impacting it.
18+
/// </summary>
19+
protected virtual bool ShowHandedness => true;
20+
1421
/// <summary>
1522
/// <inheritdoc/>
1623
/// </summary>
@@ -19,7 +26,11 @@ public override VisualElement CreateInspectorGUI()
1926
var inspector = new VisualElement();
2027

2128
inspector.Add(new PropertyField(serializedObject.FindProperty(sortingOrderBindingPath)));
22-
inspector.Add(new PropertyField(serializedObject.FindProperty(targetHandednessBindingPath)));
29+
30+
if (ShowHandedness)
31+
{
32+
inspector.Add(new PropertyField(serializedObject.FindProperty(targetHandednessBindingPath)));
33+
}
2334

2435
return inspector;
2536
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright (c) Reality Collective. All rights reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
using RealityToolkit.Editor.Utilities;
5+
using RealityToolkit.Input.InteractionBehaviours;
6+
using UnityEditor;
7+
using UnityEditor.UIElements;
8+
using UnityEngine.UIElements;
9+
10+
namespace RealityToolkit.Editor.Inspectors
11+
{
12+
[CustomEditor(typeof(InteractionEventsBehaviour), true)]
13+
public class InteractionEventsBehaviourInspector : BaseInteractionBehaviourInspector
14+
{
15+
/// <summary>
16+
/// The events behaviour does not need to display handedness as it will and should
17+
/// forward events for any handedness and leave it to the user to decide what to do with it.
18+
/// </summary>
19+
protected override bool ShowHandedness => false;
20+
21+
public override VisualElement CreateInspectorGUI()
22+
{
23+
var inspector = base.CreateInspectorGUI();
24+
25+
inspector.Add(new PropertyField(serializedObject.FindProperty("firstFocusEntered")));
26+
inspector.Add(new PropertyField(serializedObject.FindProperty("focusEntered")));
27+
inspector.Add(new PropertyField(serializedObject.FindProperty("focusExited")));
28+
inspector.Add(new PropertyField(serializedObject.FindProperty("lastFocusExited")));
29+
30+
inspector.Add(UIElementsUtilities.VerticalSpace());
31+
inspector.Add(new PropertyField(serializedObject.FindProperty("firstSelectEntered")));
32+
inspector.Add(new PropertyField(serializedObject.FindProperty("selectEntered")));
33+
inspector.Add(new PropertyField(serializedObject.FindProperty("selectExited")));
34+
inspector.Add(new PropertyField(serializedObject.FindProperty("lastSelectExited")));
35+
36+
inspector.Add(UIElementsUtilities.VerticalSpace());
37+
inspector.Add(new PropertyField(serializedObject.FindProperty("firstGrabEntered")));
38+
inspector.Add(new PropertyField(serializedObject.FindProperty("grabEntered")));
39+
inspector.Add(new PropertyField(serializedObject.FindProperty("grabExited")));
40+
inspector.Add(new PropertyField(serializedObject.FindProperty("lastGrabExited")));
41+
42+
inspector.Add(UIElementsUtilities.VerticalSpace());
43+
inspector.Add(new PropertyField(serializedObject.FindProperty("reset")));
44+
45+
return inspector;
46+
}
47+
}
48+
}

Editor/Inspectors/InteractionEventsBehaviourInspector.cs.meta

+11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Runtime/Input/Interactables/IInteractable.cs

+6
Original file line numberDiff line numberDiff line change
@@ -68,5 +68,11 @@ public interface IInteractable
6868
/// </summary>
6969
/// <param name="behaviour">The <see cref="IInteractionBehaviour"/>.</param>
7070
void Remove(IInteractionBehaviour behaviour);
71+
72+
/// <summary>
73+
/// Resets the <see cref="IInteractable"/> and notifies any attached <see cref="IInteractionBehaviour"/>s
74+
/// about it using the <see cref="IInteractionBehaviour.OnInteractableReset"/> hook.
75+
/// </summary>
76+
void ResetInteractable();
7177
}
7278
}

Runtime/Input/Interactables/Interactable.cs

+9
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,15 @@ public void Add(IInteractionBehaviour behaviour)
379379
/// <inheritdoc/>
380380
public void Remove(IInteractionBehaviour behaviour) => behaviours.SafeRemoveListItem(behaviour);
381381

382+
/// <inheritdoc/>
383+
public void ResetInteractable()
384+
{
385+
for (var i = 0; i < behaviours.Count; i++)
386+
{
387+
behaviours[i].OnInteractableReset();
388+
}
389+
}
390+
382391
private bool IsValidInteractor(IInteractor interactor) =>
383392
(interactor.IsFarInteractor && FarInteractionEnabled) || (!interactor.IsFarInteractor && DirectInteractionEnabled);
384393

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
// Copyright (c) Reality Collective. All rights reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
using RealityToolkit.Input.Events;
5+
using UnityEngine;
6+
7+
namespace RealityToolkit.Input.InteractionBehaviours
8+
{
9+
/// <summary>
10+
/// This <see cref="IInteractionBehaviour"/> will monitor interactions and once ALL interaction on the interactable has ended it will execute a reset
11+
/// using the <see cref="Interactables.IInteractable.ResetInteractable"/> API.
12+
/// </summary>
13+
[HelpURL(RealityToolkitRuntimePreferences.Toolkit_Docs_BaseUrl + "docs/interactions/interaction-behaviours/default-behaviours/auto-reset-behaviour")]
14+
[AddComponentMenu(RealityToolkitRuntimePreferences.Toolkit_InteractionsAddComponentMenu + "/" + nameof(AutoResetBehaviour))]
15+
public class AutoResetBehaviour : BaseInteractionBehaviour
16+
{
17+
[SerializeField, Tooltip("The delay in seconds after interaction has stopped before the reset will fire.")]
18+
private float resetDelay = 5f;
19+
20+
[SerializeField, Tooltip("If set, the interactable will be reset to its initial pose upon reset.")]
21+
private bool resetPose = true;
22+
23+
private bool didReset;
24+
private float interactionStoppedTime;
25+
private Pose initialPose;
26+
27+
/// <inheritdoc/>
28+
protected override void Awake()
29+
{
30+
initialPose = new Pose(transform.position, transform.rotation);
31+
base.Awake();
32+
}
33+
34+
/// <inheritdoc/>
35+
protected override void OnEnable()
36+
{
37+
didReset = true;
38+
base.OnEnable();
39+
}
40+
41+
/// <inheritdoc/>
42+
protected override void Update()
43+
{
44+
base.Update();
45+
46+
if (didReset)
47+
{
48+
return;
49+
}
50+
51+
var timePassed = Time.time - interactionStoppedTime;
52+
if (timePassed > resetDelay)
53+
{
54+
didReset = true;
55+
Interactable.ResetInteractable();
56+
}
57+
}
58+
59+
/// <inheritdoc/>
60+
protected override void OnDisable()
61+
{
62+
didReset = true;
63+
base.OnDisable();
64+
}
65+
66+
/// <inheritdoc/>
67+
protected override void OnFocusEntered(InteractionEventArgs eventArgs) => OnInteractionDetected();
68+
69+
/// <inheritdoc/>
70+
protected override void OnLastFocusExited(InteractionExitEventArgs eventArgs)
71+
{
72+
if (Interactable.IsSelected || Interactable.IsGrabbed)
73+
{
74+
return;
75+
}
76+
77+
OnInteractionEnded();
78+
}
79+
80+
/// <inheritdoc/>
81+
protected override void OnSelectEntered(InteractionEventArgs eventArgs) => OnInteractionDetected();
82+
83+
/// <inheritdoc/>
84+
protected override void OnLastSelectExited(InteractionExitEventArgs eventArgs)
85+
{
86+
if (Interactable.IsFocused || Interactable.IsGrabbed)
87+
{
88+
return;
89+
}
90+
91+
OnInteractionEnded();
92+
}
93+
94+
/// <inheritdoc/>
95+
protected override void OnGrabEntered(InteractionEventArgs eventArgs) => OnInteractionDetected();
96+
97+
/// <inheritdoc/>
98+
protected override void OnLastGrabExited(InteractionExitEventArgs eventArgs)
99+
{
100+
if (Interactable.IsSelected || Interactable.IsFocused)
101+
{
102+
return;
103+
}
104+
105+
OnInteractionEnded();
106+
}
107+
108+
/// <inheritdoc/>
109+
protected override void OnResetBehaviour()
110+
{
111+
didReset = true;
112+
113+
if (resetPose)
114+
{
115+
ResetPose();
116+
}
117+
}
118+
119+
private void OnInteractionDetected()
120+
{
121+
didReset = true;
122+
}
123+
124+
private void OnInteractionEnded()
125+
{
126+
interactionStoppedTime = Time.time;
127+
didReset = false;
128+
}
129+
130+
private void ResetPose()
131+
{
132+
transform.SetPositionAndRotation(initialPose.position, initialPose.rotation);
133+
}
134+
}
135+
}

Runtime/Input/InteractionBehaviours/AutoResetBehaviour.cs.meta

+11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Runtime/Input/InteractionBehaviours/BaseInteractionBehaviour.cs

+7-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace RealityToolkit.Input.InteractionBehaviours
1212
/// <summary>
1313
/// Base implementation for <see cref="IInteractionBehaviour"/>s.
1414
/// </summary>
15-
[HelpURL("https://www.realitytoolkit.io/docs/interactions/interaction-behaviours/custom-behaviours")]
15+
[HelpURL(RealityToolkitRuntimePreferences.Toolkit_Docs_BaseUrl + "docs/interactions/interaction-behaviours/custom-behaviours")]
1616
[RequireComponent(typeof(Interactable))]
1717
public abstract class BaseInteractionBehaviour : MonoBehaviour, IInteractionBehaviour
1818
{
@@ -74,6 +74,12 @@ protected virtual void OnDestroy() { }
7474
/// </summary>
7575
protected virtual void OnValidate() { }
7676

77+
/// <inheritdoc/>
78+
void IInteractionBehaviour.OnInteractableReset() => OnResetBehaviour();
79+
80+
/// <inheritdoc cref="IInteractionBehaviour.OnInteractableReset"/>
81+
protected virtual void OnResetBehaviour() { }
82+
7783
/// <inheritdoc/>
7884
void IInteractionBehaviour.OnFirstFocusEntered(InteractionEventArgs eventArgs)
7985
{

Runtime/Input/InteractionBehaviours/ButtonBehaviour.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace RealityToolkit.Input.InteractionBehaviours
1111
/// <summary>
1212
/// A <see cref="IInteractionBehaviour"/> for creating <see cref="Interactables.IInteractable"/>s that mimick button behaviour.
1313
/// </summary>
14-
[HelpURL("https://www.realitytoolkit.io/docs/interactions/interaction-behaviours/default-behaviours/button-behaviour")]
14+
[HelpURL(RealityToolkitRuntimePreferences.Toolkit_Docs_BaseUrl + "docs/interactions/interaction-behaviours/default-behaviours/button-behaviour")]
1515
[AddComponentMenu(RealityToolkitRuntimePreferences.Toolkit_InteractionsAddComponentMenu + "/" + nameof(ButtonBehaviour))]
1616
public class ButtonBehaviour : BaseInteractionBehaviour
1717
{

Runtime/Input/InteractionBehaviours/ChangeMaterialBehaviour.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace RealityToolkit.Input.InteractionBehaviours
88
/// <summary>
99
/// This <see cref="IInteractionBehaviour"/> will change the main material used on a <see cref="MeshRenderer"/> on the <see cref="Interactables.IInteractable"/>.
1010
/// </summary>
11-
[HelpURL("https://www.realitytoolkit.io/docs/interactions/interaction-behaviours/default-behaviours/change-material-behaviour")]
11+
[HelpURL(RealityToolkitRuntimePreferences.Toolkit_Docs_BaseUrl + "docs/interactions/interaction-behaviours/default-behaviours/change-material-behaviour")]
1212
[AddComponentMenu(RealityToolkitRuntimePreferences.Toolkit_InteractionsAddComponentMenu + "/" + nameof(ChangeMaterialBehaviour))]
1313
public class ChangeMaterialBehaviour : BaseInteractionBehaviour
1414
{

Runtime/Input/InteractionBehaviours/FocusHandPoseBehaviour.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace RealityToolkit.Input.InteractionBehaviours
1212
/// <summary>
1313
/// The <see cref="FocusHandPoseBehaviour"/> will animate the <see cref="RiggedHandControllerVisualizer"/>
1414
/// into the assigned <see cref="focusPose"/>, when the <see cref="Interactables.IInteractable"/> is focused.
15-
[HelpURL("https://www.realitytoolkit.io/docs/interactions/interaction-behaviours/default-behaviours/focus-hand-pose-behaviour")]
15+
[HelpURL(RealityToolkitRuntimePreferences.Toolkit_Docs_BaseUrl + "docs/interactions/interaction-behaviours/default-behaviours/focus-hand-pose-behaviour")]
1616
[AddComponentMenu(RealityToolkitRuntimePreferences.Toolkit_InteractionsAddComponentMenu + "/" + nameof(FocusHandPoseBehaviour))]
1717
public class FocusHandPoseBehaviour : BaseInteractionBehaviour, IProvideHandPose
1818
{

Runtime/Input/InteractionBehaviours/FocusLockBehaviour.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace RealityToolkit.Input.InteractionBehaviours
1111
/// This <see cref="IInteractionBehaviour"/> will focus lock <see cref="IInteractor"/>s on the <see cref="Interactables.IInteractable"/>,
1212
/// when the <see cref="Interactables.IInteractable.IsSelected"/> or <see cref="Interactables.IInteractable.IsGrabbed"/>.
1313
/// </summary>
14-
[HelpURL("https://www.realitytoolkit.io/docs/interactions/interaction-behaviours/default-behaviours/focus-lock-behaviour")]
14+
[HelpURL(RealityToolkitRuntimePreferences.Toolkit_Docs_BaseUrl + "docs/interactions/interaction-behaviours/default-behaviours/focus-lock-behaviour")]
1515
[AddComponentMenu(RealityToolkitRuntimePreferences.Toolkit_InteractionsAddComponentMenu + "/" + nameof(FocusLockBehaviour))]
1616
public class FocusLockBehaviour : BaseInteractionBehaviour
1717
{

Runtime/Input/InteractionBehaviours/GrabBehaviour.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace RealityToolkit.Input.InteractionBehaviours
1212
/// <see cref="IDirectInteractor"/>s. It allows to "pick up" the <see cref="Interactables.IInteractable"/>
1313
/// and carry it around.
1414
/// </summary>
15-
[HelpURL("https://www.realitytoolkit.io/docs/interactions/interaction-behaviours/default-behaviours/grab-behaviour")]
15+
[HelpURL(RealityToolkitRuntimePreferences.Toolkit_Docs_BaseUrl + "docs/interactions/interaction-behaviours/default-behaviours/grab-behaviour")]
1616
[AddComponentMenu(RealityToolkitRuntimePreferences.Toolkit_InteractionsAddComponentMenu + "/" + nameof(GrabBehaviour))]
1717
public class GrabBehaviour : BaseInteractionBehaviour
1818
{

Runtime/Input/InteractionBehaviours/GrabHandPoseBehaviour.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace RealityToolkit.Input.InteractionBehaviours
1212
/// <summary>
1313
/// The <see cref="GrabHandPoseBehaviour"/> will animate the <see cref="RiggedHandControllerVisualizer"/>
1414
/// into the assigned <see cref="grabPose"/>, when the <see cref="Interactables.IInteractable"/> is grabbed.
15-
[HelpURL("https://www.realitytoolkit.io/docs/interactions/interaction-behaviours/default-behaviours/grab-hand-pose-behaviour")]
15+
[HelpURL(RealityToolkitRuntimePreferences.Toolkit_Docs_BaseUrl + "docs/interactions/interaction-behaviours/default-behaviours/grab-hand-pose-behaviour")]
1616
[AddComponentMenu(RealityToolkitRuntimePreferences.Toolkit_InteractionsAddComponentMenu + "/" + nameof(GrabHandPoseBehaviour))]
1717
public class GrabHandPoseBehaviour : BaseInteractionBehaviour, IProvideHandPose
1818
{

Runtime/Input/InteractionBehaviours/IInteractionBehaviour.cs

+6
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ public interface IInteractionBehaviour
2323
/// </summary>
2424
IInteractable Interactable { get; }
2525

26+
/// <summary>
27+
/// The <see cref="IInteractable"/> was reset. Use this hook to implement any custom reset
28+
/// behaviour your <see cref="IInteractionBehaviour"/> might need to perform when <see cref="IInteractable"/> resets.
29+
/// </summary>
30+
void OnInteractableReset();
31+
2632
#region Focus
2733

2834
/// <summary>

0 commit comments

Comments
 (0)