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…)


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);
	}	
}

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

  1. Seth Post author

    Yeah, that works. But if you want to know how many fingers are down, the order they were pressed or be able to put finger data in static 0-10 arrays you’ll have to do some housekeeping at some point.

  2. Andrej

    Thanks a ton for this, it was of a lot of help, even though I had to change a lot of it since this does not appear to be Objective C as far as I can tell. But just the idea of using array of pointers was all that was needed. Thanks!

  3. Raslanove

    Sorry for bringing up several years old thread to life, but thanks a lot, this was very helpful. I’ve noticed on iPod 4g that all touches are immediately cancelled once my sixth finger is down. Was that the case for you as well?

Leave a Reply

Your email address will not be published. Required fields are marked *