Skip to content

Commit 52225b5

Browse files
authored
Merge pull request #196 from micthom-msft/master
Adding support for hand based object manipulation
2 parents a47f3b1 + 3549fa0 commit 52225b5

File tree

6 files changed

+805
-20
lines changed

6 files changed

+805
-20
lines changed

Assets/HoloToolkit/Input/README.md

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,24 @@ Stabilize the user's gaze to account for head jitter.
7272
**StabilityVarianceWeight** Stability variance weight multiplier factor.
7373

7474
#### GestureManager.cs
75-
GestureManager creates a gesture recognizer and signs up for a tap gesture. When a tap gesture is detected, GestureManager uses GazeManager to find the game object.
76-
GestureManager then sends a message to that game object.
75+
GestureManager provides access to several different input gestures, including Tap and Manipulation.
7776

78-
It also has an **OverrideFocusedObject** which lets you send gesture input to a specific object by overriding the gaze.
77+
When a tap gesture is detected, GestureManager uses GazeManager to find the game object.
78+
GestureManager then sends a message to that game object. It also has an **OverrideFocusedObject** which lets you send gesture input to a specific object by overriding the gaze.
79+
80+
Using Manipulation requires subscribing to the ManipulationStarted events and then querying information about the manipulation gesture via ManipulationOffset and ManipulationHandPosition. See GestureManipulator for an example.
81+
82+
#### GestureManipulator.cs
83+
A component for moving an object via the GestureManager manipulation gesture.
84+
85+
When an active GestureManipulator component is attached to a GameObject it will subscribe
86+
to GestureManager's manipulation gestures, and move the GameObject when a ManipulationGesture occurs.
87+
If the GestureManipulator is disabled it will not respond to any manipulation gestures.
88+
89+
This means that if multiple GestureManipulators are active in a given scene when a manipulation
90+
gesture is performed, all the relevant GameObjects will be moved. If the desired behavior is that only
91+
a single object be moved at a time, it is recommended that objects which should not be moved disable
92+
their GestureManipulators, then re-enable them when necessary (e.g. the object is focused).
7993

8094
#### HandGuidance.cs
8195
Show a GameObject when a gesturing hand is about to lose tracking.

Assets/HoloToolkit/Input/Scripts/GestureManager.cs

Lines changed: 165 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,40 @@
33

44
using UnityEngine;
55
using UnityEngine.VR.WSA.Input;
6+
using System.Collections.Generic;
67

