Search Unity

Other Double Tap Gesture for AR

Discussion in 'XR Interaction Toolkit and Input' started by FernwehSmith, Apr 21, 2022.

  1. FernwehSmith

    FernwehSmith

    Joined:
    Jan 31, 2022
    Posts:
    36
    Hi all,
    I want to make a custom interactable to reset the local transformation for my currently selected object whenever the object is selected and the screen is double tapped. Currently there is no double tap gesture so I'm wondering if anyone can point me in the right direction to get started on creating my own that would work with the XR Interaction Toolkit.

    Specific requirements for what I need to do are:
    1) Recognise when the user double taps the screen
    2) Ensure that the XR Interaction Toolkit does not first make a regular tap gesture
    3) Have XR Interaction Toolkit notify all the registered interactable of this new double tap gesture the same way it would any other gesture

    Not sure where to start with this one. Any help at would be greatly appreciated!
     
  2. FernwehSmith

    FernwehSmith

    Joined:
    Jan 31, 2022
    Posts:
    36
    So just incase anyone was wondering the same thing, I haven't yet figured out how to make an actual gesture but I do have a workaround.

    Basically I have a custom Selection Interactable that stores the time of the previous tap, and if the difference between the current and previous is smaller than a threshold will undo any changes made by the previous tap, and then also fire an event. I have a Reset Transform Interactable that listens for this event and will execute it's logic when triggered.

    This is a very hacky workaround and still a work in progress. My big problem is that it still does something on the first tap, and then just undoes that action if it detects a double tap. This is pretty messy to me so I'm going to try figure out a solution with coroutines or something so that only one action occurs each time.
     
  3. FernwehSmith

    FernwehSmith

    Joined:
    Jan 31, 2022
    Posts:
    36
    Okay so I've got a solution that works well. Could definitely be better for sure but I wanted to post it here in case anyone has the same dilemma and would like a starting point.

    Instead of trying to extend the Gesture Interactor with a new Double Tap Gesture and Recogniser, I have made it the responsibility of the interactable to process the incoming gestures to determine if it is a double tap and what should be done. For Interactables where we want something to happen ON a double tap, I check if the difference between the time of the current tap and previous tap is below a certain threshold.
    bool isDoubleTap = tapTime-lastTapTime < doubleTapDeltaTime; 
    Simple and straightforward.

    When I want to NOT have something happen on a double tap, I instead use a coroutine. I'll share my code bellow but basically when the OnEndManipulation method is called, I check if the coroutine is running. If it is I kill it and return without doing anything. If its not the I start it, and if it finishes before being killed, then I treat that as a single tap and do what ever I need. The coroutine starts by waiting for the maximum period of time we are allowed to provide a second tap for it to be considered a double tap.

    Code (CSharp):
    1. public class ARSelectionInteractable_02 : ARSelectionInteractable
    2. {
    3.  
    4.     bool selectionActive;
    5.     bool isWaiting = false;
    6.  
    7.     Coroutine WaitCoroutine;
    8.  
    9.     public override bool IsSelectableBy(IXRSelectInteractor interactor) => interactor is ARGestureInteractor && selectionActive;
    10.  
    11.  
    12.     protected override bool CanStartManipulationForGesture(TapGesture gesture) => !EventSystem.current.IsPointerOverGameObject(gesture.fingerId);
    13.  
    14.  
    15.     protected override void OnEndManipulation(TapGesture gesture)
    16.     {
    17.         if (gesture.isCanceled)
    18.             return;
    19.         if (gestureInteractor == null)
    20.             return;
    21.  
    22.         if (!isWaiting)
    23.         {
    24.  
    25.             WaitCoroutine = StartCoroutine(WaitForDoubleTap(0.2f, gesture));
    26.         }
    27.         else
    28.         {
    29.             StopCoroutine(WaitCoroutine);
    30.             isWaiting = false;
    31.             Debug.Log("WaitForDoubleTap Coroutine Cancelled");
    32.         }
    33.     }
    34.  
    35.     IEnumerator WaitForDoubleTap(float maxDelta, TapGesture gesture)
    36.     {
    37.         Debug.Log("WaitForDoubleTap Coroutine Started");
    38.         isWaiting = true;
    39.         yield return new WaitForSeconds(maxDelta);
    40.         isWaiting = false;
    41.  
    42.         if (gesture.targetObject == gameObject)
    43.         {
    44.             // Toggle selection
    45.             selectionActive = !selectionActive;
    46.         }
    47.         else
    48.             selectionActive = false;
    49.  
    50.         Debug.Log("WaitForDoubleTap Coroutine Ended");
    51.     }
    52. }
    53.  
    Again this could probably be much better but its here for anyone who may need it :)
     
    solothinker and pankao like this.
  4. pankao

    pankao

    Joined:
    Feb 18, 2013
    Posts:
    18
    Hey, thanks dude I was trying to wrap my head about the very same issue recently, thanks again for your wit to get things done, I am grateful as I already got some better insight just by reading your thoughts;)
     
  5. pankao

    pankao

    Joined:
    Feb 18, 2013
    Posts:
    18
    Creating custom gestures seems pretty undocumented really