Swipe and Tap detection

Monkey Targets Forums/iOS/Swipe and Tap detection

Grey Alien(Posted 2013) [#1]
Hi all, I just made a class to handle swipe and tap detection but I'm wondering if I've gone about this the best way: a swipe is based on distance traveled on finger up). Perhaps instead I should detect speed of swipe and check for a minimum distance traveled (with finger still down).

Also current my tap is based on finger up but it would be nice to maybe have that on finger down and not have it confused with a swipe, but I don't think this doable in a decent way. For example I could say that if it's down for a couple of frames (or down up rapidly) that is a tap, but someone might hold it down for a bit at the start of a swipe and the code would think it was a tap.

How are you handling this in your games? Are there any links to best practices for this type of thing? (like in Apple docs) I've been searching for a while but not found anything decent. Thx.

'/////////////////////////////////////////////////
'TGestureControl
'/////////////////////////////////////////////////
Class TGestureControl
	Const SWIPE_NONE:Int = 0
	Const SWIPE_UP:Int = 1
	Const SWIPE_DOWN:Int = 2
	Const SWIPE_LEFT:Int = 3
	Const SWIPE_RIGHT:Int = 4
	Const SWIPE_TAP:Int = 5
	
	Field downCounter:Int = 0
	Field minDistance:Int = 100
	Field swipeType:Int = SWIPE_NONE
	Field tapMaxDistance:Int = 80 'measured diagonally
	Field tapMaxTime:Int = 30 'frames	
	Field touchStartX:Float = -1
	Field touchStartY:Float = -1
	
	Method GetSwipe:Int()
		'Return and clear the swipe type
		Local temp:Int = swipeType
		swipeType = SWIPE_NONE
		Return temp
	End Method
	
	Method Reset:Void()
		touchStartX = -1
		touchStartY = -1	
	End Method

	Method Update:Void()
		If myGame.touchDown[0] Then
			'Has the finger just been placed down?
			If touchStartX = -1 And touchStartY = -1 Then
				touchStartX = VTouchX(0)
				touchStartY = VTouchY(0)
				downCounter = 0
			Else
				downCounter += 1		
			Endif
		Else
			If touchStartX <> -1 And touchStartY <> -1 Then				
				'Has it moved far enough to be a swipe yet?
				Local newX:Float = VTouchX(0)
				Local newY:Float = VTouchY(0)
				Local diffX:Float = newX-touchStartX
				Local diffY:Float = newY-touchStartY
					
				If diffX>0 And diffX>minDistance And diffX>Abs(diffY) Then
					swipeType = SWIPE_RIGHT
					Reset()
				Elseif diffX<0 And diffX<-minDistance And Abs(diffX)>Abs(diffY) Then
					swipeType = SWIPE_LEFT
					Reset()
				Elseif diffY>0 And diffY>minDistance And diffY>Abs(diffX) Then
					swipeType = SWIPE_DOWN
					Reset()
				Elseif diffY<0 And diffY<-minDistance And Abs(diffY)>Abs(diffX) Then
					swipeType = SWIPE_UP
					Reset()
				Else
					'Is the distance small enough that we can call it a tap?
					If math.Sqrt(diffX*diffX+diffY*diffY)<=tapMaxDistance And downCounter<=tapMaxTime Then
						swipeType = SWIPE_TAP
					Else
						swipeType = SWIPE_NONE
					Endif
					Reset()
				Endif				
			Endif
		Endif	
	End Method
	
End Class




DGuy(Posted 2013) [#2]
I've always taken the approach of first getting the touch-sequence start/end info & then interpreting it.

A Touch Sequence ...
STARTS   : When TouchDown(0), and only TouchDown(0), becomes True
ENDS     : When TouchDown(0) becomes False & a touch-sequence is in progress
IS VOIDED: If TouchDown(n), where n >= 1, becomes True while a touch-sequence is in progress


Touch Sequence Info:
touchStartX, touchStartY, touchStartTime
touchEndX, touchEndY, touchEndTime


To interpret the touch sequence:
_MAX_TAP_DELTA_TIME	'The max time allowed between start & end to be considered a SINGLE-TAP (anything longer is a HOLD event)
_MAX_TAP_DELTA_DIST	'The max distance allowed between start & end to be considered a SINGLE-TAP

_MAX_SWIPE_DELTA_TIME	'The max time allowed between start & end to still be considered a SWIPE (anything longer is a DRAG)
_MIN_SWIPE_DELTA_DIST	'The min distance required between start & end for consideration as a SWIPE (must be larger than _MAX_TAP_DELTA_DIST)



Some pseudo-code:
for t = 0 until 5
  if TouchDown( t ) touch-count+=1
end

select touch-count
case 1
  if TouchDown( 0 )
    if not touch-in-progress
      touch-in-progress = True

      touchStartX = TouchX
      touchStartY = TouchY
      touchStartTime = Millisecs

      ' just in case touch-sequence ends before next update
      touchEndX = TouchX
      touchEndY = TouchY
      touchEndTime = Millisecs
    else
      touchEndX = TouchX
      touchEndY = TouchY
      touchEndTime = Millisecs
    end
  else
    touch-in-progress = false
  end

case 0
  if touch-in-progress
    touch-in-progress = False

    delta-x = Abs( touchStartX - touchEndX )
    delta-y = Abs( touchStartY - touchEndY )
    delta-time = Abs( touchStartTime - touchEndTime )

    ' check for single tap
    if ((delta-x <= _MAX_TAP_DELTA_DIST) and (delta-y <= _MAX_TAP_DELTA_DIST)) and (delta-time <= _MAX_TAP_DELTA_TIME)
      ' is a single-tap

    ' check for swipe
    else if ((delta-x >= _MIN_SWIPE_DELTA_DIST) or (delta-y >= _MIN_SWIPE_DELTA_DIST)) and (delta-time <= _MAX_SWIPE_DELTA_TIME)
      if delta-x >= delta-y
        If end-x < start-x
          ' swipe left
        else
          ' swipe right
        end
      else
        if end-y < start-y
          ' swipe up
        else
          ' swipe-down
        end
      end
    end
  end
	
default
  touch-in-progress = false

end


HTH


Grey Alien(Posted 2013) [#3]
Very interesting, thanks for sharing. Looks like you are handling taps the same as me (time and distance limitations) and also adding time to the swipe which I could also do.


fictorial(Posted 2013) [#4]
Hey guys,

Why not use wrapped gesture recognizers?


Grey Alien(Posted 2013) [#5]
Has anyone done that for Monkey yet with a module? I don't want to fart around with Objective C.


DGuy(Posted 2013) [#6]
I've thought of using the iOS/Android gesture recognizes but as I long ago developed code to handle detection of taps/double-taps/drags/swipes/pinches, I kinda' consider it a solved problem and never seriously looked for another solution.