Skip to content

188. Understanding touch

December 28, 2014

I realised I haven’t done a proper post on how touch works in Codea.

So this is it. It is quite detailed.

When you run a Codea program, it compiles itself, then

  • the setup function runs first
  • the draw function runs 60 times a second after that
  • the touched function runs only when there is a touch (or a touch ends)

If you want your program to handle touches, you should include a touched function, like this

function touched(touchData)
    --code here
end

Note the touchData in brackets. This is a table provided by Codea that holds all the important information about what touches have been happening.

In some code examples, you will see it called touch instead. It doesn’t matter what you call it, it is just a label for the table provided by Codea. I am using the name touchData throughout this post, just so you don’t get confused with the function name touched.

So when a touch happens, Codea will run the touched function and pass it a table of information. What is in the table? All this stuff.

Touch States

What most people find confusing is that a touch has three “states”, which are

  • BEGAN – a finger has just touched the screen
  • MOVING – the finger is sliding along the screen (not standing still)
  • ENDED – the finger has lifted off the screen

BEGAN, MOVING and ENDED are just constants which have number values of 0, 1 and 2 respectively. Codea provides these labels for your convenience, because it is easier to understand code that uses BEGAN than 0.

You can find out which state a touch is in, with touchData.state, which will give you a number 0, 1, or 2.

You can see how they are used by running this small program

function touched(touchData)
    print(touchData.state)
end

and if you run it, and tap the screen once, you will see 0 and 2 print out (ie BEGAN, then ENDED). This makes sense, because our touch started and stopped without sliding on the screen.

If you now touch and slide your finger on the screen, you will get 0 1 1 1 1 1 1 … 1 1 2 (when you lift your finger). Why so many 1’s?

The update cycle

Now please try to understand this part, because it is what confuses most beginners.

Every 1/60 of a second, Codea updates the touch information, running the touched function if there has been a change, and then it runs the draw function. I’m going to call all of this the update cycle.

So let’s see what happens when there is a touch. I’m going to number the update cycles below just to make them clearer.

program has been running for a while
each update is 1/60 of a second apart
update 100 - no touch changes, so just run draw
update 101 - no touch changes, so just run draw
update 102 - no touch changes, so just run draw
update 103 - new touch! touch state=BEGAN, run touched, then draw
update 104 - touch slides! touch state=MOVING, run touched, then draw
update 105 - touch slides! touch state=MOVING, run touched, then draw
update 106 - still touching, but not sliding, so run draw
update 107 - still touching, but not sliding, so run draw
update 108 - still touching, but not sliding, so run draw
update 109 - touch ends! touch state=ENDED, run touched, then draw
update 110 - no touch changes, so just run draw
update 111 - no touch changes, so just run draw
update 112 - no touch changes, so just run draw

Codea notices a new touch when it does update 103, so it sets the touch state to BEGAN, and runs the touched function.

At the next update cycle, 1/60 of a second later, Codea notices that the finger is still touching the screen, and it has moved, so touch.state is set to MOVING, and the touched function is run.

The same thing happens at update cycle 105 (ie the finger has moved again).

At update 106, the finger is still on the screen, but it has not moved since update 105. So Codea does not run the touched function. The same happens at updates 107 and 108. (The touched state stays as MOVING).

At update 109, the touch has ended, so Codea changes the touch state to ENDED, and runs the touched function.

After that, there are no touches, so the touched function is not run.

How would it look if we hadn’t slid our finger? There would be no MOVING states, but there would still be a separate BEGAN and ENDED state (and if you had held your finger down for a while, you would have had some cycles like 106 to 108 above when nothing changed).

Now let’s learn a really important lesson from this.

The touched function runs at least twice for any touch, no matter how short, because there will always be a separate BEGAN and an ENDED state that occur in different update cycles.

So if you want to detect a tap, and you put code in the touched function so it runs when there is a touch, your code will run twice, once for BEGAN, and once for ENDED. (If you’re confused, take another look at the set of update cycles above). And if you have users who slide their fingers when they touch, your code could run many times!

