Touch Tutorial

loopspace

2014-12-07

Creative Commons License

Contents

  1. Home

  2. 1. Introduction

  3. 2. Initial Structure

  4. 3. Gestures

  5. 4. Example Code

1 Introduction

Just about every project that I write uses my Touch Controller. So if you want to use or understand one of my Codea projects, it is probably a good idea to understand how this one works.

To do that, it is first a good idea to understand the problem that it is designed to solve, and to understand that, one needs to first recall how a Codea program runs. Codea first runs the setup function, then it enters a loop where it runs the draw function interspersed with the touched function as needed. That is, in each loop the draw function is run once and then the touched function is called for each new touch event. This means that the touched function can be run several times or not at all, and if a touch doesn't do anything new in the current loop then the touched function is not run for that touch even though it may still be "active".

So doing stuff in the touched function is dangerous because it can be called an unpredictable number of times, including none at all.

Also, touch events are essentially independent pieces of information with only the touch.id to link a new event to previous touch events.

The touch controller, therefore, is designed to do two things:

  1. Keep track of which object in Codea is associated to a particular touch.

    This is done as follows. When a new touch – i.e. one with state BEGAN – is registered, every known object is queried to see if it claims the touch. The first to do so is assigned that touch and all of its updates.

  2. "Smooth out" the incoming touch information so that each object can deal with all of its touches in one go and on every draw.

    To do this, the touch controller maintains a list of all the touches associated with each object and once per draw cycle it passes all of an object's touches – whether updated with new information or not – to it as a single collection.

This means that each object knows that its touch handling function will be called exactly once per draw cycle whenever it is "being touched".

2 Initial Structure

The touch controller needs to maintain a list of "touchable objects". Therefore, each object that wants to be able to accept touches needs to register itself with the touch controller. The touch controller interacts with these objects in two ways. The first interaction comes when a new touch comes in. Then the controller must query each of its objects to see which claims it. It does this by calling the method isTouchedBy which must return true or false. The second interaction comes when it passes all the touches to an object. It does this by calling the method processTouches.

Thus the structure of a program that uses this touch controller must be as follows. We set up the touch controller in the setup function:



touches = Touches()

Then each object that wants to claim touches registers itself with the controller via:



touches:pushHandler(object)

(there's also unshiftHandler for putting oneself at the front of the list).

The objects must have methods isTouchedBy and processTouches. The first of these takes a touch as input. The touches are bundled into a gesture before being passed to processTouches. More on that in a moment.

The final pieces in the main program are that the touch controller must claim every touch for itself via:



function touched(t)
    touches:addTouch(t)
end

and at the beginning of the draw function, it must run its processing code:



function draw()
    touches:draw()

3 Gestures

The touches associated with an object are passed to it as a single collection once every draw cycle. Technically, the touches are first augmented so that they contain more information than just the latest update. Then, the touches are bundled into a Gesture which is passed to the receiving object. Before being passed on, the gesture is analysed to establish some basic information.

The augmented Touch object knows the following information (plus some internal stuff):

Name Explanation
id The touch identifier
touch The current inbuilt touch corresponding to this extended object
firsttouch The first touch
updated Was there new information for this touch?
updatedat Last time that new information was given
createdat The time this object was created
startedat The second time new information was given
deltatime The time between the latest updates
laststate What happened the time before
moved Has this touch moved?
long Was there a long delay at the start?
short Did it end very soon after it began?
keepalive Should we prolong the life of this object?

It is also possible to compute its velocity, either instantaneous, recent, or mean.

A Gesture knows the following information:

Name Explanation
num Number of touches
updated Have any of its touches been updated?
updatedat The time of the last information
type.ended Have all the touches ended?
type.finished Have all the touches ended at least half a second ago?
type.tap Is this a tap (no touches have moved)?
type.long Is this a long gesture?
type.short Is this a short gesture?
type.pinch If a multi-touch gesture, is it a pinch?

The touches themselves are contained in both the table touches and the array (numerically indexed table) touchesArr.

The gesture is passed to the object's processTouches method. One thing that that method usually ought to do is call the noted method on the gesture. This resets the updated flag on the gesture and all of its touches. It is up to the object to do this.

One subtlety worth mentioning is the distinction between a gesture being ended and being finished. A gesture lives on beyond the lifespan of each of its touches by half a second. This means that if a new touch starts within half a second of a previous one, it is counted as being part of the same gesture. The reason for this is to allow for a gesture to consist of multiple taps and similar complicated sequences. If a gesture is ended then all of its touches are in the state ENDED, but if it is finished then that has been true for half a second. Once a gesture reaches the state finished then the touch controller resets it. However, an object can reset it early by calling the reset method on the gesture.

4 Example Code

An example project, containing the touch controller code, can be found on github. This project has two touchable classes. The first, Stuff, will display information about the Gesture when it is touched. The second, Box, is simply a box that can be touched and will change colour when it is being touched. Note that this shows the advantage of the fact that the processTouches method of the Box class being called exactly once per draw cycle while it is being touched since we can update the colour in the processTouches function without worrying about it being called the wrong number of times.