@@ -29,6 +29,9 @@ public EditorHandData(uint handId)
2929 IsFingerDownPending = false ;
3030 FingerStateChanged = false ;
3131 FingerStateUpdateTimer = - 1 ;
32+ ManipulationInProgress = false ;
33+ HoldInProgress = false ;
34+ CumulativeDelta = Vector3 . zero ;
3235 }
3336
3437 public readonly uint HandId ;
@@ -38,36 +41,39 @@ public EditorHandData(uint handId)
3841 public bool IsFingerDownPending ;
3942 public bool FingerStateChanged ;
4043 public float FingerStateUpdateTimer ;
41- public float FingerDownStartTime ;
44+ public float FingerDownStartTime ;
45+ public bool ManipulationInProgress ;
46+ public bool HoldInProgress ;
47+ public Vector3 CumulativeDelta ;
4248 }
4349
4450 private ManualHandControl manualHandControl ;
4551
4652 /// <summary>
47- /// Dispatched each frame that a hand is moving
53+ /// Dispatched each frame that a hand is moving.
4854 /// </summary>
4955 public event Action < IInputSource , uint > HandMoved ;
5056
5157 /// <summary>
5258 /// Delay before a finger pressed is considered.
5359 /// This mitigates fake finger taps that can sometimes be detected while the hand is moving.
5460 /// </summary>
55- private const float FingerPressDelay = 0.07f ;
61+ private const float FINGER_PRESS_DELAY = 0.07f ;
5662
5763 /// <summary>
5864 /// The maximum interval between button down and button up that will result in a clicked event.
5965 /// </summary>
60- private const float MaxClickDuration = 0.5f ;
66+ private const float MAX_CLICK_DURATION = 0.5f ;
6167
6268 /// <summary>
6369 /// Number of fake hands supported in the editor.
6470 /// </summary>
65- private const int EditorHandsCount = 2 ;
71+ private const int EDITOR_HANDS_COUNT = 2 ;
6672
6773 /// <summary>
6874 /// Array containing the hands data for the two fake hands
6975 /// </summary>
70- private readonly EditorHandData [ ] editorHandsData = new EditorHandData [ EditorHandsCount ] ;
76+ private readonly EditorHandData [ ] editorHandsData = new EditorHandData [ EDITOR_HANDS_COUNT ] ;
7177
7278 /// <summary>
7379 /// Dictionary linking each hand ID to its data.
@@ -79,6 +85,10 @@ public EditorHandData(uint handId)
7985 private readonly HashSet < uint > currentHands = new HashSet < uint > ( ) ;
8086 private readonly HashSet < uint > newHands = new HashSet < uint > ( ) ;
8187
88+ [ SerializeField ]
89+ [ Tooltip ( "The total amount of hand movement that needs to happen to signal intent to start a manipulation. This is a distance, but not a distance in any one direction." ) ]
90+ private float manipulationStartMovementThreshold = 0.03f ;
91+
8292 public override SupportedInputInfo GetSupportedInputInfo ( uint sourceId )
8393 {
8494 return SupportedInputInfo . Position ;
@@ -112,55 +122,55 @@ public Vector3 GetHandDelta(uint handId)
112122 {
113123 if ( handId >= editorHandsData . Length )
114124 {
115- string message = string . Format ( "GetHandDelta called with invalid hand ID {0}." , handId ) ;
125+ string message = string . Format ( "GetHandDelta called with invalid hand ID {0}." , handId . ToString ( ) ) ;
116126 throw new ArgumentException ( message , "handId" ) ;
117127 }
118128
119129 return editorHandsData [ handId ] . HandDelta ;
120130 }
121131
122132 /// <summary>
123- /// Gets the pressed state of the specified hand
133+ /// Gets the pressed state of the specified hand.
124134 /// </summary>
125135 /// <param name="handId">ID of the hand to get.</param>
126136 /// <returns>True if the specified hand is currently in an airtap.</returns>
127137 public bool GetFingerState ( uint handId )
128138 {
129139 if ( handId >= editorHandsData . Length )
130140 {
131- var message = string . Format ( "GetFingerState called with invalid hand ID {0}." , handId ) ;
141+ var message = string . Format ( "GetFingerState called with invalid hand ID {0}." , handId . ToString ( ) ) ;
132142 throw new ArgumentException ( message , "handId" ) ;
133143 }
134144
135145 return editorHandsData [ handId ] . IsFingerDown ;
136146 }
137147
138148 /// <summary>
139- /// Gets whether the specified hand just started an airtap this frame
149+ /// Gets whether the specified hand just started an airtap this frame.
140150 /// </summary>
141151 /// <param name="handId">ID of the hand to get.</param>
142152 /// <returns>True for the first frame of an airtap</returns>
143153 public bool GetFingerDown ( uint handId )
144154 {
145155 if ( handId >= editorHandsData . Length )
146156 {
147- var message = string . Format ( "GetFingerDown called with invalid hand ID {0}." , handId ) ;
157+ var message = string . Format ( "GetFingerDown called with invalid hand ID {0}." , handId . ToString ( ) ) ;
148158 throw new ArgumentException ( message , "handId" ) ;
149159 }
150160
151161 return editorHandsData [ handId ] . IsFingerDown && editorHandsData [ handId ] . FingerStateChanged ;
152162 }
153163
154164 /// <summary>
155- /// Gets whether the specified hand just ended an airtap this frame
165+ /// Gets whether the specified hand just ended an airtap this frame.
156166 /// </summary>
157167 /// <param name="handId">ID of the hand to get.</param>
158168 /// <returns>True for the first frame of the release of an airtap</returns>
159169 public bool GetFingerUp ( uint handId )
160170 {
161171 if ( handId >= editorHandsData . Length )
162172 {
163- var message = string . Format ( "GetFingerUp called with invalid hand ID {0}." , handId ) ;
173+ var message = string . Format ( "GetFingerUp called with invalid hand ID {0}." , handId . ToString ( ) ) ;
164174 throw new ArgumentException ( message , "handId" ) ;
165175 }
166176
@@ -171,12 +181,13 @@ private void Awake()
171181 {
172182#if ! UNITY_EDITOR
173183 Destroy ( this ) ;
174- #endif
184+ #else
175185 manualHandControl = GetComponent < ManualHandControl > ( ) ;
176186 for ( uint i = 0 ; i < editorHandsData . Length ; i ++ )
177187 {
178188 editorHandsData [ i ] = new EditorHandData ( i ) ;
179189 }
190+ #endif
180191 }
181192
182193#if UNITY_EDITOR
@@ -249,6 +260,8 @@ private EditorHandData GetOrAddHandData(uint sourceId)
249260 /// </summary>
250261 /// <param name="handSource">Hand source to use to update the position.</param>
251262 /// <param name="editorHandData">EditorHandData structure to update.</param>
263+ /// <param name="deltaTime">Unscaled delta time of last event.</param>
264+ /// <param name="time">Unscaled time of last event.</param>
252265 private void UpdateHandState ( DebugInteractionSourceState handSource , EditorHandData editorHandData , float deltaTime , float time )
253266 {
254267 // Update hand position
@@ -263,7 +276,7 @@ private void UpdateHandState(DebugInteractionSourceState handSource, EditorHandD
263276 if ( handSource . Pressed != editorHandData . IsFingerDownPending )
264277 {
265278 editorHandData . IsFingerDownPending = handSource . Pressed ;
266- editorHandData . FingerStateUpdateTimer = FingerPressDelay ;
279+ editorHandData . FingerStateUpdateTimer = FINGER_PRESS_DELAY ;
267280 }
268281
269282 // Finger presses are delayed to mitigate issue with hand position shifting during air tap
@@ -289,31 +302,79 @@ private void UpdateHandState(DebugInteractionSourceState handSource, EditorHandD
289302 /// Sends the events for hand state changes.
290303 /// </summary>
291304 /// <param name="editorHandData">Hand data for which events should be sent.</param>
305+ /// <param name="time">Unscaled time of last event.</param>
292306 private void SendHandStateEvents ( EditorHandData editorHandData , float time )
293307 {
294- // Hand moved event
308+ // Hand moved event.
295309 if ( editorHandData . HandDelta . sqrMagnitude > 0 )
296310 {
297311 HandMoved . RaiseEvent ( this , editorHandData . HandId ) ;
298312 }
299313
300- // Finger pressed/released events
314+ // If the finger state has just changed to be down vs up.
301315 if ( editorHandData . FingerStateChanged )
302316 {
317+ // New down presses are straightforward - fire input down and be on your way.
303318 if ( editorHandData . IsFingerDown )
304319 {
305- inputManager . RaiseSourceDown ( this , editorHandData . HandId ) ;
320+ InputManager . Instance . RaiseSourceDown ( this , editorHandData . HandId ) ;
321+ editorHandData . CumulativeDelta = Vector3 . zero ;
306322 }
323+ // New up presses require sending different events depending on whether it's also a click, hold, or manipulation.
307324 else
308325 {
309- inputManager . RaiseSourceUp ( this , editorHandData . HandId ) ;
326+ // A gesture is always either a click, a hold or a manipulation.
327+ if ( editorHandData . ManipulationInProgress )
328+ {
329+ InputManager . Instance . RaiseManipulationCompleted ( this , editorHandData . HandId , editorHandData . CumulativeDelta ) ;
330+ editorHandData . ManipulationInProgress = false ;
331+ }
332+ // Clicks and holds are based on time, and both are overruled by manipulations.
333+ else if ( editorHandData . HoldInProgress )
334+ {
335+ InputManager . Instance . RaiseHoldCompleted ( this , editorHandData . HandId ) ;
336+ editorHandData . HoldInProgress = false ;
337+ }
338+ else
339+ {
340+ // We currently only support single taps in editor.
341+ InputManager . Instance . RaiseInputClicked ( this , editorHandData . HandId , 1 ) ;
342+ }
343+
344+ InputManager . Instance . RaiseSourceUp ( this , editorHandData . HandId ) ;
345+ }
346+ }
347+ // If the finger state hasn't changed, and the finger is down, that means if calculations need to be done
348+ // nothing might change, or it might trigger a hold or a manipulation (or a hold and then a manipulation).
349+ else if ( editorHandData . IsFingerDown )
350+ {
351+ editorHandData . CumulativeDelta += editorHandData . HandDelta ;
310352
311- // Also send click event when using this hands replacement input
312- if ( time - editorHandData . FingerDownStartTime < MaxClickDuration )
353+ if ( ! editorHandData . ManipulationInProgress )
354+ {
355+ // Manipulations are triggered by the amount of movement since the finger was pressed down.
356+ if ( editorHandData . CumulativeDelta . magnitude > manipulationStartMovementThreshold )
357+ {
358+ // Starting a manipulation will cancel an existing hold.
359+ if ( editorHandData . HoldInProgress )
360+ {
361+ InputManager . Instance . RaiseHoldCanceled ( this , editorHandData . HandId ) ;
362+ editorHandData . HoldInProgress = false ;
363+ }
364+
365+ InputManager . Instance . RaiseManipulationStarted ( this , editorHandData . HandId , editorHandData . CumulativeDelta ) ;
366+ editorHandData . ManipulationInProgress = true ;
367+ }
368+ // Holds are triggered by time.
369+ else if ( ! editorHandData . HoldInProgress && ( time - editorHandData . FingerDownStartTime >= MAX_CLICK_DURATION ) )
313370 {
314- // We currently only support single taps in editor
315- inputManager . RaiseInputClicked ( this , editorHandData . HandId , 1 ) ;
316- }
371+ InputManager . Instance . RaiseHoldStarted ( this , editorHandData . HandId ) ;
372+ editorHandData . HoldInProgress = true ;
373+ }
374+ }
375+ else
376+ {
377+ InputManager . Instance . RaiseManipulationUpdated ( this , editorHandData . HandId , editorHandData . CumulativeDelta ) ;
317378 }
318379 }
319380 }
@@ -326,7 +387,7 @@ private void SendHandVisibilityEvents()
326387 // Send event for new hands that were added
327388 foreach ( uint newHand in newHands )
328389 {
329- inputManager . RaiseSourceDetected ( this , newHand ) ;
390+ InputManager . Instance . RaiseSourceDetected ( this , newHand ) ;
330391 }
331392
332393 // Send event for hands that are no longer visible and remove them from our dictionary
@@ -335,7 +396,7 @@ private void SendHandVisibilityEvents()
335396 if ( ! currentHands . Contains ( existingHand ) )
336397 {
337398 pendingHandIdDeletes . Add ( existingHand ) ;
338- inputManager . RaiseSourceLost ( this , existingHand ) ;
399+ InputManager . Instance . RaiseSourceLost ( this , existingHand ) ;
339400 }
340401 }
341402
@@ -347,4 +408,4 @@ private void SendHandVisibilityEvents()
347408 pendingHandIdDeletes . Clear ( ) ;
348409 }
349410 }
350- }
411+ }
0 commit comments