How do you make sure your code only runs once? By testing the state, like this

function touched(touchData)
    if touchData.state==ENDED then
        --do something when touch ends
    end
end

So it ignores BEGAN and MOVING, and only runs when the touch ends.

Quick taps

For quick taps, it doesn’t really matter whether you test for BEGAN or ENDED, as long as you only run your code for one of them. ENDED is probably better, if you have users who are slow to lift their fingers!

Here is an example that runs another function, if the user has touched a circular button on the screen. To figure this out, we need to know where we touched, and our touchData table has x and y values that give this to us.

function touched(touchData)
    if touchData.state==ENDED then
        --see if we touched the button
        if vec2(touchData.x,touchData.y):dist(bCentre)<=bRadius then
            DoSomething()
        end
    end
end

If you don’t understand the line of code in the middle, don’t despair, it isn’t that complex. The simplest way to see if a touch is inside a button is to measure the distance from the touch to the middle of the button. If that distance is less than the radius of the button, we touched it.

Codea has a dist function that measures the distance between two points. They need to be given to Codea as vectors with an x and y value, which are called vec2. I’m assuming bCentre is already a vec2, and it might have been created like this

bCentre = vec2(300,350)
bRadius = 50

So I created a new vec2 with the touch x and y position, then used the dist function to see how far I was from the button centre.

What if the button is square? Then you’ll need to check if your touch is inside the four corners.

Slides and swipes

Suppose you want the user to be able to move a spaceship by swiping in any direction, maybe using an onscreen joystick.

You need to know not just whether swiping is happening, but which direction the swipe is in. And the touchData table has information for you, in deltaX and deltaY, which tell you how much the x and y position of the touch has moved (in pixels) since the last update cycle.

I’m going to assume you store the spacecraft position in a vec2 called SpacecraftPos, and in the draw function, you change it by adding a vec2 called velocity. So if you are moving straight up the screen, velocity might be (0,3), and if you are moving toward the upper right of the screen, it might be (4,4).

This code will change the velocity whenever we swipe or slide our fingers on the screen.

function touched(touchData)
    if touchData.state==MOVING then
        velocity = vec2(touchData.deltaX,touchData.deltaY)
    end 
end

function draw()
    --other code
    SpacecraftPos = SpacecraftPos + velocity
    --other code
end

So whenever you swipe, the direction will change, and will stay that way until you swipe again. How fast you move depends on how fast you swipe (because we are only capturing the change in the latest update cycle).

Now this may be a problem if you want the spaceship to move at a constant speed, and not go zooming all over the screen if the user gets over excited. In that case, a good option may be to adjust the deltaX and deltaY values so they have a combined distance of 1, then you multiply by the speed you want. The result is that your spacecraft will travel a constant speed in the direction of the swipe. Codea has a special function normalize that will adjust the deltaX and deltaY values for us, like so.

function touched(touchData)
    if touchData.state==MOVING then
        local v = vec2(touchData.deltaX,touchData.deltaY)
        velocity = v:normalize() * speed
    end 
end

Another way to look at this, is that our vector (v in the code above) contains not only direction, but a distance too (the length of the swipe). We don’t care about the distance, because we want to use a constant speed. The normalize function gets rid of the distance (or rather, it makes it equal to 1) and leaves us just with direction. Then we can multiply this by our speed to get the velocity to be added to our position at each update cycle.

Of course, if you only care about the x (or the y) direction, you can just use the deltaX (or deltaY) value.

 Multiple (one finger) taps

First, let’s be clear. This is one finger tapping on the screen.

Codea’s touch information also includes something called tapCount. You can try playing with it using this simple code (you don’t even need a setup or draw function).

function touched(touchData)
    print(touchData.tapCount)
end

