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:
-
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. -
"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.