11. Handling touches – simple pinch and zoom
In this post, we’ll look at a simple pinch and zoom class, as an introduction to handling touches – and get a little more practice with classes. This one is quite short.
You should also look at this later post, which explains touch more fully.
We’ll draw an image on the screen, and zoom it in or out as the user pinches or zooms with two fingers. We’ll calculate the zoom very simply, as the ratio of the distance between the fingers now, to the distance the last time we measured it. So if the fingers are 100 apart now, and they were 80 before, we’ll apply a zoom of 100/80 = 125%.
Codea tells you about touches using this function in Main
function touched(touch) -- your code here end
Every single touch calls this function (two fingers means it is called twice), and it keeps getting called as you hold your finger(s) down, until you let go.
Codea provides the touch item when it calls touched, and this has lots of useful information about the touch (see the built in help for full details – press the little square at top left of the main Codea screen and find your way from there).
Probably the most important information is the state property, which has three values – BEGAN, for the start of the touch, MOVING, while the touch continues, and ENDED. The BEGAN and ENDED states will occur exactly once for each touch, but the MOVING state could occur dozens of times, depending on the length of the touch.
There is also an x and y property to tell you where the touch happened. This example shows all of these in action. It will print a message when each touch ends.
function touched(touch) if touch.state==ENDED then print('You touched',touch.x,touch.y) end end
These items are all we will need for pinch and zoom. The problem, though, is that the touched function is called separately for each finger touch. How will we identify a two finger touch, as opposed to a three or four finger touch?
What I’ve done is to use a table “tb” to store a couple of details about each touch, and in the draw function, we’ll count the number of touches in the table. If there are two, we’ll calculate the distance between the fingers and compare it with the previous value, which we’ve stored. We can then ratio the two distances to calculate zoom, which is then used to draw the sprite.
For neatness, I’m going to put my pinch code into a class, like so (it’s mainly comments)
pinch=class() function pinch:init() self.tb={}. -- this table will hold touch info self.zoom=1 --set the initial zoom end --this function doesn't call itself. Only the touched --function in Main does that, but we will get that --function to call this one, and pass the touch info across function pinch:touched(touch) --when touch ends, clear table and set distances to nil if touch.state==ENDED then self.tb={} self.d1=nil self.d2=nil --if we are touching, add touch to tb, store x and y else table.insert(self.tb,{x=touch.x,y=touch.y}) end end --draw in Main will call this function, to see if we --need to pinch/zoom function pinch:processTouches() if #self.tb==2 then -- continue if we had two touches --set the two vectors for the two fingers local v1=vec2(self.tb[1].x,self.tb[1].y), local v2=vec2(self.tb[2].x,self.tb[2].y) self.d1=v1:dist(v2). -- calculate the distance between end --if we've got a previous measurement, check if its --changed and set the zoom if self.d2~=nil then self.zoom=self.zoom*self.d1/self.d2 end --store the current distance and clear the array self.d2=self.d1 self.tb={} end
The code below illustrates how to use the class. It only takes about 4 lines of code. As you get to use classes more and more, you will increasingly rely on those classes to handle touches and drawing, rather than Main, so it’s important to know how to do it.
function setup() img=readImage('Small World:Mine Medium') p=pinch() --initialise pinch end function draw() background(0) p:processTouches(). --process touches sprite(img,WIDTH/2,HEIGHT/2,500*p.zoom) --note zoom end function touched(touch) p:touched(touch) --pass the touch data to p end
Thanks for this lesson. I had been having trouble with touches and in a game I am developing to learn Codea I was getting double inputs whenever I touched the screen. Proper use of the touch.state ENDED has sorted this for me. Brilliant !
What does “~=” symbol mean?
It can’t be almost equal it, can it?
Saurabh, it means not equal to – but you really need to do some reading.
Have a look at the Lua documentation (I think I gave a link to it in about post 4), and try to learn the basics of the language. You can’t learn everything from my posts – they are intended only to explain the difficult parts of the language, and you need to read up on the easy stuff by yourself.
I looked up in the lua manual 5.1 but it didn’t have anything written about the symbol, so I asked here. Thanks anyways!
Saurabh, try this
http://www.lua.org/pil/3.2.html
I didn’t understand one thing. where do you calculate self.d2 before using it in processTouches to set the zoom
Look at the last line of processTouches
But doesn’t that mean that self.d1 is equal to self.d2 hence self.d1/self.d2 will always be 1?
Nope. Self.d1 is set first, then if self.d2 is not blank, the zoom is calculated (self.d1 will not be the same as self.d2), and then self.d2 is set to self.d1.
So self.d2 always has the value of self.d1 from the previous redraw
Oh okay understood thanks