Now try tapping the screen slowly and quickly, to get an idea of how tapCount works. You can probably see that it could be difficult for users to time their tapping perfectly to make tapCount equal exactly 3 or more, but a double tap is fairly easy.

So you can test for a double tap easily by checking the value of tapCount.

Touches by several fingers

Again, let’s be clear. Now we have more than one finger touching the screen at the same time. Try it with the code just above.

Hmm. tapCount still shows 1, so that isn’t going to work.

Also, the touched function is called separately for each finger, ie if you touch with three fingers, the touched function runs three times per update cycle – which could cause problems.

Luckily, Codea has a demonstration program called Multi Touch, which shows how to handle this situation, and I’ll explain how it works. It uses another piece of touch data, touchID, which is a unique id number for each touch. This enables us to know which fingers are still touching the screen, and which are not.

Let’s suppose we touch the screen with three fingers at once. So the touched function will run three times, with a state of BEGAN, and a different id number for each finger (say, 100, 101 and 102). Suppose we have already set up an empty table called Touches to hold this information.

In the touched function, we test the state, and if it is BEGAN, we add the touch to our table. If it is ENDED, we remove it from our table.

function setup()
    touches = {} --create empty touch table
end

function touched(touchData)
    if touchData.state==BEGAN then --add to table
        touches[touchData.id]=touchData
    elseif touchData.state==ENDED then  --delete
        touches[touchData.id]=nil
    end
end

Note that this adds all the information for the touch (x, y, deltaX, deltaY, etc) to the table, because we will probably want to use some of this information in our program, eg in the demo program, the draw function loops through all the table items and draws circles on the screen at their x,y positions.

Why do we put the id number inside the square brackets? Why not just use table.insert to add the touch data to our table? The reason is that we want to delete that touch later, when it ends, we need some way of knowing which touch it was. Labelling it by its id number makes this very easy. And if you didn’t know, setting a table item to nil, deletes it.

This code is not quite the same as the demo I referred to above. That code looks like this

function touched(touchData)
    if touchData.state==ENDED then --delete
        touches[touchData.id]=nil
    else --add to table
        touches[touchData.id]=touchData
    end
end

Why do it backwards, testing for ENDED? Where has BEGAN gone?

The reason is simply this. In my version, if the user slid their finger, the state would be MOVING, and I didn’t test for that, so I wouldn’t change the x,y position as the finger moved. And you might need  that updated information.

The solution is to update the table if the finger moves. I could do it by testing if state == MOVING and updating any item that has changed, but it’s much easier to just completely replace the old data with the new data. The code for doing that is the same as the code for adding the data to the table (as I’ll explain below), so now the code is the same for BEGAN and MOVING, and I could test for both of them.

But it’s a teeny bit more efficient to first test for ENDED, and if it’s not ENDED, then it has to be BEGAN or MOVING, and so the demo is written that way round. That’s why the code is back to front.

Now – why does the same code work for adding and updating the touch data in our table? It is because you are not actually adding any of the touch data to your table. A variable can only store a number or a string. So if we ask it to store a table of touch data, what Codea does is to store the memory address of the table (which is a number) in the variable. Then when we use that variable, Codea goes and looks up the table at that address.

And that’s why the code is the same for adding and updating touch data in our table – because we are simply storing a memory address with a lookup key which is equal to the touch id. Updating it is simply a case of changing the memory address so it points at the latest table.

What about CurrentTouch?

CurrentTouch is a global variable (you can use it anywhere in your program) which contains information about the current touch (only one). This might seem to be better than using the touched function, because it is simpler.

However, you can’t rely on CurrentTouch to have accurate information, because it doesn’t tell you when touch information changes, and it doesn’t get updated in between touches, so it has the out of date touch data.

I think I can confidently say that all experienced Codea users recommend not using CurrentTouch.

Advertisement
2 Comments
  1. Anonymous permalink

    This is a really great article. Thanks for posting!

Trackbacks & Pingbacks

  1. 11. Handling touches – simple pinch and zoom | coolcodea

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: