Category Archives: Proton SDK

Relating to my C++ mobile focused open source cross platform development framework

How to get rock solid multi-touch input finger tracking on iOS

So I’ve got this game prototype idea I wanted to try but I needed better multi-touch input support (accurate and comprehensive fingerID tracking) to make it happen.

The new test in RTSimpleApp: You can't see it in the shot, but each square has a fingerID # on it as well

It works great and has been added to the Proton SDK svn along with a new “Multitouch input test” option in RTSimpleApp.

Some stuff I noticed:

  • Tracks 11 fingers on an iPad
  • Tracks 4 fingers on an iPod Touch G1 (5 sort of.. but not reliably..)
  • Tracks 5 fingers on an iPhone4
  • Impossible to confuse it with fast movement, sliding off the screen or fast button mashing.  Solid!
  • Tracks 1.5 fingers on a Google Nexus One (It’s the HW’s fault.  Curious to see the results from a Tab or other new devices though)

For those interested, here is the relevant tracking code to get the “finger id”:  (yeah, I could have done it a more Obj-C way, but meh.. also, you may need your file named .mm, not .m for this code to work…)

[php]

const int MAX_TOUCHES= 11; //Oops, it can handle 11, not 10. Thanks @Bob_at_BH

class TouchTrack
{
public:

TouchTrack()
{
m_touchPointer = NULL;
}

void *m_touchPointer;
};

TouchTrack g_touchTracker[MAX_TOUCHES];

int GetFingerTrackIDByTouch(void* touch)
{
for (int i=0; i < MAX_TOUCHES; i++)
{
if (g_touchTracker[i].m_touchPointer == touch)
{
return i;
}
}

//LogMsg("Can’t locate fingerID by touch %d", touch);
return -1;
}

int AddNewTouch(void* touch)
{
for (int i=0; i < MAX_TOUCHES; i++)
{
if (!g_touchTracker[i].m_touchPointer)
{
//hey, an empty slot, yay
g_touchTracker[i].m_touchPointer = touch;
return i;
}
}

LogMsg("Can’t add new fingerID");
return -1;
}

int GetTouchesActive()
{
int count = 0;

for (int i=0; i < MAX_TOUCHES; i++)
{
if (g_touchTracker[i].m_touchPointer)
{
count++;
}
}
return count;
}

// Handles the start of a touch
– (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// Enumerate through all the touch objects.

for (UITouch *touch in touches)
{
//found a touch. Is it already on our list?
int fingerID = GetFingerTrackIDByTouch(touch);

if (fingerID == -1)
{
//add it to our list
fingerID = AddNewTouch(touch);
} else
{
//already on the list. Don’t send this
//LogMsg("Ignoring touch %d", fingerID);
continue;
}

CGPoint pt =[touch locationInView:self];
ConvertCoordinatesIfRequired(pt.x, pt.y);
GetMessageManager()->SendGUIEx(MESSAGE_TYPE_GUI_CLICK_START,pt.x, pt.y,fingerID);
}

#ifdef _DEBUG
//LogMsg("%d touches active", GetTouchesActive());
#endif
}

– (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
// Enumerate through all the touch objects.
for (UITouch *touch in touches)
{
//found a touch. Is it already on our list?
int fingerID = GetFingerTrackIDByTouch(touch);
if (fingerID != -1)
{
g_touchTracker[fingerID].m_touchPointer = NULL; //clear it
} else
{
//wasn’t on our list
continue;
}

CGPoint pt =[touch locationInView:self];
ConvertCoordinatesIfRequired(pt.x, pt.y);
GetMessageManager()->SendGUIEx(MESSAGE_TYPE_GUI_CLICK_END,pt.x, pt.y, fingerID);
}
}

– (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
// Enumerate through all the touch objects.
for (UITouch *touch in touches)
{
//found a touch. Is it already on our list?
int fingerID = GetFingerTrackIDByTouch(touch);
if (fingerID != -1)
{
g_touchTracker[fingerID].m_touchPointer = NULL; //clear it
} else
{
//wasn’t on our list
continue;
}

CGPoint pt =[touch locationInView:self];
ConvertCoordinatesIfRequired(pt.x, pt.y);
GetMessageManager()->SendGUIEx(MESSAGE_TYPE_GUI_CLICK_END,pt.x, pt.y, fingerID);
}
}

– (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
// Enumerate through all the touch objects.
for (UITouch *touch in touches)
{

//found a touch. Is it already on our list?
int fingerID = GetFingerTrackIDByTouch(touch);
if (fingerID != -1)
{
//found it
} else
{
//wasn’t on our list?!
continue;
}

CGPoint pt =[touch locationInView:self];
ConvertCoordinatesIfRequired(pt.x, pt.y);
GetMessageManager()->SendGUIEx(MESSAGE_TYPE_GUI_CLICK_MOVE,pt.x, pt.y, fingerID);
}
}

[/php]