Can't get my swipe method to work correctly

Monkey Forums/Monkey Programming/Can't get my swipe method to work correctly

Tri|Ga|De(Posted 2012) [#1]
I'm trying to make a swipe method but I'm having a little trouble with it.


	'**********************************
	'** Check Swipe
	'**********************************
	Method CheckSwipe:Int()
		Local result:Int = 0
		
		If touching = 0 And TouchDown(0) = 1 Then
			touching = 1
			touchingXstart = TouchX()
			touchingYstart = TouchY()
		End
		If TouchDown(0) = 0  And touching = 1 Then
			touching = 0
			touchingXend = TouchX()
			touchingYend = TouchY()
			
			If touchingXend > touchingXstart Then result = 1
			If touchingXend < touchingXstart Then result = 3
			If touchingYend > touchingYstart Then result = 2
			If touchingYend < touchingYstart Then result = 4
	
			touchingXstart = 0
			touchingXend = 0
			touchingYstart = 0
			touchingYend = 0
		End

		Return result
	End	


The touching variables are defined as Integers.

I then call it like this in OnUpdate.

swipeResult = CheckSwipe()


I want the swipeResult to give this results.

1 = swipe right
2 = swipe down
3 = swipe left
4 = swipe up.

BUT I can't get it working, any one got any idea on how to solve this?


Goodlookinguy(Posted 2012) [#2]
The results are overwriting each other because of the way you setup the if statements.

If start x is less than end x, but then if start y is less than end y as well, the result is 2, even if you were aiming 1.


Tri|Ga|De(Posted 2012) [#3]
Okay tried it differently but still not working.


	'**********************************
	'** Check Swipe
	'**********************************
	Method CheckSwipe:Int()
		Local result:Int = 0
		
		If touching = 0 And TouchDown(0) = 1 Then
			touching = 1
			touchingXstart = TouchX()
			touchingYstart = TouchY()
		End
		If TouchDown(0) = 0  And touching = 1 Then
			touching = 0
			touchingXend = TouchX()
			touchingYend = TouchY()
			
			If touchingXend > touchingXstart Then 
				touchingXstart = 0
				touchingXend = 0
				touchingYstart = 0
				touchingYend = 0
				Return 1
			End	
			If touchingXend < touchingXstart Then 
				touchingXstart = 0
				touchingXend = 0
				touchingYstart = 0
				touchingYend = 0
				result = 3
			End	
			If touchingYend > touchingYstart Then 
				touchingXstart = 0
				touchingXend = 0
				touchingYstart = 0
				touchingYend = 0
				result = 2
			End	
			If touchingYend < touchingYstart Then 
				touchingXstart = 0
				touchingXend = 0
				touchingYstart = 0
				touchingYend = 0
				result = 4
			End
		End

		Return result
	End	



Goodlookinguy(Posted 2012) [#4]
Try this

[monkeycode] Method CheckSwipe:Int()
If Not touching And TouchDown(0) Then
touching = True
touchingXstart = TouchX()
touchingYstart = TouchY()
ElseIf Not TouchDown(0) And touching Then
touching = False
touchingXend = TouchX()
touchingYend = TouchY()

Local xDiff:Int = Max(touchingXend, touchingXstart) - Min(touchingXstart, touchingXend)
Local yDiff:Int = Max(touchingYend, touchingYstart) - Min(touchingYstart, touchingYend)

If xDiff > yDiff
If touchingXend > touchingXstart Then Return 1
If touchingXend < touchingXstart Then Return 3
Else
If touchingYend > touchingYstart Then Return 2
If touchingYend < touchingYstart Then Return 4
End
End

Return 0
End [/monkeycode]


Tri|Ga|De(Posted 2012) [#5]
Sorry but your code doesn't seem to work for me.

I checking like this in OnUpdate

If CheckSwipe() = 1 Then score = 1
If CheckSwipe() = 2 Then score = 2
If CheckSwipe() = 3 Then score = 3
If CheckSwipe() = 4 Then score = 4


I'm drawing the value to screen but I dod not get the correct result.
I only get a 1 return.


Raz(Posted 2012) [#6]
The subsequent calls to CheckSwipe won't work, what do you get if you do the following...

Local checkResult:int = CheckSwipe()
If checkResult > 0
    Print checkResult
End



Tri|Ga|De(Posted 2012) [#7]
Thats it!

Thanks both of you now the swipe function works.


NoOdle(Posted 2012) [#8]
I just quickly knocked this up, it allows for the screen to rotate but still detect up/left/right/down (relative to the rotation). It also has an optional tolerance value, allowing you to fine tune what should be recognised.

Import mojo

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


'/ Screen Angle (allows swiping with rotation)
Global AxisAngle : Float = 0.0




Class MyApp Extends App


	Field touchStart : Int[ 2 ]
	Field touchEnd : Int[ 2 ]
	Field touching : Bool
	
	
	Method OnCreate()
		SetUpdateRate 60
	End Method
	


	Method OnUpdate()


		If KeyDown( KEY_LEFT ) Then AxisAngle = AxisAngle - 1.0
		If KeyDown( KEY_RIGHT ) Then AxisAngle = AxisAngle + 1.0
		

		If TouchDown() = 1
			If touching = False
				touching = True
				touchStart[ 0 ] = TouchX()
				touchStart[ 1 ] = TouchY()
			Else
				touchEnd[ 0 ] = TouchX()
				touchEnd[ 1 ] = TouchY()
			Endif
		Else
			If touching = True

				'/ check if touch vector is a valid swip
				Local result : Int = GestureSwipe( touchStart, touchEnd )
				
				If result = SWIPE_NONE Then Print "Not a valid swipe"
				If result = SWIPE_UP Then Print "Swipe Up Detected!"
				If result = SWIPE_DOWN Then Print "Swipe Down Detected!"
				If result = SWIPE_LEFT Then Print "Swipe Left Detected!"
				If result = SWIPE_RIGHT Then Print "Swipe Right Detected!"
				
				touching = False
			Endif
		Endif

	End Method
	



	Method OnRender()
		Cls 0, 0, 0
		
		DrawText "Press Left / Right to rotate Axis", 0, 0

		Local centreX : Int = DeviceWidth() / 2
		Local centreY : Int = DeviceHeight() / 2
		DrawLine centreX, centreY, centreX + Cos( AxisAngle ) * 100, centreY + Sin( AxisAngle ) * 100
		DrawLine centreX, centreY, centreX + Sin( AxisAngle ) * 100, centreY - Cos( AxisAngle ) * 100
		
	End Method
	


End Class



Function Main()
	New MyApp()
End Function






Function GestureSwipe : Int( touchStart : Int[], touchEnd : Int[], tolerance : Float = 0.15 )
	Local touchVectorX : Float = touchEnd[ 0 ] - touchStart[ 0 ]
	Local touchVectorY : Float = touchEnd[ 1 ] - touchStart[ 1 ]
	Local touchLength : Float = Sqrt( touchVectorX * touchVectorX + touchVectorY * touchVectorY )
	touchVectorX = touchVectorX / touchLength
	touchVectorY = touchVectorY / touchLength
	Local dot : Float = touchVectorX * Cos( AxisAngle ) + touchVectorY * Sin( AxisAngle )
	If Abs( dot ) < tolerance
		If touchVectorY < 0 Then Return SWIPE_UP
		Return SWIPE_DOWN
	Endif
	If Abs( dot ) > 1.0 - tolerance
		If touchVectorX < 0 Then Return SWIPE_LEFT
		Return SWIPE_RIGHT
	Endif
	Return SWIPE_NONE
End Function



Tri|Ga|De(Posted 2012) [#9]
Thats some nice piece of code NoOdle.

Could come in handy!


Raph(Posted 2013) [#10]
This is definitely hacky, but I extended NoOdle's code with detection of a few more gestures, some basic inertia, etc.

New stuff:

* I moved GestureSwipe() and the update stuff into a class; all you should need to do is create an instance of NoodleSwipe and check
Local result:Int = swipeMon.Update()

* it can now return while drags are in progress, including if the drag changes direction midway

* it also returns inertia values (a decaying float; change NoodleSwipe.inertiaDamp to affect the damping rate)

* it maintains a history of the drag, might be useful for drawing paths or lines or detecting arcs. You can get segment lengths and durations out, etc.

* there's a bunch of new helper methods in there.





Raph(Posted 2013) [#11]
Updated the above with a fix for drags, much nicer inertia, plus added more visualization to the test app.


Zurrr(Posted 2013) [#12]
Look great! could you show the use of 'Inertia' in your next update test.


Raph(Posted 2013) [#13]
It's pretty simple, and the test class does show how it works. If you run this in HTML5 you can see the inertia events firing in the text box, and see how it decays away.

How I am using it in my game right now (pseudocode):



Be sure to adjust the inertiaDamp value to suit your use case. I defaulted it at 0.9, but that was too many inertia ticks for the particular usage I had. I then tried 0.5, and it died off too fast. Currently at 0.75, may move it to 0.8!

Same with dragRange. The default is 50. Probably want to adjust it based on screen and device size. 44px at non-retina levels is what Apple recommends for a touch, but of course, you go retina and it goes to 100, Android has a pile of resolutions and screen sizes, etc.

Also, cf this bug report here: http://www.monkeycoder.co.nz/Community/posts.php?topic=5645

If you add this one-line hack:



you'll get consistent behavior across platforms.


Supertino(Posted 2013) [#14]
I use ATAN2 with my swipes, then from the angle I can see if it's up,down,left or right. And then Sqrt to find the distance.

Class TouchSwip
	Field originX:Int
	Field originY:Int
	Field distanceFromOrigin:Int
	Field angle:Int
	Field isTouched:Bool
	Field state:Int
	Field nullZoneDistance:Int
	Field direction:Int
	
	Method New(tMinDistance:Int)
		nullZoneDistance = tMinDistance	
	End Method
	
	Method Render()
		If state <> 2 Then Return
		DrawLine originX - nullZoneDistance, originY - nullZoneDistance, originX + nullZoneDistance, originY + nullZoneDistance
		DrawLine originX + nullZoneDistance, originY - nullZoneDistance, originX - nullZoneDistance, originY + nullZoneDistance
	End Method
	
	Method Update()
		Local x:Int = TouchX
		Local y:Int = TouchY

		Select state
			Case 0
				If isTouched = False And TouchDown(0) = True Then state = 1
			Case 1
				isTouched = True
				state = 2
				originX = TouchX
				originY = TouchY
			Case 2
				Local x:Int = TouchX
				Local y:Int = TouchY
				If TouchDown(0) = True
					distanceFromOrigin = Sqrt( (x - originX) * (x - originX) + (y - originY) * (y - originY))
					angle = (ATan2(originX - x, originY - y) + (180)) / 45
				Else
					If distanceFromOrigin > nullZoneDistance
						If angle = 4 or angle = 3 Then direction = 0
						If angle = 2 or angle = 1 Then direction = 1
						If angle = 7 or angle = 0 Then direction = 2
						If angle = 5 or angle = 6 Then direction = 3
					Else
						Self.Clear
					End If
				End If
		End Select
	End Method
	
	Method Clear()
		isTouched = False
		angle = -1
		direction = -1
		state = 0
	End Method
	
	Method Up:Bool()
		If direction = 0 Then Self.Clear; Return True
		Return False
	End Method
	
	Method Right:Bool()
		If direction = 1 Then Self.Clear; Return True
		Return False
	End Method
	
	Method Down:Bool()
		If direction = 2 Then Self.Clear; Return True
		Return False
	End Method
	
	Method Left:Bool()
		If direction = 3 Then Self.Clear; Return True
		Return False
	End Method
	
End Class