Touch Monitor Class
Monkey Forums/Monkey Code/Touch Monitor Class
| ||
Hi, That's going to be a long post, but I thought my touch monitor class might be useful for some other Monkey coders. The class monitors any number of touch indices that are supported by the target and records things such as * the duration of the touch * the position * the first position/origin (at what coordinates did the touch begin) * the offset relative to the origin * the touch velocity (useful to drag things, etc.) * obviously what touch indices are touching * if a touch has just been released And some useful values for multi touch, e.g. * the centre position (calculates the centre of multiple touch coordinates) * the average position (usually equals the centre position, but e.g. when one finger is on the left of the screen and three are on the right the average position is more weighted towards the right) * the size (if using 2 or more fingers, what's the size/distance between them) * the size factor (how much increased/decreased the size; useful for pinch gestures) Checking if the touch is in a certain area * is one or are multiple touches in a certain area? * works with rectangular, circular and elliptic areas (rectangles and ellipses can be rotated) * tons of different methods to use for a lot of different purposes The class also has an optional coordinate offset and adjustment ratio which is useful when working with a virtual device size. Enter the correct adjustment ratio and you can work with the virtual values (if your virtual device is 1536x2048 but your actual device is 768x1024 then the adjustment ratio is 2.0 and the TouchMonitor will return virtual values). Set an offset if you have borders, which may happen if the aspect ratio of the virtual device and actual device are different. [EDIT] Download (updated version): http://attic.nugob.org/communities/monkeycoder.co.nz/forum/attachments/touchMonitorTest_2012-12-11.zip Screenshot of some information that the class can give you: ![]() Here's some sample code that demonstrates how to use it. I usually create a Global TouchMonitor instance that can be used for any purpose within the app. Ideally test this with a touch device and use multiple touches: [monkeycode] Strict Import mojo Import vector Import touchMonitor Global Touch:TouchMonitor Function Main:Int() New MyApp Return 0 End Class MyApp Extends App Field circleRadius:Float = 50 Field circleVelocity:Vector Field circleCoord:Vector Field rectCoord:Vector Field showTapInfo:Bool Field showTouchHoldInfo:Bool Method OnCreate:Int() SetUpdateRate(60) Touch = New TouchMonitor(5) ' monitors five touch indices; change the number to monitor more or less indices circleVelocity = New Vector circleCoord = New Vector(DeviceWidth()/2, DeviceHeight()/2) rectCoord = New Vector(DeviceWidth()-200, DeviceHeight()-200) Return 0 End Method OnUpdate:Int() ' must call this!!! Touch.Update() ''''''''''''''''''''''''''''''' ' SOME EXAMPLES ' MONITORING A RECTANGULART AREA '''''''''''''''''''' ' checks if the user quickly tapped on the rectangle (tap and release in under 0.1 secs) If Touch.FirstTouchInArea(rectCoord.X, rectCoord.Y, 100, 100) And Touch.Released And Touch.Duration < 100 showTapInfo = Not showTapInfo ' toggle show info Endif ' checks if the user touches and holds the touch (for more than 0.5 secs If Touch.FirstTouchInArea(rectCoord.X, rectCoord.Y, 100, 100) And Touch.Duration > 500 showTouchHoldInfo = True Endif ' reset a value on touch release If Touch.Released showTouchHoldInfo = False Endif ' MONITORING A CIRCULAR AREA '''''''''''''''''''' ' to drag an element: if the touch is in a circular area then add the touch velocity to the coordinates If Touch.IsInCircle(circleCoord, circleRadius) circleVelocity.Set(Touch.Velocity) Else ' gradually decrease the element velocity when the user isn't dragging the element circleVelocity.Multiply(0.99) EndIf ' add the velocity to the current coordinates (moves the element) circleCoord.Add(circleVelocity) ' keep the element within the boundaries of the screen circleCoord.X = Clamp(circleCoord.X, circleRadius, DeviceWidth() - circleRadius) circleCoord.Y = Clamp(circleCoord.Y, circleRadius, DeviceHeight() - circleRadius) ' check if the element is at the edge; if so then reverse the velocity (make it bounce off the edge) If circleVelocity.X <> 0 And circleCoord.X = circleRadius Or circleCoord.X = DeviceWidth() - circleRadius circleVelocity.X = -circleVelocity.X Endif If circleVelocity.Y <> 0 And circleCoord.Y = circleRadius Or circleCoord.Y = DeviceHeight() - circleRadius circleVelocity.Y = -circleVelocity.Y Endif Return 0 End Method OnRender:Int() Cls() ' DRAW RECTANGLE AND RELATED INFO ''''''''''''''''''''' ' draw a simple rectangle that changes color if touched/clicked If Touch.IsInArea(rectCoord.X, rectCoord.Y, 100, 100) Then SetColor(255,0,0) DrawRect(rectCoord.X, rectCoord.Y, 100, 100) DrawText("tap quickly", rectCoord.X + 10, rectCoord.Y + 10) DrawText("or", rectCoord.X + 10, rectCoord.Y + 30) DrawText("touch hold", rectCoord.X + 10, rectCoord.Y + 50) SetColor(255, 255, 255) ' display something if the user tapped the rectangle If showTapInfo Then DrawText("Tap the rectangle again to hide this text.", rectCoord.X - 100, rectCoord.Y - 30) If showTouchHoldInfo Then DrawText("Release the touch to hide this text.", rectCoord.X - 100, rectCoord.Y - 30) ' DRAW CIRCLE AND RELATED INFO ''''''''''''''''''''' ' draw a circle (this circle can be dragged according to the code in OnUpdate) SetColor(255, 128, 0) DrawCircle(circleCoord.X, circleCoord.Y, 50) DrawText("drag", circleCoord.X, circleCoord.Y) ' VISUALIZE TOUCH POSITIONS ETC. ''''''''''''''''''''' ' draw a circle that shows the touch size (for multi touch only) SetColor(255, 255, 0) SetAlpha(0.2) DrawCircle(Touch.CenterPosition.X, Touch.CenterPosition.Y, Touch.Size/2) ' center position of multi touch SetAlpha(1) DrawText("center position", Touch.CenterPosition.X, Touch.CenterPosition.Y - 30) SetAlpha(0.3) DrawCircle(Touch.CenterPosition.X, Touch.CenterPosition.Y, 10) ' average position of multi touch (this equals the center position above if two fingers are touching the screen, but it is different ' for 3 or more touch indices as it calculates the average position, and not the centre between the minimum and maximum positions; ' e.g, if one finger is touching on the left and three are touching more on the right, the average position will be more weighted to the right) SetColor(0, 255, 255) SetAlpha(1) DrawText("average position", Touch.AveragePosition.X, Touch.AveragePosition.Y + 20) DrawCircle(Touch.AveragePosition.X, Touch.AveragePosition.Y, 10) SetColor(255, 255, 255) SetAlpha(1) ' draw some info about each touch index Local textPositionY:Float = 10 For Local index:= Eachin Touch.TouchDownIndices DrawText("TOUCH INDEX " + index, 10, textPositionY) DrawText(" first position: " + Touch.FirstPosition(index).X + ", " + Touch.FirstPosition(index).Y, 10, textPositionY + 25) DrawText(" position: " + Touch.Position(index).X + ", " + Touch.Position(index).Y, 10, textPositionY + 40) DrawText(" offset: " + Touch.Offset(index).X + ", " + Touch.Offset(index).Y, 10, textPositionY + 55) DrawText(" velocity: " + Touch.Velocity(index).X + ", " + Touch.Velocity(index).Y, 10, textPositionY + 70) DrawText(" duration: " + Touch.Duration(index), 10, textPositionY + 85) textPositionY += 100 ' visualize each touch index DrawText("index " + index, Touch.Position(index).X, Touch.Position(index).Y - 40) SetAlpha(0.3) DrawCircle(Touch.Position(index).X, Touch.Position(index).Y, 30) SetAlpha(1) Next ' draw touch info (this is mainly useful for multi touch) If Touch.AnyTouchDown DrawText("TOUCH INFO (try with multi touch gestures)", 300, 10) DrawText(" touch size: " + Touch.Size + " (size factor: " + Touch.SizeFactor + ")", 300, 25) DrawText(" Average position: " + Touch.AveragePosition.X + ", " + Touch.AveragePosition.Y, 300, 40) DrawText(" Center position: " + Touch.CenterPosition.X + ", " + Touch.CenterPosition.Y, 300, 55) DrawText(" Average offset: " + Touch.AverageOffset.X + ", " + Touch.AverageOffset.Y, 300, 70) DrawText(" Center offset: " + Touch.CenterOffset.X + ", " + Touch.CenterOffset.Y, 300, 85) DrawText(" Average velocity: " + Touch.AverageVelocity.X + ", " + Touch.AverageVelocity.Y, 300, 100) Endif Return 0 End End [/monkeycode] I wrote this class very early on when I was quite new to Monkey, so there are a few things I would probably solve in a different way now, but overall it works very well. Also, I have a separate swipe gesture class which extends the Touch class, but I discovered some bugs. I might post this at a later stage. (Before I posted the touch monitor class here I found a few bugs which should be fixed now. If you find some more, please let me know.) Classes required: touchMonitor.monkey [EDIT] updated code vector.monkey by Tibit* * The class works a lot with vectors because I find this is much more useful. I borrowed and slightly adjusted a vector class from Tibit (Thank you!!!): http://www.monkeycoder.co.nz/Community/posts.php?topic=568#4319 I'm sure there are some other similar and useful classes around, e.g. in diddy. If this class suits you, feel free to use and modify it. Cheers! Anatol |
| ||
Great Helper class! I think I'll have great use for this when implementing proper pinch zoom And happy that you had use of the Vector class :) Just curious, what can your swipe gesture class be used for? Sounds cool. |
| ||
Thanks Tibit, and thanks for the work on the excellent vector class which is extremely useful. When I first started using Monkey I did everything without vectors, but I scrapped it all when I found your class in the forum - vectors just save so much time! A pinch gesture should be easy with Touch.SizeFactor that monitors the change in size of a multi touch relative to the original size when the multi touch began. I will definitely post the swipe class extensions here but I probably won't have the time this week to clean them up. The swipes I've written so far simply check if a swipe goes in a specific direction that can be defined with an angle, and how long the minimum swipe length needs to be in order to validate. Optional you can also set a "velocity coefficient" which just means that if you do a quick flick then the swipe gesture also validates if it's shorter than the defined minimum length. Swipes can also be defined to only validate if they start in a certain area, and if it requires one, two or more fingers to validate. I guess it can be extended to more complex gestures, but I didn't need that so far. I probably have some more code to share here over the next couple of weeks, one of which is a 3D vector class based on your vector class (thanks again!). I just want to clean it up and add proper comments so that it's easier to read here in the forum. It's just a matter of finding the time... |
| ||
@Anatol -- Any luck finding time? Thanks for sharing. |
| ||
Hi. Thanks for the interest. I'm on a conference this week, and Wellington is full of Hobbits, which I have to admit is a bit distracting. I'll post the swipe extensions when I'm back, some time next week. |
| ||
Good stuff. I was looking at implementing a gesture class too. Was going to approach it from a completely different angle so I could detect odd shaped gestures (swirls, ticks etc) May see if I can extend this class, but feel I may have to start again If I get anywhere I'll post that up. |
| ||
Hi, OK, here are my swipe gesture classes that extend the touch monitor class above. These are "simple" swipe gestures. You can define any angle and direction for a gesture (with a vector that describes the swipe) and define if the gesture validates with one or more touch indices (fingers). As mentioned earlier, optional you can also set a "velocity coefficient" which just means that if you do a quick flick then the swipe gesture also validates if it's shorter than the defined minimum length. Swipes can also be defined to only validate if they start in a certain area, and if it requires one, two or more fingers to validate. PLEASE NOTE: If you earlier downloaded the TouchMonitor from the post above you need to update it. I had to change a few lines to fix a bug in the SwipeGesture class. This archive contains all the updated classes, sample code and the new swipe gestures: Download: http://attic.nugob.org/communities/monkeycoder.co.nz/forum/attachments/touchMonitorTest_2012-12-11.zip Screenshot of the swipe gesture sample code (see download or below): ![]() Here's some sample code that demonstrate how to set up a gesture that has to start in a rectangular area, one that has to start in a circular area and one multi touch gesture. [monkeycode] Strict Import mojo Import vector Import swipeGestures Global Touch:TouchMonitor Function Main:Int() New MyApp Return 0 End Class MyApp Extends App Field swipeGestureRectangularArea:SwipeGestureWithRectangularArea Field swipeGestureCircularArea:SwipeGestureWithCircularArea Field multiTouchSwipeGesture:SwipeGesture Field showSwipeGestureRectangularAreaInfo:Bool Field showSwipeGestureCircularAreaInfo:Bool Field showMultiTouchSwipeGestureInfo:Bool Method OnCreate:Int() SetUpdateRate(60) ' define a swipe gesture with a rectangular start area that validates if the user swipes a minimum of 200 pixels to the left; ' the 0.1 value in the line below means that swipes that are shorter than 200 pixels can validate if the swipe is quick (a fast flick) swipeGestureRectangularArea = New SwipeGestureWithRectangularArea(New Vector(-200, 0), 0, DeviceHeight()-50, DeviceWidth(), 50, 0.1) ' define a swipe gesture with a circular start area that validates if the user swipes a minimum of 100 pixels down swipeGestureCircularArea = New SwipeGestureWithCircularArea(New Vector(0, 100), DeviceWidth() / 2, DeviceHeight()/2, 100) ' define a multi touch swipe gesture that requires 2 touch indices to validate (without start area, so this gesture validates anywhere as long as the direction and length validate) multiTouchSwipeGesture = New SwipeGesture(New Vector(200, 0), , 2) ' 2 defines the number of touch indices (fingers) that need to touch the screen to validate Return 0 End Method OnUpdate:Int() ' MONITORING SWIPE GESTURES '''''''''''''''''''''' ' swipe gesture with rectangular start area swipeGestureRectangularArea.Update() ' must call this If swipeGestureRectangularArea.ValidOnRelease showSwipeGestureRectangularAreaInfo = Not showSwipeGestureRectangularAreaInfo ' toggle the value of a Bool if the swipe gesture validates Endif ' swipe gesture with circular start area swipeGestureCircularArea.Update() ' must call this If swipeGestureCircularArea.ValidOnRelease showSwipeGestureCircularAreaInfo = Not showSwipeGestureCircularAreaInfo ' toggle the value of a Bool if the swipe gesture validates Endif ' multi touch swipe gesture multiTouchSwipeGesture.Update() ' must call this If multiTouchSwipeGesture.ValidOnRelease showMultiTouchSwipeGestureInfo = Not showMultiTouchSwipeGestureInfo ' toggle the value of a Bool if the swipe gesture validates Endif Return 0 End Method OnRender:Int() Cls() ' VISUALIZE THE RECTANGULAR SWIPE GESTURE AREA '''''''''''''''''''' SetColor(255, 0, 255) SetAlpha(0.3) DrawRect(swipeGestureRectangularArea.StartAreaPosition.X, swipeGestureRectangularArea.StartAreaPosition.Y, swipeGestureRectangularArea.StartAreaWidth, swipeGestureRectangularArea.StartAreaHeight) SetAlpha(1) DrawText("swipe 200 pixels to the left (or less if you swipe quickly)", swipeGestureRectangularArea.StartAreaPosition.X + 200, swipeGestureRectangularArea.StartAreaPosition.Y + 10) ' display something if the rectangular swipe gesture validated on release If showSwipeGestureRectangularAreaInfo Then DrawText("Swipe to the left again to hide this text.", swipeGestureRectangularArea.StartAreaPosition.X + 200, swipeGestureRectangularArea.StartAreaPosition.Y + 30) ' VISUALIZE THE CIRCULAR SWIPE GESTURE AREA '''''''''''''''''''' SetColor(255, 255, 0) SetAlpha(0.3) DrawCircle(swipeGestureCircularArea.StartAreaPosition.X, swipeGestureCircularArea.StartAreaPosition.Y, swipeGestureCircularArea.Radius) SetAlpha(1) DrawText("swipe 100 pixels down", swipeGestureCircularArea.StartAreaPosition.X - 50, swipeGestureCircularArea.StartAreaPosition.Y) ' display something if the rectangular swipe gesture validated on release If showSwipeGestureCircularAreaInfo Then DrawText("Swipe down again to hide this text.", swipeGestureCircularArea.StartAreaPosition.X - 50, swipeGestureCircularArea.StartAreaPosition.Y + 20) ' INFOR FOR THE MULTI TOUCH SWIPE GESTURE AREA '''''''''''''''''''' SetColor(0, 255, 0) DrawText("swipe with 2 fingers 200 pixels to the right (anywhere)", DeviceWidth() - 400, 10) ' display something if the rectangular swipe gesture validated on release If showMultiTouchSwipeGestureInfo Then DrawText("Swipe with 2 fingers right again to hide this text.", DeviceWidth() - 400, 25) ' DRAW SWIPE INFOS ' swipe gesture with rectangular start area If swipeGestureRectangularArea.IsSwiping SetColor(255, 0, 255) DrawText("RECTANGULAR SWIPE GESTURE INFO", 10, 10) DrawText(" Length: " + swipeGestureRectangularArea.Offset.Length, 10, 25) DrawText(" Angle: " + swipeGestureRectangularArea.Offset.Direction, 10, 40) DrawText(" Center offset: " + swipeGestureRectangularArea.CenterOffset.X + ", " + swipeGestureRectangularArea.CenterOffset.Y, 10, 55) If swipeGestureRectangularArea.HasValidStartArea DrawText("valid start area: YES", 10, 70) Else DrawText("valid start area: NO ", 10, 70) Endif Endif ' swipe gesture with circular start area If swipeGestureCircularArea.IsSwiping SetColor(255, 255, 0) DrawText("CIRCULAR SWIPE GESTURE INFO", 10, 10) DrawText(" Length: " + swipeGestureCircularArea.Offset.Length, 10, 25) DrawText(" Angle: " + swipeGestureCircularArea.Offset.Direction, 10, 40) DrawText(" Center offset: " + swipeGestureCircularArea.CenterOffset.X + ", " + swipeGestureCircularArea.CenterOffset.Y, 10, 55) If swipeGestureCircularArea.HasValidStartArea DrawText("valid start area: YES", 10, 70) Else DrawText("valid start area: NO ", 10, 70) Endif Endif ' multi touch swipe gesture If multiTouchSwipeGesture.IsSwiping SetColor(0, 255, 0) DrawText("MULTI TOUCH SWIPE GESTURE INFO", DeviceWidth() - 300, 55) DrawText(" Length: " + multiTouchSwipeGesture.Offset.Length, DeviceWidth() - 300, 70) DrawText(" Angle: " + multiTouchSwipeGesture.Offset.Direction, DeviceWidth() - 300, 85) DrawText(" Center offset: " + multiTouchSwipeGesture.CenterOffset.X + ", " + multiTouchSwipeGesture.CenterOffset.Y, DeviceWidth() - 300, 100) Endif Return 0 End End [/monkeycode] The new swipe gesture classes I hope this is helpful for some people. Feel free to post any bugs, improvements, replacements here. Cheers! Anatol |
| ||
Anatol, Thank you for a very useful post even for beginners. You have commented so thoroughly, and provided a working example so that even if one doesn't understand how all the code works yet, he can see where to "plug in" to make things "work". I'd suggest in your zip file that you just add some instructions so beginners know to put "vector" and "touchMonitor" in the modules file and the runnable examples in the bananas file. As one who is still learning slowly, but gets lost in the assumed basics sometimes, I appreciate all the help I can get. Your examples make things workable for a beginner and encourage further learning by showing how it all works. Thanks! |
| ||
Looks kinda like Diddy's InputCache in terms of functionality. |
| ||
Anatol, thanks for this - very helpful! :) |