78
namespace HoloToolkit.Unity
89
{
910
/// <summary>
10-
/// GestureManager creates a gesture recognizer and signs up for a tap gesture.
11+
/// GestureManager provides access to several different input gestures, including
12+
/// Tap and Manipulation.
13+
/// </summary>
14+
/// <remarks>
1115
/// When a tap gesture is detected, GestureManager uses GazeManager to find the game object.
1216
/// GestureManager then sends a message to that game object.
13-
/// </summary>
17+
///
18+
/// Using Manipulation requires subscribing the the ManipulationStarted events and then querying
19+
/// information about the manipulation gesture via ManipulationOffset and ManipulationHandPosition
20+
/// </remarks>
1421
[RequireComponent(typeof(GazeManager))]
1522
public partial class GestureManager : Singleton<GestureManager>
1623
{
24+
/// <summary>
25+
/// Occurs when a manipulation gesture has started
26+
/// </summary>
27+
public System.Action ManipulationStarted;
28+
29+
/// <summary>
30+
/// Occurs when a manipulation gesture ended as a result of user input
31+
/// </summary>
32+
public System.Action ManipulationCompleted;
33+
34+
/// <summary>
35+
/// Occurs when a manipulated gesture ended as a result of some other condition.
36+
/// (e.g. the hand being used for the gesture is no longer visible).
37+
/// </summary>
38+
public System.Action ManipulationCanceled;
39+
1740
/// <summary>
1841
/// Key to press in the editor to select the currently gazed hologram
1942
/// </summary>
@@ -24,66 +47,180 @@ public partial class GestureManager : Singleton<GestureManager>
2447
/// set the override focused object.
2548
/// If its null, then the gazed at object will be selected.
2649
/// </summary>
27-
public GameObject OverrideFocusedObject
28-
{
29-
get; set;
30-
}
31-
50+
public GameObject OverrideFocusedObject { get; set; }
51+
3252
/// <summary>
3353
/// Gets the currently focused object, or null if none.
3454
/// </summary>
35-
public GameObject FocusedObject
55+
public GameObject FocusedObject { get; private set; }
56+
57+
/// <summary>
58+
/// Whether or not a manipulation gesture is currently in progress
59+
/// </summary>
60+
public bool ManipulationInProgress { get; private set; }
61+
62+
/// <summary>
63+
/// The offset of the hand from its position at the beginning of
64+
/// the currently active manipulation gesture, in world space. Not valid if
65+
/// a manipulation gesture is not in progress
66+
/// </summary>
67+
public Vector3 ManipulationOffset { get; private set; }
68+
69+
/// <summary>
70+
/// The world space position of the hand being used for the current manipulation gesture. Not valid
71+
/// if a manipulation gesture is not in progress.
72+
/// </summary>
73+
public Vector3 ManipulationHandPosition
3674
{
37-
get { return focusedObject; }
75+
get
76+
{
77+
Vector3 handPosition = Vector3.zero;
78+
currentHandState.properties.location.TryGetPosition(out handPosition);
79+
return handPosition;
80+
}
3881
}
3982

4083
private GestureRecognizer gestureRecognizer;
41-
private GameObject focusedObject;
84+
// We use a separate manipulation recognizer here because the tap gesture recognizer cancels
85+
// capturing gestures whenever the GazeManager focus changes, which is not the behavior
86+
// we want for manipulation
87+
private GestureRecognizer manipulationRecognizer;
88+
89+
private bool HandPressed { get { return pressedHands.Count > 0; } }
90+
private HashSet<uint> pressedHands = new HashSet<uint>();
91+
92+
private InteractionSourceState currentHandState;
4293

4394
void Start()
4495
{
96+
InteractionManager.SourcePressed += InteractionManager_SourcePressed;
97+
InteractionManager.SourceReleased += InteractionManager_SourceReleased;
98+
InteractionManager.SourceUpdated += InteractionManager_SourceUpdated;
99+
InteractionManager.SourceLost += InteractionManager_SourceLost;
100+
45101
// Create a new GestureRecognizer. Sign up for tapped events.
46102
gestureRecognizer = new GestureRecognizer();
47103
gestureRecognizer.SetRecognizableGestures(GestureSettings.Tap);
48104

105+
manipulationRecognizer = new GestureRecognizer();
106+
manipulationRecognizer.SetRecognizableGestures(GestureSettings.ManipulationTranslate);
107+
49108
gestureRecognizer.TappedEvent += GestureRecognizer_TappedEvent;
50109

110+
manipulationRecognizer.ManipulationStartedEvent += ManipulationRecognizer_ManipulationStartedEvent;
111+
manipulationRecognizer.ManipulationUpdatedEvent += ManipulationRecognizer_ManipulationUpdatedEvent;
112+
manipulationRecognizer.ManipulationCompletedEvent += ManipulationRecognizer_ManipulationCompletedEvent;
113+
manipulationRecognizer.ManipulationCanceledEvent += ManipulationRecognizer_ManipulationCanceledEvent;
114+
51115
// Start looking for gestures.
52116
gestureRecognizer.StartCapturingGestures();
117+
manipulationRecognizer.StartCapturingGestures();
53118
}
54119

55-
private void OnTap()
120+
121+
122+
private void InteractionManager_SourcePressed(InteractionSourceState state)
123+
{
124+
if (!HandPressed)
125+
{
126+
currentHandState = state;
127+
}
128+
129+
pressedHands.Add(state.source.id);
130+
}
131+
132+
private void InteractionManager_SourceUpdated(InteractionSourceState state)
56133
{
57-
if (focusedObject != null)
134+
if (HandPressed && state.source.id == currentHandState.source.id)
58135
{
59-
focusedObject.SendMessage("OnSelect");
136+
currentHandState = state;
60137
}
61138
}
62139

140+
private void InteractionManager_SourceReleased(InteractionSourceState state)
141+
{
142+
pressedHands.Remove(state.source.id);
143+
}
144+
145+
private void InteractionManager_SourceLost(InteractionSourceState state)
146+
{
147+
pressedHands.Remove(state.source.id);
148+
}
149+
63150
private void GestureRecognizer_TappedEvent(InteractionSourceKind source, int tapCount, Ray headRay)
64151
{
65152
OnTap();
66153
}
67154

155+
private void OnTap()
156+
{
157+
if (FocusedObject != null)
158+
{
159+
FocusedObject.SendMessage("OnSelect");
160+
}
161+
}
162+
163+
private void ManipulationRecognizer_ManipulationStartedEvent(InteractionSourceKind source, Vector3 cumulativeDelta, Ray headRay)
164+
{
165+
// Don't start another manipulation gesture if one is already underway
166+
if (!ManipulationInProgress)
167+
{
168+
OnManipulation(true, cumulativeDelta);
169+
if (ManipulationStarted != null)
170+
{
171+
ManipulationStarted();
172+
}
173+
}
174+
}
175+
176+
private void ManipulationRecognizer_ManipulationUpdatedEvent(InteractionSourceKind source, Vector3 cumulativeDelta, Ray headRay)
177+
{
178+
OnManipulation(true, cumulativeDelta);
179+
}
180+
181+
private void ManipulationRecognizer_ManipulationCompletedEvent(InteractionSourceKind source, Vector3 cumulativeDelta, Ray headRay)
182+
{
183+
OnManipulation(false, cumulativeDelta);
184+
if (ManipulationCompleted != null)
185+
{
186+
ManipulationCompleted();
187+
}
188+
}
189+
190+
private void ManipulationRecognizer_ManipulationCanceledEvent(InteractionSourceKind source, Vector3 cumulativeDelta, Ray headRay)
191+
{
192+
OnManipulation(false, cumulativeDelta);
193+
if (ManipulationCanceled != null)
194+
{
195+
ManipulationCanceled();
196+
}
197+
}
198+
199+
private void OnManipulation(bool inProgress, Vector3 offset)
200+
{
201+
ManipulationInProgress = inProgress;
202+
ManipulationOffset = offset;
203+
}
204+
68205
void LateUpdate()
69206
{
70-
GameObject oldFocusedObject = focusedObject;
207+
GameObject oldFocusedObject = FocusedObject;
71208

72209
if (GazeManager.Instance.Hit &&
73210
OverrideFocusedObject == null &&
74211
GazeManager.Instance.HitInfo.collider != null)
75212
{
76213
// If gaze hits a hologram, set the focused object to that game object.
77214
// Also if the caller has not decided to override the focused object.
78-
focusedObject = GazeManager.Instance.HitInfo.collider.gameObject;
215+
FocusedObject = GazeManager.Instance.HitInfo.collider.gameObject;
79216
}
80217
else
81218
{
82219
// If our gaze doesn't hit a hologram, set the focused object to null or override focused object.
83-
focusedObject = OverrideFocusedObject;
220+
FocusedObject = OverrideFocusedObject;
84221
}
85222

86-
if (focusedObject != oldFocusedObject)
223+
if (FocusedObject != oldFocusedObject)
87224
{
88225
// If the currently focused object doesn't match the old focused object, cancel the current gesture.
89226
// Start looking for new gestures. This is to prevent applying gestures from one hologram to another.
@@ -103,6 +240,17 @@ void OnDestroy()
103240
{
104241
gestureRecognizer.StopCapturingGestures();
105242
gestureRecognizer.TappedEvent -= GestureRecognizer_TappedEvent;
243+
244+
manipulationRecognizer.StopCapturingGestures();
245+
manipulationRecognizer.ManipulationStartedEvent -= ManipulationRecognizer_ManipulationStartedEvent;
246+
manipulationRecognizer.ManipulationUpdatedEvent -= ManipulationRecognizer_ManipulationUpdatedEvent;
247+
manipulationRecognizer.ManipulationCompletedEvent -= ManipulationRecognizer_ManipulationCompletedEvent;
248+
manipulationRecognizer.ManipulationCanceledEvent -= ManipulationRecognizer_ManipulationCanceledEvent;
249+
250+
InteractionManager.SourcePressed -= InteractionManager_SourcePressed;
251+
InteractionManager.SourceReleased -= InteractionManager_SourceReleased;
252+
InteractionManager.SourceUpdated -= InteractionManager_SourceUpdated;
253+
InteractionManager.SourceLost -= InteractionManager_SourceLost;
106254
}
107255
}
108256
}

0 commit comments

Comments
 (0)